mipi驱动研究

一,MIPI了解

MIPI 接口广泛用于摄像头,和显示部分;PHY 属于 MIPI 接口的最底层部分,也就是物理层,直接涉及到物理连线,信号传输等等,要搞清楚 MIPI 的数据传送,那么从 PHY 入手了解,是一个不错的选择;

既然是 PHY,那么肯定在 PHY 层上,还搭建了其他的硬件层次结构,这里暂时只关注 PHY 层,后面会结合更加上层的逻辑来分析;在 PHY 上,只负责定义数据物理层的收发标准;

MIPI 的 PHY 分为了 3 种:

D-PHY
C-PHY
M-PHY

他们之前有什么区别和联系呢?

D-PHY 最先出来,现在用得很多,传输速度快;M-PHY 再出来,速度最快,但是与 D-PHY 不兼容,而且受限于其他器件的发展,如此高的带宽,暂时用不上;最后出来的是 C-PHY,没有时钟信号,靠 pin 脚的编码来区分数据信号;

也就是说,M-PHY 现在基本没有应用场景,D-PHY 和 C-PHY 用得居多;

注意:C-PHY 和 D-PHY 的 C 和 D,不是 Camera 和 Display 的简写,只是 PHY 的命名;D-PHY 可以接 CSI 成为摄像头端的 PHY,也可以用在 DSI,作为 Display 的 PHY;C-PHY 也一样;

C-PHY是电压驱动型,由于是两两相差,信号幅度绝对值分别是0,100,200mv,信号较弱,不利于传输。D-PHY是电流驱动型,单信号幅度一般是200mv,线对差分的幅度在400mv左右,布线要求是等长且成双成对。

C-PHY和D-PHY最直观的差异是DPHY是源同步系统,有专门的同步时钟通道,但是CPHY没有同步时钟,时钟是嵌入到数据中的。显然的,实现嵌入时钟的目的是为了增加带宽,肯定会涉及到编码,物理层的结构必然是完全不同,单从线路上看,CPHY是一个A/B/C三线系统

      MIPI DPHY的物理层,咱们大家都很清楚,一对时钟,几对数据,接收端根据时钟边沿采样数据,找到0xB8的同步头,物理层实现就算是齐活了,但MIPI CPHY不同,因为它不传输时钟,那么要接收CPHY的数据,必须先恢复时钟,然后再用恢复的时钟采样数据并寻找同步头,最后还需要进行数据解码恢复出最初的发送的内容(发送端的过程相反)

MIPI D-PHY是差分串口传输,速度快,抗干扰。主流手机模组现在都是用MIPI传输,传输时使用4对差分信号传输图像数据和一对差分时钟信号;

ov13850 MIPI摄像头采用D-PHY,有三个电源:VDDIO(IO电源),AVDD(模拟电源),DVDD(内核数字电源),不同sensor模组的摄像头供电不同

MIPI传输模式
LP(Low-Power) 模式:用于传输控制信号,最高速率 10 MHz
HS(High-Speed)模式:用于高速传输数据,速率范围 [80 Mbps, 1Gbps] per Lane
传输的最小单元为 1 个字节,采用小端的方式及 LSB first,MSB last。

clk lane:
mipi csi clk 存在两种工作模式,一种是连续时钟模式,传输过程不会切换 LP 状态;另一种是非连续时钟信号模式,每传输完一帧图像数据,帧 blanking 时将会切换为 LP 状态。
从时序图可以看到,clk lane 也会有一个 LP11→LP01→LP00 的时序,从而进入 HS 模式。
如果是连续时钟信号模式,在 sensor 传输的图像数据的过程,帧间隔时,clk lane 不会切换到 LP 状态,即 LP11→LP01→LP00 时序只有一次;
如果是非连续时钟信号模式,每传输完一帧图像数据,都将会从 HS 模式切换回 LP 模式,在传输下一帧图像数据时,再从 LP 模式进入 HS 模式;
如果 camera sensor mipi clk lane 支持非连续时钟模式,建议配置为非连续时钟模式。

