平台 | 内核版本 |
---|---|
RK1108 | Linux3.1 |
疑问每次开机日志
download fh8553 firmware
fh8553 firmware
目录:common/system/lib/firmware/FH8553_firmware.hex
设备树
目录:kernel/arch/arm/boot/dts/rv1108-t3-dvr-v10.dts
&spi0 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spim0_clk &spim0_cs0 &spim0_tx &spim0_rx>;
fh8553: fh8553@00 {
status = "okay";
compatible = "rockchip,fh8553";
reg = <0x0>;
spi-max-frequency = <2250000>;
spi-min-frequency = <2250000>;
device_type = "v4l2-spi-subdev";
/* 1 or 2 or 4 */
channels = <1>;
/* 1.8v or 3.3v */
apio_vol = <3300>;
/* PAL or NTSC */
cvbs_mode = "PAL";
//nvp_mode = "720p_2530";
nvp_mode = "1080p_2530";
reset-gpio = <&gpio0 GPIO_C3 GPIO_ACTIVE_LOW>;
//reset-gpio = <&gpio3 GPIO_B0 GPIO_ACTIVE_LOW>;
boot0-gpio = <&gpio0 GPIO_B3 GPIO_ACTIVE_HIGH>;
boot1-gpio = <&gpio0 GPIO_A6 GPIO_ACTIVE_LOW>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&cif_dvp_d2d9 &cif_dvp_clk_in>;
pinctrl-1 = <&cif_dvp_d2d9_sleep &cif_dvp_clk_in_sleep>;
};
};
驱动
目录:kernel/drivers/media/platform/rk-cif/fh8553.c
static struct of_device_id fh8553_dt_ids[] = {
{.compatible = "rockchip,fh8553"},
{}
};
static struct spi_driver fh8553_driver = {
.driver = {
.name = FH_SPI_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(fh8553_dt_ids),
},
.probe = fh8553_probe,
.resume = fh8553_preisp_resume,
};
module_spi_driver(fh8553_driver);
当和设备树匹配之后会执行prob
:
static int fh8553_probe(struct spi_device *client)
{
struct device_node *np = client->dev.of_node;
fh8553 = devm_kzalloc(&client->dev,
sizeof(*fh8553),
GFP_KERNEL);
if (!fh8553) {
dev_err(&client->dev, "fh8553 kzalloc failed\n");
return -ENOMEM;
}
fh8553->client = client;
fh8553->device_id = 0;
v4l2_spi_subdev_init(&fh8553->sd, client, &fh8553_module_ops);
/* initialize name */
snprintf(
fh8553->sd.name,
sizeof(fh8553->sd.name),
"%s",
client->dev.driver->name);
if (of_property_read_u32(np,
OF_NVP_MODULE_CHANNELS,
&fh8553->channels)) {
fh8553->channels = 0;
dev_err(&client->dev,
"get %s from dts failed!\n",
OF_NVP_MODULE_CHANNELS);
return -EINVAL;
}
if (of_property_read_u32(np,
OF_NVP_MODULE_APIO_VOL,
&fh8553->apio_vol)) {
dev_err(&client->dev,
"get %s from dts failed!\n",
OF_NVP_MODULE_APIO_VOL);
return -EINVAL;
}
if (of_property_read_string(np,
OF_NVP_MODULE_CVBS_MODE,
&fh8553->cvbs_mode)) {
dev_err(&client->dev,
"get %s from dts failed!\n",
OF_NVP_MODULE_CVBS_MODE);
return -EINVAL;
}
if (of_property_read_string(np,
OF_NVP_MODULE_NVP_MODE,
&fh8553->fh8553_mode)) {
dev_warn(&client->dev,
"get %s from dts failed!\n",
OF_NVP_MODULE_NVP_MODE);
fh8553->fh8553_mode = NULL;
}
of_property_read_u32_array(
np,
OF_CAMERA_MODULE_DEFRECT0,
(unsigned int *)&fh8553->defrects[0],
6);
of_property_read_u32_array(
np,
OF_CAMERA_MODULE_DEFRECT1,
(unsigned int *)&fh8553->defrects[1],
6);
of_property_read_u32_array(
np,
OF_CAMERA_MODULE_DEFRECT2,
(unsigned int *)&fh8553->defrects[2],
6);
of_property_read_u32_array(
np,
OF_CAMERA_MODULE_DEFRECT3,
(unsigned int *)&fh8553->defrects[3],
6);
if(fh8553_hw_config() < 0)
{
pr_err("fh8553 hw config error\n");
return -EINVAL;
}
fh8553->misc.minor = MISC_DYNAMIC_MINOR;
fh8553->misc.name = "fh_preisp";
fh8553->misc.fops = &fhpreisp_fops;
if (misc_register(&fh8553->misc) < 0)
{
pr_err("Error: misc_register");
}
return 0;
}
其中:
- V4L2
fh8553 = devm_kzalloc(&client->dev,
sizeof(*fh8553),
GFP_KERNEL);
fh8553->client = client;
fh8553->device_id = 0;
v4l2_spi_subdev_init(&fh8553->sd, client, &fh8553_module_ops);
函数绑定只是将驱动所实现的函数赋值给相关的变量即可
static struct v4l2_subdev_video_ops fh8553_module_video_ops = {
.enum_frameintervals = fh8553_module_enum_frameintervals,
.g_mbus_fmt = fh8553_module_g_fmt,
.s_mbus_fmt = fh8553_module_s_fmt,
.s_frame_interval = fh8553_module_s_frame_interval,
.s_stream = fh8553_module_s_stream
};
static struct v4l2_subdev_ops fh8553_module_ops = {
.core = &fh8553_module_core_ops,
.video = &fh8553_module_video_ops,
};
其中v4l2_subdev_ops
相关解释可参考:The Linux Kernel
与V4L2
子系统
其中的参数下节分析:
- 读取相关属性
of_property_read_u32
读取相关属性名的节点整数值
of_property_read_string
读取相关属性名的节点字符串
相关属性名为:
#define OF_NVP_MODULE_CHANNELS "channels"
#define OF_NVP_MODULE_APIO_VOL "apio_vol"
#define OF_NVP_MODULE_NVP_MODE "nvp_mode"
#define OF_NVP_MODULE_CVBS_MODE "cvbs_mode"
属性的定义在设备树中。
- FH8553参数配置
static int fh8553_hw_config(void)
{
int ret = 0;
enum of_gpio_flags flags;
struct device_node *node;
struct device *dev = &fh8553->client->dev;
node = dev->of_node;
if(NULL != fh8553->client)
{
of_property_read_u32(node, "spi-max-frequency", &fh8553->client->max_speed_hz);
}
ret = of_get_named_gpio_flags(node, "reset-gpio", 0, &flags);
if (ret <= 0) {
dev_warn(dev, "can not find property reset-gpio, error %d\n", ret);
}
fh8553->reset_gpio = ret;
ret = devm_gpio_request(dev, fh8553->reset_gpio, "reset-gpio");
if (ret) {
dev_err(dev, "reset gpio %d request error %d\n", fh8553->reset_gpio, ret);
return ret;
}
ret = gpio_direction_output(fh8553->reset_gpio, GPIO_HIGH);
if (ret) {
dev_err(dev, "gpio %d direction output error %d\n",
fh8553->reset_gpio, ret);
return ret;
}
ret = of_get_named_gpio_flags(node, "boot0-gpio", 0, &flags);
if (ret <= 0) {
dev_warn(dev, "can not find property boot0-gpio, error %d\n", ret);
}
fh8553->boot_sel0_gpio = ret;
ret = devm_gpio_request(dev, fh8553->boot_sel0_gpio, "boot0-gpio");
if (ret) {
dev_err(dev, "boot0 gpio %d request error %d\n", fh8553->boot_sel0_gpio, ret);
return ret;
}
ret = gpio_direction_output(fh8553->boot_sel0_gpio, GPIO_HIGH);
if (ret) {
dev_err(dev, "gpio %d direction output error %d\n",
fh8553->boot_sel0_gpio, ret);
return ret;
}
ret = of_get_named_gpio_flags(node, "boot1-gpio", 0, &flags);
if (ret <= 0) {
dev_warn(dev, "can not find property boot1-gpio, error %d\n", ret);
}
fh8553->boot_sel1_gpio = ret;
ret = devm_gpio_request(dev, fh8553->boot_sel1_gpio, "boot1-gpio");
if (ret) {
dev_err(dev, "boot1 gpio %d request error %d\n", fh8553->boot_sel1_gpio, ret);
return ret;
}
ret = gpio_direction_output(fh8553->boot_sel1_gpio, GPIO_HIGH);
if (ret) {
dev_err(dev, "gpio %d direction output error %d\n",
fh8553->boot_sel1_gpio, ret);
return ret;
}
gpio_direction_output(fh8553->reset_gpio, GPIO_HIGH);
return 0;
}
其中:
of_get_named_gpio_flags
从设备树中读取 GPIO
配置编号和标志,然后通过devm_gpio_request
申请一个gpio
,然后gpio_direction_output
操作gpio
。
- 注册杂项设备
最后通过misc_register
注册杂项设备。
fh8553->misc.minor = MISC_DYNAMIC_MINOR;
fh8553->misc.name = "fh_preisp";
fh8553->misc.fops = &fhpreisp_fops;
misc_register(&fh8553->misc);
接下来看下其file_operations
static const struct file_operations fhpreisp_fops = {
.owner = THIS_MODULE,
.open = fhpreisp_open,
.release = fhpreisp_release,
.write = fhpreisp_write,
.poll = fhpreisp_poll,
.unlocked_ioctl = fhpreisp_ioctl,
.compat_ioctl = fhpreisp_ioctl,
};
上面可以看出是只要的操作:
firmware download
可以看出开篇的日志是echo on > /dev/fh_preisp
触发的
启动脚本:
目录: common/root/etc/init.d/rcS
echo on > /dev/fh_preisp
fhpreisp_write(操作)函数详解
当open
static int fhpreisp_open(struct inode *inode, struct file *file)
{
printk("fhpreisp open\n");
file->private_data = fh8553;
return 0;
}
以后:
static ssize_t fhpreisp_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
int ret = 0;
printk("fhpreisp write\n");
gpio_set_value(fh8553->boot_sel0_gpio, GPIO_HIGH);
//gpio_set_value(fh8553->boot_sel0_gpio, GPIO_LOW);
gpio_set_value(fh8553->boot_sel1_gpio, GPIO_LOW);
gpio_set_value(fh8553->reset_gpio, GPIO_HIGH);
mdelay(10);
gpio_set_value(fh8553->reset_gpio, GPIO_LOW);
mdelay(10);
gpio_set_value(fh8553->reset_gpio, GPIO_HIGH);
mdelay(200);
ret = fh8553_download_fw(fh8553->client, FH_RAMBOOT_NAME, FH_FIRMWARE_NAME);
if (ret < 0) {
gpio_set_value(fh8553->boot_sel0_gpio, GPIO_LOW);
gpio_set_value(fh8553->boot_sel1_gpio, GPIO_LOW);
gpio_set_value(fh8553->reset_gpio, GPIO_HIGH);
mdelay(10);
gpio_set_value(fh8553->reset_gpio, GPIO_LOW);
mdelay(10);
gpio_set_value(fh8553->reset_gpio, GPIO_HIGH);
mdelay(200);
}
mdelay(10);
gpio_set_value(fh8553->boot_sel0_gpio, GPIO_LOW);
return count;
}
fh8553_download_fw
目录:kernel/drivers/media/platform/rk-cif/fh8553-fw.c
int fh8553_download_fw(struct spi_device *spi, const char *boot_name, const char *fw_name)
{
if(fh8553_download_part1(spi, boot_name) >= 0)
{
mdelay(10);
if(fh8553_download_part2(spi, fw_name) >= 0)
{
return 0;
}
}
return -1;
}
首先看下fh8553_download_part1
static int fh8553_download_part1(struct spi_device *spi, const char *boot_name)
{
const struct firmware *fw;
unsigned int fw_size;
unsigned int fw_verifycode;
int name_len = 0;
int try_count = 0;
char head_data[64] = {0};
char read_data[3] = {0};
int data_count = 0;
int ret = 0;
#if PRINTW_HEX || PRINTR_HEX
int i = 0;
#endif
if (boot_name == NULL)
return -1;
if (request_firmware(&fw, boot_name, &spi->dev))
{
dev_err(&spi->dev, "request boot firmware %s failed!", boot_name);
return -1;
}
fw_verifycode = check_sum(fw->data, fw->size);
fw_size = fw->size;
name_len = strlen(boot_name);
if(name_len > 48)
{
memcpy(&head_data[0], boot_name, 48);
}else
{
memcpy(&head_data[0], boot_name, name_len);
}
data_count += 48;
memcpy(&head_data[48], &fw_size, 4);
data_count += 4;
head_data[52] = 0x00;
head_data[53] = 0x00;
head_data[54] = 0x00;
head_data[55] = 0x00;
data_count += 4;
head_data[56] = 0x00;
head_data[57] = 0x00;
head_data[58] = 0x00;
head_data[59] = 0xA0;
data_count += 4;
fw_verifycode &= 0xFF;
memcpy(&head_data[60], &fw_verifycode, 4);
data_count += 4;
spi2apb_safe_write(spi, head_data, data_count, data_count);
#if PRINTW_HEX
printk("write:");
for(i=0; i<data_count; i++)
{
printk("%02X ", head_data[i]);
}
printk("\n");
#endif
mdelay(2);
spi2apb_safe_write(spi, fw->data, fw->size, fw->size);
while(1)
{
memset(read_data, 0, sizeof(read_data));
mdelay(APB_SAFE_OPERATION_TRY_DELAY_US);
spi2apb_safe_read(spi, read_data, 3);
#if PRINTR_HEX
printk("read:");
for(i=0; i<3; i++)
{
printk("%02X ", read_data[i]);
}
printk("\n");
#endif
ret = check_head(0xB0, read_data, 3) ;
if(TR_CMD_ERROR == ret || TR_SUM_ERROR == ret)//ÃüÁî×Ö´íÎó
{
if(try_count++ > APB_SAFE_OPERATION_TRY_MAX)
{
try_count = 0;
ret = -1;
dev_err(&spi->dev, "ready dowmload [read timeout]\n");
break;
}
continue;
}else
{
if(0x00 != read_data[1])
{
ret = -1;
dev_err(&spi->dev, "ready dowmload [failed]\n");
}else
{
dev_info(&spi->dev, "ready dowmload [ok]\n");
}
break;
}
}
release_firmware(fw);
return ret;
}
其中
static void spi2apb_safe_read(struct spi_device *spi, char *data, size_t data_len)
{
mutex_lock(&spi2apb_lock);
_spi2apb_read(spi, data, data_len);
mutex_unlock(&spi2apb_lock);
}
static int _spi2apb_read(struct spi_device *spi, char *data, size_t data_len)
{
struct spi_transfer data_packet = {
.rx_buf = data,
.len = data_len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&data_packet, &m);
return spi_sync(spi, &m);
}
最后数据传输
下节分享