medfield 平台 lvds bridge 驱动

lvds bridge, 应该是一个I2C接口控制的,采用lvds数据接口,液晶屏驱动器桥。

这也是个I2C 驱动,挂接在 I2C_2 总线上。

1. 初始化如下:

    /* ************************************************************************* *\
     * FUNCTION: dsi_lvds_bridge_init
     *
     * DESCRIPTION: Driver Init function for lvds bridge
     *
    \* ************************************************************************* */
    static int __init dsi_lvds_bridge_init(void)
    {
        int ret = 0;
        struct i2c_adapter *adapter = NULL;
        struct i2c_client *client = NULL;
        struct i2c_board_info info;
        printk(KERN_INFO "[DISPLAY] %s: Enter\n", __func__);
        ret = misc_register(&dsi_lvds_bridge_dev);
        if (ret) {
            printk(KERN_ERR "[DISPLAY] %s: Can not register misc device.\n", __func__);
            return ret;
        }
        adapter = i2c_get_adapter(2); /*DV0 is on I2C bus 2 */
        if (!adapter) {
            printk(KERN_ERR "[DISPLAY] %s: Can not get bridge i2c adapter.\n", __func__);
            return 0;
        }
        /* Setup the i2c board info with the device type and
         the device address. */
        memset(&info, 0, sizeof(info));
        strlcpy(info.type, "i2c_disp_brig", sizeof(info.type));
        info.addr = 0x0F; /*I2C2 address 0x0F */
        /* Create the i2c client */
        client = i2c_new_device(adapter, &info);
        if (!client) {
            printk(KERN_ERR "[DISPLAY] %s: ERROR, i2c client=NULL\n", __func__);
            return -1;
        }
        ret = i2c_add_driver(&dsi_lvds_bridge_i2c_driver);
        printk(KERN_INFO "[DISPLAY] %s: Exit, ret = %d\n", __func__, ret);
        return 0;
    }

    /* ************************************************************************* *\
     * FUNCTION: dsi_lvds_bridge_exit
     *
     * DESCRIPTION: Driver exit function for lvds bridge
     *
    \* ************************************************************************* */
    static void __exit dsi_lvds_bridge_exit(void)
    {
        printk(KERN_INFO "[DISPLAY] %s\n", __func__);
        misc_deregister(&dsi_lvds_bridge_dev);
        i2c_del_driver(&dsi_lvds_bridge_i2c_driver);
    }
    module_init(dsi_lvds_bridge_init);
    module_exit(dsi_lvds_bridge_exit);
2. 初始化用到的几个结构体:

    static const struct i2c_device_id dsi_lvds_bridge_id[] = {
        { "i2c_disp_brig", 0 },
        { }
    };
    MODULE_DEVICE_TABLE(i2c, dsi_lvds_bridge_id);
    static struct i2c_driver dsi_lvds_bridge_i2c_driver = {
        .driver = {
            .name = "i2c_disp_brig",
        },
        .id_table = dsi_lvds_bridge_id,
        .probe = dsi_lvds_bridge_probe,
        .remove = dsi_lvds_bridge_remove,
    };

    static const struct file_operations mipi_dsi_dev_fops = {
        .owner = THIS_MODULE,
        .unlocked_ioctl = dsi_lvds_dev_ioctl,
    };
    static struct miscdevice dsi_lvds_bridge_dev = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = "mipi_dsi",
        .fops = &mipi_dsi_dev_fops,
    };


dsi_lvds_bridge_probe()

    /* ************************************************************************* *\
     * FUNCTION: dsi_lvds_bridge_probe
     *
     * DESCRIPTION: Probe function for LVDS bridge.
     *
    \* ************************************************************************* */
    static int dsi_lvds_bridge_probe(struct i2c_client *client, const struct i2c_device_id *id)
    {
        printk(KERN_INFO "[DISPLAY] %s: Enter\n", __func__);
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
            printk(KERN_ERR "[DISPLAY] %s: Check I2C functionality failed.\n", __func__);
            return -ENODEV;
        }
        dsi_lvds_inst = kzalloc(sizeof(struct dsi_lvds_bridge_instance), GFP_KERNEL);
        if (dsi_lvds_inst == NULL) {
            printk(KERN_ERR "[DISPLAY] %s: Can not allocate memory.\n", __func__);
            return -ENOMEM;
        }
        dsi_lvds_inst->client = client;
        i2c_set_clientdata(client, dsi_lvds_inst);
        dsi_lvds_inst->client->addr = 0x0F;
        printk(KERN_INFO "[DISPLAY] %s: Exit\n", __func__);
        return 0;
    }