data lane:
在数据线上有 3 种可能的操作模式:Escape mode, High-Speed (Burst) mode and Control mode,下面是从停止状态进入相应模式需要的时序:
Escape mode
进入时序:LP11→LP10→LP00→LP01→LP00
退出时序:LP10→LP11
High-Speed mode
进入时序:LP11→LP01→LP00→SoT(0001_1101)
退出时序:EoT→LP11
时序图如下:
Turnaround
进入时序:LP11→LP10→LP00→LP10→LP00
退出时序:LP00→LP10→LP11

D-PHY规范的2.0版的标准

二,关键数据

MIPI接口在系统的实现如上图所示,MIPI DPHY提供了4 Lane的Rx接口,由Sensor提供Clock,并通过四条数据Lane输入图像数据。DPHY与CSI-2 Host Contrller之间通过PPI(PHY-Protocol Interface)相连,该接口包括了控制,数据,时钟等多条信号。CSI-2 Host Contrller通过PPI接口收到数据后进行解析,完成后通过IDI(Image Data Interface)或者IPI(Image Pixel Interface)输出到SoC的其他模块(ISP);同时SoC通过APB Slave总线控制CSI-2 Host Contrller的相关寄存器。

PPI(Phy Protocal Interface)接口:

PPI接口主要分成四部分,分别为

(1)    Data: 用于传输HS/LP模式下数据;

(2)    Clock: 用于传输数据时钟控制;

(3)    Interface Control: 接口控制,如Shutdown信号;

(4)    Error Signal: 用于传输错误信号;

(5)几个用于实现Synopsys专用命令控制的TEST信号;

IDI(Image Data Interface )接口:

-       输出CSI-2 规格书中推荐的32位或者64位的图像格式;

-       提供水平或竖直方向的精确视频同步信号;

-       提供CSI-2 Data Type (DT) 和 Virtual Channel (VC)的相关信息;

 IPI(Image Pixel Interface)接口:

-       48 位并行输出,提供像素时钟同步信号;

-       提供水平或竖直方向的精确视频同步信号;

MIPI DPHY采用1对源同步的差分时钟和1~4对差分数据链路来进行数据传输,数据传输采用DDR方式,即在时钟的上下边沿都有数据传输;根据Sensor不同的输出链路数量,接收端可以配置1到4条输入链路;数据链路越多,图像数据传输速度也就越快,多通道发送实现如上图所示,接收端与其类似,从不同通道接收的数据加以合并最后形成完整的数据流。

Interface Control:

SHUTDOWNZ:

用于关闭DPHY的信号;

ENABLE_N:

用于使能第N条Lane的信号;

RSTZ:

用于Reset DPHY的信号;

Clock

STOPSTATEDATA_N:

用于指示第N条Lane处于停止状态;

RXBYTECLKHS

High-Speed数据接收时钟(Byte为单位),即用于PPI接口图像数据的同步时钟;

STOPSTATECLK

用于指示时钟通道处于停止状态;

RXULPSCLKNOT

用于指示时钟通道处于ULP(Ultra Low Power)模式;

RXCLKACTIVEHS

用于指示时钟通道处于高速数据接收状态;

Data

RXDATAHS_N[7:0]

高速模式下数据传输线,在RXBYTECLKHS的上升沿被传输;

RXACTIVEHS_N

用于指示当前处于高速数据接收状态;

RXVALIDHS_N

用于指示当前正在接收有效的数据(从Phy层到协议层);

ERRSOTHS_N

用于指示高速数据传输时发生SoT(Start of Transmission)错误,但是信号仍然可以被同步;每个RXBYTECLKHS周期检测一次;

ERRSOTSYNCHS_N

用于指示高速数据传输时发生SoT(Start of Transmission)错误,信号也无法被同步;每个RXBYTECLKHS周期检测一次;

ERRESC_N  

用于指示Escape模式进入错误,会一直保持错误状态直至返回停止状态;

TEST信号

TESTDIN[7:0]

Vendor专用数据输入信号

TESTDOUT[7:0]

Vendor专用数据输出信号

TESTCLK

Vendor专用信号时钟

TESTEN

Vendor专用信号使能

TESTCLR

Vendor专用信号清除

