[RK1108][Linux3.1]调试笔记 -富瀚ISP驱动

平台 内核版本
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;
}

其中:

  1. 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子系统
其中的参数下节分析:

  1. 读取相关属性
    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"

属性的定义在设备树中。

  1. 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

  1. 注册杂项设备
    最后通过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);
}

最后数据传输
下节分享

发布了295 篇原创文章 · 获赞 99 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/qq_33487044/article/details/104632469