3. use GPIO to reset lvds state
    /* ************************************************************************* *\
     * FUNCTION: dsi_lvds_set_bridge_reset_state
     *
     * DESCRIPTION: This function uses GPIO to force in and out of reset state.
    \* ************************************************************************* */
    void dsi_lvds_set_bridge_reset_state(int state)
    {
        printk(KERN_INFO "[DISPLAY ] %s: state = %d, gpio = %d\n", __func__, state, gpio_get_value(GPIO_MIPI_BRIDGE_RESET));
        if (state) {
            gpio_direction_output(GPIO_MIPI_BRIDGE_RESET, 0);
            gpio_set_value_cansleep(GPIO_MIPI_BRIDGE_RESET, 0);
            mdelay(10);
        } else {
            gpio_direction_output(GPIO_MIPI_BRIDGE_RESET, 0);
            gpio_set_value_cansleep(GPIO_MIPI_BRIDGE_RESET, 0); /*Pull MIPI Bridge reset pin to Low */
            mdelay(20);
            gpio_direction_output(GPIO_MIPI_BRIDGE_RESET, 1);
            gpio_set_value_cansleep(GPIO_MIPI_BRIDGE_RESET, 1); /*Pull MIPI Bridge reset pin to High */
            mdelay(40);
        }
    }