(1)数据Lane序号对应PPI接口中的序号N,例如数据Lane0对应的输出数据线为RXDATAHS_0[7:0];

  (2)  MIPI DPHY使用DDR(Double Date Rate)时钟,即上升沿和下降沿都能传输数据,所以一个时钟周期传输2bit;

  (3) 假设clk lane频率为200Mhz, 那么数据lane的bit clk = 200*2 = 400Mbps; 对应的RXBYTECLKHS字节时钟为400/8=50MBps;

  (4) 每个数据Lane对应的PPI都有8根数据线(RXDATAHS_N[7:0],如果配置为1条Lane,则每个RXBYTECLKHS时钟周期传输1个Byte; 如果配置为2条Lane,则每个RXBYTECLKHS时钟周期传输2个Byte;如果配置为4条Lane,则每个RXBYTECLKHS时钟周期传输4个Byte;

DPHY的状态切换

DPHY支持HS(High Speed)和LP(Low Power)两种工作模式。HS模式下采用低压差分信号,功耗较大,但是可以传输很高的数据速率(数据速率为80M~1.5Gbps); LP模式下采用单端信号,数据速率很低(<10Mbps),但是相应的功耗也很低。两种模式的结合保证了MIPI总线在需要传输大量数据(如图像)时可以高速传输,而在不需要大数据量传输时又能够减少功耗。

三,mipi驱动研究

1,mipi寄存器写入方法

用于实现Synopsys专用命令控制的TEST信号
GRF_DPHY_RX0_TESTDIN[7:0]:Vendor专用数据输入信号
GRF_DPHY_RX0_TESTCLK:Vendor专用信号时钟
GRF_DPHY_RX0_TESTEN:Vendor专用信号使能
GRF_DPHY_RX0_TESTDOUT[7:0]:Vendor专用数据输出信号

https://rockchip.fr/RK3288%20TRM/rk3288-chapter-35-mipi-csi-phy.pdf

中描述,设置D-PHY为RX模式的方法:

If you want the D-PHY work as for RX, you must set grf_dphy_tx1rx1_masterslavez = 1’b1,
and set grf_dphy_tx1rx1_basedir = 1’b1, then you must select the data from D-PHY RX1 to
CSI Host or ISP by setting grf_con_isp_dphy_sel (bit[1] of GRF_SOC_CON6)

2,mipi链路速率计算方法

数据总线频率。数据总线频率与媒体总线像素代码、总线类型(每个采样的时钟周期)一起定义像素阵列中的像素速率(V4L2_CID_PIXEL_RATE),(如果设备不是图像sensor,则可能在其他地方)。帧速率可以通过像素时钟、图像宽度和高度以及水平和垂直消隐来计算。虽然像素速率控制可以在包含像素阵列的子设备之外的其他地方定义,但无法从该信息获得帧速率。这是因为只有在像素阵列上,才能假定垂直和水平消隐信息是准确的:像素阵列中不允许其他消隐。帧速率的选择通过选择所需的水平和垂直消隐来执行。这种控制的单位是赫兹

链路的速率,即是MIPI-CSI 的clock
在摄像头驱动中,link_freq_menu_items是一个参数或变量,用于表示可选的链接频率列表。链接频率指的是摄像头与主机之间的通信速度。
确定link_freq_menu_items的值需要考虑以下几个因素:
摄像头硬件支持的链接频率范围。
主机系统支持的链接频率范围。
应用场景对链接频率的需求和性能要求。
举个例子,假设某款摄像头硬件支持以下三种链接频率:100 MHz、200 MHz、300 MHz。而主机系统对这些频率都提供了相应的接口和支持。
那么,在设置link_freq_menu_items时可以将其定义为一个包含这三种链接频率的数组
关于pixelclock 有如下关系:
pclk = (HTS*VTS)*fps = (width + HBLANK)*(height + VBLANK)*fps
关于曝光时间,计算如下:
exposure_line_time = HTS/pclk;
exposure_time = exposure_line_time * exposure_line
曝光时间就是开启一次快门,能曝光多少行。一帧图像需要多次曝光来完成。
exposure_line 是曝光的行数,一般是一个寄存器,用来控制曝光时间是多少倍的行曝光时间(exposure_line_time)。曝光行数最大就是VTS。
MIPI-CSI 链路速率计算公式如下:
link_freq = (pclk * bits_per_sample) /(2*nr_of_lanes)
变量                   描述
nr_of_lanes    Number of data lanes used on the CSI-2 link. This can be obtained from the OF endpoint configuration.CSI-2链路上使用的数据通道数。这可以从OF端点配置中获得
2                 Two bits are transferred per clock cycle per lane.每个通道每个时钟周期传输两个比特
bits_per_sample      Number of bits per sample
当描述单一像素的时候,位深的概念可以被定义为 BPP(bit per pixel,每个像素的位深)或者是 BPS(bit per sample,每个样本的位深),
这指明了用于描述一个像素的比特总数。当描述单一像素的单一色彩分量时候,位深的概念可以被定义为 BPC(bit per channel,每个通道的位深)或者是 BPC(bit per color,每个色彩的位深)

3,mipi txrx stream on方法

/*
     *Config rk3288:
     *step1:rk3288 isp connected to phy1-rx
     *step2:rk3288 phy1-rx test bus connected to csi host
     *step3:rk3288 phy1-rx source selected as: isp = 1'b1,csi-host = 1'b0
If you want the D-PHY work as for RX, you must set grf_dphy_tx1rx1_masterslavez = 1’b1,
and set grf_dphy_tx1rx1_basedir = 1’b1, then you must select the data from D-PHY RX1 to
CSI Host or ISP by setting grf_con_isp_dphy_sel (bit[1] of GRF_SOC_CON6)

**/
    write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);/**PHY_REG(0x25c, 1, 1)**/
    write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);/**PHY_REG(0x25c, 1, 14)**/
    if (is_linked_isp)
        write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);/**PHY_REG(0x27c, 1, 13)**/
    else
        write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 0);


    /* Belowed is the sequence of mipi configuration */
    /* Step1: set RSTZ = 1'b0, phy1-rx controlled by isp */
    /* Step2: set SHUTDOWNZ = 1'b0, phy1-rx controlled by isp */
    /**RSTZ用于Reset DPHY的信号
     * SHUTDOWNZ用于关闭DPHY的信号**/
     /**
#define CIF_MIPI_CTRL_OUTPUT_ENA        BIT(0)
#define CIF_MIPI_CTRL_FLUSH_FIFO        BIT(1)
#define CIF_MIPI_CTRL_SHUTDOWNLANES(a)        (((a) & 0xF) << 8)
#define CIF_MIPI_CTRL_NUM_LANES(a)        (((a) & 0x3) << 12)
#define CIF_MIPI_CTRL_ERR_SOT_HS_SKIP        BIT(16)
#define CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP    BIT(17)
#define CIF_MIPI_CTRL_CLOCKLANE_ENA        BIT(18)
     **/
    if (!is_linked_isp) {
        write_txrx_reg(priv, TXRX_PHY_RSTZ, 0);
        write_txrx_reg(priv, TXRX_PHY_SHUTDOWNZ, 0);
    } else {
        mipi_ctrl = readl(isp_mipi_ctrl);
        /**shutdown模式是最低电源消耗模式,RSTZ和SHUTDOWNZ被拉低,此时模拟电路禁止,数字部分被复位,主要消耗来自模拟电路的待机电流
        和数字部分的漏电流。在此模式下,DATAN和CLKN/CLKP都处于高阻态,但可以通过TESTCLR/TESTDIN信号进行hsfreqrange频率的配置**/
        mipi_ctrl &= 0xfffff0ff;/**CIF_MIPI_CTRL_SHUTDOWNLANES**/
        writel(mipi_ctrl, isp_mipi_ctrl);
    }

    /* Step3: set TESTCLR= 1'b1,TESTCLK=1'b1 */
    /***
    TESTCLKVendor专用信号时钟,这个操作触发testclk(bit2),并将testddatain的数据采样生成一个新的test code
    TESTCLRVendor专用信号清除,这个操作激活接口初始化
    **/
    write_txrx_reg(priv, TXRX_PHY_TEST_CTRL0, PHY_TESTCLR | PHY_TESTCLK);
    usleep_range(100, 150);

    /* Step4: apply REFCLK signal with the appropriate frequency */

    /* Step5: apply CFG_CLK signal with the appropriate frequency */

    /*
     * Step6: set MASTERSLAVEZ = 1'b0 (for SLAVE),
     *        phy1 is set as slave,controlled by isp
     */
    write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);

    /*
     * Step7: set BASEDIR_N = 1’b1 (for SLAVE),
     *        phy1 is set as slave,controlled by isp
     */
    write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);

    /* Step8: set all REQUEST inputs to zero, need to wait to take effective */
    /**它的PHY层将TurnRequest作为协议层的输入**/
    /* Disable lan turn around, which is ignored in receive mode */
    write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);/**PHY_REG(0x268, 4, 4)**/
    write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);/**PHY_REG(0x268, 4, 8)**/
    write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST,0);/**PHY_REG(RK3288_GRF_SOC_CON15, 4, 4)**/
    write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);/**PHY_REG(0x268, 4, 0)**/
    /* Step9: Wait for taking effective */
    usleep_range(100, 150);

    /* Step10: set TESTCLR=1'b0,TESTCLK=1'b1 need to wait to take effective */
    write_txrx_reg(priv, TXRX_PHY_TEST_CTRL0, PHY_TESTCLK);

    /* Step11: Wait for taking effective */
    usleep_range(100, 150);

    /*
     * Step12: configure Test Code 0x44 hsfreqrange according to values
     * step12.1:set clock lane
     * step12.2:set hsfreqrange by lane0(test code 0x44)
     */
    /***clock lane: continue mode: non-continue mode?
    continue mode: 连续时钟模式,即无论data lane上是否有数据在传输,clock lane总是在HS状态,从不进入LP状态
    non-continue mode:非连续时钟模式, 即当data lane 进入LP状态时,clock lane也会进入LP状态,在下次data lane进入HS状
    态时 clock lane会先于 data lane进入提前进入HS状态
    **/

    hsfreq <<= 1;
    /* HS hsfreq_range & lane 0  settle bypass */
    mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0, true);
    /* HS RX Control of lane0 */
    mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq, true);
    mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0, true);
    mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0, true);
    mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0, true);

    /* Step13: Configure analog references: of Test Code 0x22 */

    /*
     * Step14: Set ENABLE_N=1'b1, need to wait 5ns
     * Set lane num:
     * for 3288,controlled by isp,enable lanes actually
     * is set by grf_soc_con9[12:15];
     * for 3399,controlled by isp1,enable lanes actually
     * is set by isp1,
     * if run 3399 here operates grf_soc_con23[0:3]
     */
    if (is_linked_isp) {
        mipi_ctrl = readl(isp_mipi_ctrl);

        /*
         * Step15.1: Set ENABLE_N=1'b1, controlled by isp
         *             with isp_mipi_ctrl[18]
         */
        mipi_ctrl &= 0xfffbffff;
        mipi_ctrl |= 0x00040000;
/**CIF_MIPI_CTRL_CLOCKLANE_ENA**/

        /*
         * Step15.2: set lane num, controlled by isp
         *            with isp_mipi_ctrl[13:12]
         */
        if (sensor->lanes == 4)
            mipi_ctrl |= 0x00003000;
        else if (sensor->lanes == 3)
            mipi_ctrl |= 0x00002000;
        else if (sensor->lanes == 2)
            mipi_ctrl |= 0x00001000;
        else if (sensor->lanes == 1)
            mipi_ctrl &= 0xffff0fff;
/**CIF_MIPI_CTRL_NUM_LANES(a) (((a) & 0x3) << 12)**/

        writel(mipi_ctrl, isp_mipi_ctrl);

        write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
                  GENMASK(sensor->lanes - 1, 0));