4. dsi_lvds_configure_lvds_bridge () , 用I2C总线,配置lvds bridge设备。
    /* ************************************************************************* *\
     * FUNCTION: dsi_lvds_configure_lvds_bridge
     *
     * DESCRIPTION: This function uses I2C interface to set bridge registers.
     * to configure timings and MIPI lanes.
    \* ************************************************************************* */
    void dsi_lvds_configure_lvds_bridge(struct drm_device *dev)
    {
        if (lvds_disp_init) {
            printk(KERN_ALERT "[DISPLAY ] %s is already initialized\n", __func__);
            return;
        }
        printk(KERN_INFO "[DISPLAY ]%s: Enter\n", __func__);
        DSI_I2C_ByteWrite(0x013C, 0x00050006, 6); /*PPI_TX_RX_TA, BTA parameters */
        DSI_I2C_ByteRead(0x013C, 4);
        DSI_I2C_ByteWrite(0x0114, 0x00000004, 6); /*PPI_LPTXTIMCNT */
    #ifdef FIXME_MLD // DV0.9 changes
        DSI_I2C_ByteWrite(0x0164, 0x00000007, 6); /*PPI_D0S_CLRSIPOCOUNT */
        DSI_I2C_ByteWrite(0x0168, 0x00000007, 6); /*PPI_D1S_CLRSIPOCOUNT */
        DSI_I2C_ByteWrite(0x016c, 0x00000007, 6); /*PPI_D2S_CLRSIPOCOUNT */
        DSI_I2C_ByteWrite(0x0170, 0x00000007, 6); /*PPI_D3S_CLRSIPOCOUNT */
    #else
        DSI_I2C_ByteWrite(0x0164, 0x00000001, 6); /*PPI_D0S_CLRSIPOCOUNT */
        DSI_I2C_ByteWrite(0x0168, 0x00000001, 6); /*PPI_D1S_CLRSIPOCOUNT */
        DSI_I2C_ByteWrite(0x016c, 0x00000001, 6); /*PPI_D2S_CLRSIPOCOUNT */
        DSI_I2C_ByteWrite(0x0170, 0x00000001, 6); /*PPI_D3S_CLRSIPOCOUNT */
    #endif
        /*Enabling MIPI & PPI lanes, Enable 4 lanes */
        DSI_I2C_ByteWrite(0x0134, 0x0000001F, 6); /*PPI_LANEENABLE */
        DSI_I2C_ByteWrite(0x0210, 0x0000001F, 6); /*DSI_LANEENABLE */
        DSI_I2C_ByteWrite(0x0104, 0x00000001, 6); /*PPI_SARTPPI */
        DSI_I2C_ByteWrite(0x0204, 0x00000001, 6); /*DSI_SARTPPI */
    #ifdef FIXME_MLD
        /*Setting LVDS output frequency */
        DSI_I2C_ByteWrite(0x04A0, 0x00000006, 6); /*LVDS PHY Register 0 (LVPHY0) */
        /*Calculating video panel control settings */
        /*Setting video panel control register */
        DSI_I2C_ByteWrite(0x0450, 0x00000130, 6); /*VPCTRL, Video Path Control, VTGen=ON */
        /*Setting display timing registers */
        DSI_I2C_ByteWrite(0x0454, 0x0064000A, 6); /*HTIM1, HBPR=100, HPW=10 */
        DSI_I2C_ByteWrite(0x0458, 0x00BE0400, 6); /*HTIM2, HFPR=190, HDISPR=1024 */
        DSI_I2C_ByteWrite(0x045c, 0x000F0005, 6); /*VTIM1, VBPR=15, VPW=5 */
        DSI_I2C_ByteWrite(0x0460, 0x000F0258, 6); /*VTIM2, VFPR=15, VDISPR=600 */

    #else
        /*Setting LVDS output frequency */
        DSI_I2C_ByteWrite(0x04A0, 0x00048006, 6); /*LVDS PHY Register 0 (LVPHY0) */
        /*Calculating video panel control settings */
        /*Setting video panel control register */
        DSI_I2C_ByteWrite(0x0450, 0x00000120, 6); /*VPCTRL, Video Path Control, VTGen=ON */
        /*Setting display timing registers */
        DSI_I2C_ByteWrite(0x0454, 0x00280028, 6); /*HTIM1, HBPR=100, HPW=10 */
        DSI_I2C_ByteWrite(0x0458, 0x00500500, 6); /*HTIM2, HFPR=190, HDISPR=1024 */
        DSI_I2C_ByteWrite(0x045c, 0x000e000a, 6); /*VTIM1, VBPR=15, VPW=5 */
        DSI_I2C_ByteWrite(0x0460, 0x000e0320, 6); /*VTIM2, VFPR=15, VDISPR=600 */
    #endif
        DSI_I2C_ByteWrite(0x0464, 0x00000001, 6); /*VFUEN */
        /*Setting LVDS bit arrangement */
        DSI_I2C_ByteWrite(0x0480, 0x03020100, 6); /*LVMX0003 */
        DSI_I2C_ByteWrite(0x0484, 0x08050704, 6); /*LVMX0407 */
        DSI_I2C_ByteWrite(0x0488, 0x0F0E0A09, 6); /*LVMX0811 */
        DSI_I2C_ByteWrite(0x048C, 0x100D0C0B, 6); /*LVMX1215 */
        DSI_I2C_ByteWrite(0x0490, 0x12111716, 6); /*LVMX1619 */
        DSI_I2C_ByteWrite(0x0494, 0x1B151413, 6); /*LVMX2023 */
        DSI_I2C_ByteWrite(0x0498, 0x061A1918, 6); /*LVMX2427 */
    #ifdef FIXME_MLD //DV0.9 changes
        DSI_I2C_ByteWrite(0x049c, 0x00000101, 6); /*LVCFG */
    #else
        DSI_I2C_ByteWrite(0x049c, 0x00000001, 6); /*LVCFG */
    #endif
        DSI_I2C_ByteWrite(0x0288, 0xFFFFFFFF, 6); /*DSI_INTCLR */
        lvds_disp_init = 1;
        printk(KERN_INFO "[DISPLAY]%s: Exit\n", __func__);
    }
5. panel on/off
    /* ************************************************************************* *\
     * FUNCTION: dsi_lvds_toshiba_bridge_panel_off
     *
     * DESCRIPTION: This function uses GPIO to turn OFF panel.
    \* ************************************************************************* */
    void dsi_lvds_toshiba_bridge_panel_off(void)
    {
        printk(KERN_INFO "[DISPLAY ] %s\n", __func__);
        gpio_direction_output(GPIO_MIPI_LCD_STBYB, 0);
        gpio_set_value_cansleep(GPIO_MIPI_LCD_STBYB, 0); /*Pull LCD_STBYB pin to Low */
        mdelay(1);
        gpio_direction_output(GPIO_MIPI_PANEL_RESET, 0);
        gpio_set_value_cansleep(GPIO_MIPI_PANEL_RESET, 0);
        mdelay(1);
    }
    /* ************************************************************************* *\
     * FUNCTION: dsi_lvds_toshiba_bridge_panel_on
     *
     * DESCRIPTION: This function uses GPIO to turn ON panel.
    \* ************************************************************************* */
    void dsi_lvds_toshiba_bridge_panel_on(void)
    {
        printk(KERN_INFO "[DISPLAY ] %s\n", __func__);
        /* set following pin to low */
        gpio_direction_output(GPIO_MIPI_LCD_STBYB, 0);
        gpio_set_value_cansleep(GPIO_MIPI_LCD_STBYB, 0); /*Pull LCD_STBYB pin to Low */
        gpio_direction_output(GPIO_MIPI_PANEL_RESET, 0);
        gpio_set_value_cansleep(GPIO_MIPI_PANEL_RESET, 0);
        gpio_direction_output(GPIO_MIPI_LCD_VADD, 0);
        gpio_set_value_cansleep(GPIO_MIPI_LCD_VADD, 0);
        gpio_direction_output(GPIO_MIPI_LCD_BIAS_EN, 0);
        gpio_set_value_cansleep(GPIO_MIPI_LCD_BIAS_EN, 0);
        mdelay(10);
        /* STBYB */
        gpio_direction_output(GPIO_MIPI_LCD_STBYB, 1);
        gpio_set_value_cansleep(GPIO_MIPI_LCD_STBYB, 1); /*Pull LCD_STBYB pin to High */
        mdelay(10);
        /* VADD */
        gpio_direction_output(GPIO_MIPI_LCD_VADD, 1);
        mdelay(50);
        /* RESET */
        gpio_direction_output(GPIO_MIPI_PANEL_RESET, 1);
        gpio_set_value_cansleep(GPIO_MIPI_PANEL_RESET, 1);
        mdelay(100);
        /* BIAS */
        gpio_direction_output(GPIO_MIPI_LCD_BIAS_EN, 1);
        gpio_set_value_cansleep(GPIO_MIPI_LCD_BIAS_EN, 1);
    }
6. init  / deinit
    /* ************************************************************************* *\
     * FUNCTION: dsi_lvds_init_lvds_bridge
     *
     * DESCRIPTION: This function does all one time init. Currently defining
     * GPIOs only.
    \* ************************************************************************* */
    void dsi_lvds_init_lvds_bridge(struct drm_device *dev)
    {
        /* request GPIOs */
        gpio_request(GPIO_MIPI_BRIDGE_RESET, "display");
        gpio_request(GPIO_MIPI_PANEL_RESET , "display");
        gpio_request(GPIO_MIPI_LCD_STBYB, "display");
        gpio_request(GPIO_MIPI_LCD_BIAS_EN, "display");
        gpio_request(GPIO_MIPI_LCD_VADD, "display");
    }
    /* ************************************************************************* *\
     * FUNCTION: dsi_lvds_deinit_lvds_bridge
     *
     * DESCRIPTION: This function does is called during deinit time.
     *
    \* ************************************************************************* */
    void dsi_lvds_deinit_lvds_bridge(struct drm_device *dev)
    {
        if (!lvds_disp_init) {
            printk(KERN_ALERT "[DISPLAY ] %s has not initialized\n", __func__);
            return;
        }
        printk(KERN_INFO "[DISPLAY ] Enter %s\n", __func__);
        lvds_disp_init = 0;
    }
7. get params
    /* ************************************************************************* *\
     * FUNCTION: dsi_lvds_bridge_get_display_params
     *
     * DESCRIPTION: This function is a callback to get the panel resolution and
     * timing settings.
     *
    \* ************************************************************************* */
    void dsi_lvds_bridge_get_display_params(struct drm_display_mode *mode)
    {
    #ifdef FIXME_MLD // Changes for DV0.9 display
    /* This settings is for 1024*600 panel*/
        mode->hdisplay = 1024;
        mode->vdisplay = 600;
        mode->hsync_start = 1655;
        mode->hsync_end = 1665;
        mode->htotal = 1765;
        mode->vsync_start = 615;
        mode->vsync_end = 620;
        mode->vtotal = 635;
        mode->clock = 33324;
    #else
        mode->hdisplay = 1200;
        mode->vdisplay = 800;
        mode->hsync_start = 1360;
        mode->hsync_end = 1400;
        mode->htotal = 1440;
        mode->vsync_start = 814;
        mode->vsync_end = 824;
        mode->vtotal = 838;
        mode->clock = 33324;
    #endif //end FIXME_MLD
        printk(KERN_INFO "[DISPLAY]: hdisplay(w) is %d\n", mode->hdisplay);
        printk(KERN_INFO "[DISPLAY]: vdisplay(h) is %d\n", mode->vdisplay);
        printk(KERN_INFO "[DISPLAY]: HSS is %d\n", mode->hsync_start);
        printk(KERN_INFO "[DISPLAY]: HSE is %d\n", mode->hsync_end);
        printk(KERN_INFO "[DISPLAY]: htotal is %d\n", mode->htotal);
        printk(KERN_INFO "[DISPLAY]: VSS is %d\n", mode->vsync_start);
        printk(KERN_INFO "[DISPLAY]: VSE is %d\n", mode->vsync_end);
        printk(KERN_INFO "[DISPLAY]: vtotal is %d\n", mode->vtotal);
        printk(KERN_INFO "[DISPLAY]: clock is %d\n", mode->clock);
    }