/*PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),0x0268*/
    } else {
        write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLECLK, 1);
        write_txrx_reg(priv, TXRX_PHY_N_LANES, sensor->lanes - 1);
    }

    /* Step15: Wait for taking effective */
    usleep_range(100, 150);

    /*
     * Step16:Set SHUTDOWNZ=1'b1, phy1-rx controlled by isp,
     *        need to wait to take effective
     */

    /* Step17: Wait for taking effective */
    /* Step18:Set RSTZ=1'b1, phy1-rx controlled by isp*/
    if (!is_linked_isp) {
        write_txrx_reg(priv, TXRX_PHY_SHUTDOWNZ, 1);
        usleep_range(100, 150);
        write_txrx_reg(priv, TXRX_PHY_RSTZ, 1);
        write_txrx_reg(priv, TXRX_PHY_RESETN, 1);
    } else {
        mipi_ctrl = readl(isp_mipi_ctrl);
        mipi_ctrl |= 0x00000f00;
/**CIF_MIPI_CTRL_SHUTDOWNLANES(a)        (((a) & 0xF) << 8)**/
        writel(mipi_ctrl, isp_mipi_ctrl);
        dev_info(priv->dev, "enable mipi phy mipi_ctrl:0x%x\n", mipi_ctrl);

    }
    /**
    mipi DPHY 的规格书里对 data rate 有定义上下限值,最低 data rate为 80M bps, 最高为 2500M bps。
    由于mipi 传输时是双采样, 这样的话, 实际的差分时钟的上下限就是 40MHz - 1250 MHz
    mipi csi 的工作时钟是 10MHz, 这意味着 DPHY 的 data rate 不能超过 80M
    (这是DPHY data rate 的下限), 即实际的差分时钟不能超过40M**/

    /* Step19: d-phy calibration */
    //if (priv->data_rate_mbps >= 875)
    //    mipidphy1_calibration(priv);

    /*
     * Step20:Wait until STOPSTATEDATA_N & STOPSTATECLK
     *        outputs are asserted
     */
    usleep_range(100, 150);

4,isp控制mipi

Configure MIPI controller 

rkisp1_config_mipi

        数据ID(数据标识符)字段,长度为1个字节,包含了虚拟通道VC和数据类型DT字段。VC字段在DI字节里的最高的两位中体现。数据类型DT字段是DI字节里的低6位

        虚拟通道ID(虚拟通道标识符)字段,是一个4-bit或5-bit的ID,用来区分数据流中不同的逻辑数据通道

{
        .mbus_code    = MEDIA_BUS_FMT_SBGGR10_1X10,
        .fmt_type    = FMT_BAYER,
        .mipi_dt    = CIF_CSI2_DT_RAW10,
        .bayer_pat    = RAW_BGGR,
        .bus_width    = 10,
    }        

emd_vc = 0xFF;
        emd_dt = 0;        

        mipi_ctrl = CIF_MIPI_CTRL_NUM_LANES(lanes - 1) |
                CIF_MIPI_CTRL_SHUTDOWNLANES(0xf) |
                CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP |
                CIF_MIPI_CTRL_CLOCKLANE_ENA;

        writel(mipi_ctrl, base + CIF_MIPI_CTRL);

        /* Configure Data Type and Virtual Channel */
        writel(CIF_MIPI_DATA_SEL_DT(in_fmt->mipi_dt) | CIF_MIPI_DATA_SEL_VC(0),
                   base + CIF_MIPI_IMG_DATA_SEL);

        writel(CIF_MIPI_DATA_SEL_DT(emd_dt) | CIF_MIPI_DATA_SEL_VC(emd_vc),
               base + CIF_MIPI_ADD_DATA_SEL_1);
        writel(CIF_MIPI_DATA_SEL_DT(emd_dt) | CIF_MIPI_DATA_SEL_VC(emd_vc),
               base + CIF_MIPI_ADD_DATA_SEL_2);
        writel(CIF_MIPI_DATA_SEL_DT(emd_dt) | CIF_MIPI_DATA_SEL_VC(emd_vc),
               base + CIF_MIPI_ADD_DATA_SEL_3);
        writel(CIF_MIPI_DATA_SEL_DT(emd_dt) | CIF_MIPI_DATA_SEL_VC(emd_vc),
               base + CIF_MIPI_ADD_DATA_SEL_4);

        /* Clear MIPI interrupts */
        writel(~0, base + CIF_MIPI_ICR);
        /*
         * Disable CIF_MIPI_ERR_DPHY interrupt here temporary for
         * isp bus may be dead when switch isp.
         */
        writel(CIF_MIPI_FRAME_END | CIF_MIPI_ERR_CSI | CIF_MIPI_ERR_DPHY |
               CIF_MIPI_SYNC_FIFO_OVFLW(0x0F) | CIF_MIPI_ADD_DATA_OVFLW,
               base + CIF_MIPI_IMSC);