8. 具体动作: read / write
    /* ************************************************************************* *\
     * FUNCTION: DSI_I2C_ByteRead
     *
     * DESCRIPTION: Local functions to read I2C registers
     *
    \* ************************************************************************* */
    static int DSI_I2C_ByteRead(u16 reg, int count)
    {
        if (dsi_lvds_inst->client)
            return __DSI_I2C_ByteRead(reg, count);
        else
            return -EIO;
    }

    /* ************************************************************************* *\
     * FUNCTION: __DSI_I2C_ByteRead
     *
     * DESCRIPTION: Local functions to process read req for I2C registers
     *
    \* ************************************************************************* */
    static int __DSI_I2C_ByteRead(u16 reg, int count)
    {
        char rxData[4] = {0};
        char regData[2] = {0};
        struct i2c_msg msgs[] = {
            {
             .addr = dsi_lvds_inst->client->addr,
             .flags = 0,
             .len = 2,
             },
            {
             .addr = dsi_lvds_inst->client->addr,
             .flags = I2C_M_RD,
             .len = count - 2,
             },
        };
        regData[0] = (reg & 0xFF00) >> 8;
        regData[1] = reg & 0xFF;
        msgs[0].buf = regData;
        msgs[1].buf = rxData;
        printk(KERN_INFO "Register: 0x%x\n", reg);
        if (i2c_transfer(dsi_lvds_inst->client->adapter, msgs, 2) < 0) {
            printk(KERN_ERR "[DISPLAY] %s: transfer error\n", __func__);
            return -EIO;
        } else if (count > 2) {
            int i = 0;
            for (i = count - 3; i > -1; --i)
                printk(KERN_INFO "%02x ", rxData[i]);
            printk(KERN_INFO "\n");
            return rxData[0];
        }
        return 0;
    }

    /* ************************************************************************* *\
     * FUNCTION: DSI_I2C_ByteWrite
     *
     * DESCRIPTION: Local functions to issue write req for I2C registers
     *
    \* ************************************************************************* */
    static int DSI_I2C_ByteWrite(u16 reg, u32 data, int count)
    {
        if (dsi_lvds_inst->client)
            return __DSI_I2C_ByteWrite(reg, data, count);
        else
            return -EIO;
    }

    /* ************************************************************************* *\
     * FUNCTION: __DSI_I2C_ByteWrite
     *
     * DESCRIPTION: Local functions to process write req for I2C registers
     *
    \* ************************************************************************* */
    static int __DSI_I2C_ByteWrite(u16 reg, u32 data, int count)
    {
        char txData[6] = {0};
        struct i2c_msg msg[] = {
            {
             .addr = dsi_lvds_inst->client->addr,
             .flags = 0,
             .len = count,
             },
        };
        /*Set the register */
        txData[0] = (reg & 0xFF00) >> 8;
        txData[1] = reg & 0xFF;
        if (count == 6) {
            /*Set the data */
            txData[2] = (data & 0xFF);
            txData[3] = (data & 0xFF00) >> 8;
            txData[4] = (data & 0xFF0000) >> 16;
            txData[5] = (data & 0xFF000000) >> 24;
        } else {
            /* Not valid for this bridge chipset */
        }
        printk(KERN_INFO "[DISPLAY] %s: addr = %x, reg = %x, data = %x\n",
            __func__, dsi_lvds_inst->client->addr, reg, data);
        msg[0].buf = txData;
        if (i2c_transfer(dsi_lvds_inst->client->adapter, msg, 1) < 0) {
            printk(KERN_ERR "[DISPLAY] %s: transfer error\n", __func__);
            return -EIO;
        } else {
            return 0;
        }
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值