Configure MUX

    u32 dpcl = readl(dev->base_addr + CIF_VI_DPCL);

    if (sensor && sensor->mbus.type == V4L2_MBUS_CSI2) {
        ret = rkisp1_config_mipi(dev);
        dpcl |= CIF_VI_DPCL_IF_SEL_MIPI;
        dev->isp_inp = INP_CSI;
    } 

    writel(dpcl, dev->base_addr + CIF_VI_DPCL);

Activate MIPI

    if (sensor && sensor->mbus.type == V4L2_MBUS_CSI2) {
        {
            val = readl(base + CIF_MIPI_CTRL);
            writel(val | CIF_MIPI_CTRL_OUTPUT_ENA,
                   base + CIF_MIPI_CTRL);
        }
    }

Configure MIPI ISR

    writel(~0, base + CIF_MIPI_ICR);

    /*
     * Disable DPHY errctrl interrupt, because this dphy
     * erctrl signal is asserted until the next changes
     * of line state. This time is may be too long and cpu
     * is hold in this interrupt.
     */
    if (mis & CIF_MIPI_ERR_DPHY) {
        val = readl(base + CIF_MIPI_IMSC);
        writel(val & ~CIF_MIPI_ERR_DPHY, base + CIF_MIPI_IMSC);
        dev->isp_sdev.dphy_errctrl_disabled = true;
    }

    /*
     * Enable DPHY errctrl interrupt again, if mipi have receive
     * the whole frame without any error.
     */
    if (mis == CIF_MIPI_FRAME_END) {
        /*
         * Enable DPHY errctrl interrupt again, if mipi have receive
         * the whole frame without any error.
         */
        if (dev->isp_sdev.dphy_errctrl_disabled) {
            val = readl(base + CIF_MIPI_IMSC);
            val |= CIF_MIPI_ERR_DPHY;
            writel(val, base + CIF_MIPI_IMSC);
            dev->isp_sdev.dphy_errctrl_disabled = false;
        }
    } else {
        v4l2_warn(v4l2_dev, "MIPI mis error: 0x%08x\n", mis);
        val = readl(base + CIF_MIPI_CTRL);
        writel(val | CIF_MIPI_CTRL_FLUSH_FIFO, base + CIF_MIPI_CTRL);
    }

  • 11
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MIPI驱动代码通常由物理层驱动和显示控制器驱动两部分组成。以下是一个简单的MIPI DSI驱动例子,展示了如何使用Linux内核的MIPI DSI子系统来配置和控制MIPI显示器。 物理层驱动: ```c static struct mipi_dsi_phy_ops phy_ops = { .reset = mipi_dsi_phy_reset, .power_on = mipi_dsi_phy_power_on, .power_off = mipi_dsi_phy_power_off, .prepare = mipi_dsi_phy_prepare, .unprepare = mipi_dsi_phy_unprepare, .enable_hs = mipi_dsi_phy_enable_hs, .enable_lp = mipi_dsi_phy_enable_lp, .set_tlp = mipi_dsi_phy_set_tlp, .set_hs_clk = mipi_dsi_phy_set_hs_clk, }; static struct mipi_dsi_phy dsi_phy = { .dev = { .of_node = dev->of_node, }, .ops = &phy_ops, }; ret = mipi_dsi_attach_phy(dev, &dsi_phy); if (ret < 0) { dev_err(dev, "failed to attach phy\n"); return ret; } ``` 显示控制器驱动: ```c static struct mipi_dsi_device_ops dsi_ops = { .attach = mipi_dsi_attach, .detach = mipi_dsi_detach, .transfer = mipi_dsi_transfer, }; static struct mipi_dsi_device dsi_device = { .dev = { .of_node = dev->of_node, }, .mode_flags = MIPI_DSI_MODE_VIDEO, .lanes = 2, .format = MIPI_DSI_FMT_RGB888, .ops = &dsi_ops, }; ret = mipi_dsi_attach(&dsi_device); if (ret < 0) { dev_err(dev, "failed to attach dsi device\n"); return ret; } ``` 这个例子中,mipi_dsi_phy结构体定义了物理层驱动所需的函数指针。在mipi_dsi_attach_phy函数中,将DSI设备与物理层驱动绑定起来。 同时,mipi_dsi_device结构体定义了显示控制器驱动所需的参数,包括像素格式、数据通道数等。在mipi_dsi_attach函数中,将DSI设备与显示控制器驱动绑定起来,以便后续调用mipi_dsi_transfer函数来传输显示数据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值