【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第四篇 嵌入式Linux系统移植篇-第七十二章 内核配置屏幕驱动

i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口一应俱全。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相关历程,支持8路PDM接口、5路SAI接口、2路Speaker。系统支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9系统。适用于智能充电桩,物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网应用、

【公众号】迅为电子

【粉丝群】258811263


第七十二章 内核配置屏幕驱动

屏幕驱动是非常重要的一部分,本章节我们将学习如何在上个章节的基础上配置屏幕驱动。

72.1修改屏幕驱动

i.MX8M Mini 处理器的 MIPI_DSI 最高分辨率可达 WQHD1920x1080p60,24bpp),支持 123 4 个数据通道。 i.MX8MM 4 通道的 MIPI_DSI 通过 30pin 0.5mm 间距的 FPC 座引出(J9)。可连接迅为的 7 寸 mipi 显示屏。或者通过连接mipi转lvds转接板,然后再连接7寸lvds 屏,9.7寸lvds屏,10.1寸lvds屏。

NXP官方写好了mipi_dsi的节点,如下图所示:

/home/topeet/bsp_kernel_imx/bsp_kernel_imx/linux-imx/arch/arm64/boot/dts/freescale/fsl-imx8mm.dtsi文件的mipi_dsi节点,如下图所示:

由上图的compatible = "fsl,imx8mm-mipi-dsim"; 可以查找到匹配的驱动文件为/home/topeet/bsp_kernel_imx/bsp_kernel_imx/linux-imx/drivers/gpu/drm/imx/sec_mipi_dsim-imx.c文件。修改此文件,如下图所示:

#include "sec_mipi_pll_1432x.h"

 

static const struct sec_mipi_dsim_plat_data imx8mm_mipi_dsim_plat_data = {
	.version	= 0x1060200,
	.max_data_lanes = 4,
	.max_data_rate  = 1500000000ULL,
	.dphy_pll       = &pll_1432x,
	.dphy_timing	= dphy_timing_ln14lpp_v1p2,
	.num_dphy_timing = ARRAY_SIZE(dphy_timing_ln14lpp_v1p2),
	.dphy_timing_cmp = dphy_timing_default_cmp,
	.mode_valid	= NULL,
};

 

然后将网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\3.Linux系统移植\8.移植内核需要用到的文件\01 适配屏幕需要使用的文件”目录下的sec_mipi_pll_1432x.h文件拷贝到/home/topeet/bsp_kernel_imx/bsp_kernel_imx/linux-imx/drivers/gpu/drm/imx/目录下。

修改/home/topeet/imx8mm/linux/linux/linux-imx/drivers/gpu/drm/bridge/sec-dsim.c文件,修改如下内容:

添加如下内容:

#define MIPI_HFP_PKT_OVERHEAD   6

#define MIPI_HBP_PKT_OVERHEAD   6

#define MIPI_HSA_PKT_OVERHEAD   6

添加如下内容:

uint32_t pref_clk;

 

 添加如下内容:

#if 1	//add by cym 20201127
static int sec_mipi_dsim_set_pref_rate(struct sec_mipi_dsim *dsim)
{
        int ret;
        uint32_t rate;
        struct device *dev = dsim->dev;
        const struct sec_mipi_dsim_plat_data *pdata = dsim->pdata;
        const struct sec_mipi_dsim_pll *dpll = pdata->dphy_pll;
        const struct sec_mipi_dsim_range *fin_range = &dpll->fin;

        ret = of_property_read_u32(dev->of_node, "pref-rate", &rate);
        if (ret < 0) {
                dev_dbg(dev, "no valid rate assigned for pref clock\n");
                dsim->pref_clk = PHY_REF_CLK;
        } else {
                if (unlikely(rate < fin_range->min || rate > fin_range->max)) {
                        dev_warn(dev, "pref-rate get is invalid: %uKHz\n",
                                 rate);
                        dsim->pref_clk = PHY_REF_CLK;
                } else
                        dsim->pref_clk = rate;
        }

set_rate:
        ret = clk_set_rate(dsim->clk_pllref,
                           ((unsigned long)dsim->pref_clk) * 1000);
        if (ret) {
                dev_err(dev, "failed to set pll ref clock rate\n");
                return ret;
        }

        rate = clk_get_rate(dsim->clk_pllref) / 1000;
        if (unlikely(!rate)) {
                dev_err(dev, "failed to get pll ref clock rate\n");
                return -EINVAL;
        }

        if (rate != dsim->pref_clk) {
                if (unlikely(dsim->pref_clk == PHY_REF_CLK)) {
                        /* set default rate failed */
                        dev_err(dev, "no valid pll ref clock rate\n");
                        return -EINVAL;
                }

                dev_warn(dev, "invalid assigned rate for pref: %uKHz\n",
                         dsim->pref_clk);
                dev_warn(dev, "use default pref rate instead: %uKHz\n",
                         PHY_REF_CLK);

                dsim->pref_clk = PHY_REF_CLK;
                goto set_rate;
        }

        return 0;
}
#endif

注释掉如下内容:

//escmode |= ESCMODE_FORCEBTA;

 

修改如下红字的部分,修改为如下图所示: 

static void sec_mipi_dsim_set_main_mode(struct sec_mipi_dsim *dsim)
{
	uint32_t bpp, hfp_wc, hbp_wc, hsa_wc, wc;
	uint32_t mdresol = 0, mvporch = 0, mhporch = 0, msync = 0;
	struct videomode *vmode = &dsim->vmode;

	mdresol |= MDRESOL_SET_MAINVRESOL(vmode->vactive) |
		   MDRESOL_SET_MAINHRESOL(vmode->hactive);
	dsim_write(dsim, mdresol, DSIM_MDRESOL);

	mvporch |= MVPORCH_SET_MAINVBP(vmode->vback_porch)    |
		   MVPORCH_SET_STABLEVFP(vmode->vfront_porch) |
		   MVPORCH_SET_CMDALLOW(0x0);
	dsim_write(dsim, mvporch, DSIM_MVPORCH);

	bpp = mipi_dsi_pixel_format_to_bpp(dsim->format);

	/* calculate hfp & hbp word counts */
#if 0
	if (dsim->panel || !dsim->hpar) {
		hfp_wc = vmode->hfront_porch * (bpp >> 3);
		hbp_wc = vmode->hback_porch * (bpp >> 3);
	} else {
		hfp_wc = dsim->hpar->hfp_wc;
		hbp_wc = dsim->hpar->hbp_wc;
	}
#else
	if (!dsim->hpar) {
                wc = DIV_ROUND_UP(vmode->hfront_porch * (bpp >> 3),
                                  dsim->lanes);
                hfp_wc = wc > MIPI_HFP_PKT_OVERHEAD ?
                         wc - MIPI_HFP_PKT_OVERHEAD : vmode->hfront_porch;
                wc = DIV_ROUND_UP(vmode->hback_porch * (bpp >> 3),
                                  dsim->lanes);
                hbp_wc = wc > MIPI_HBP_PKT_OVERHEAD ?
                         wc - MIPI_HBP_PKT_OVERHEAD : vmode->hback_porch;
        } else {
                hfp_wc = dsim->hpar->hfp_wc;
                hbp_wc = dsim->hpar->hbp_wc;
        }
#endif

	mhporch |= MHPORCH_SET_MAINHFP(hfp_wc) |
		   MHPORCH_SET_MAINHBP(hbp_wc);

	dsim_write(dsim, mhporch, DSIM_MHPORCH);

	/* calculate hsa word counts */
#if 0
	if (dsim->panel || !dsim->hpar)
		hsa_wc = vmode->hsync_len * (bpp >> 3);
	else
		hsa_wc = dsim->hpar->hsa_wc;

	msync |= MSYNC_SET_MAINVSA(vmode->vsync_len) |
		 MSYNC_SET_MAINHSA(hsa_wc);
#else
	if (!dsim->hpar) {
                wc = DIV_ROUND_UP(vmode->hsync_len * (bpp >> 3),
                                  dsim->lanes);
                hsa_wc = wc > MIPI_HSA_PKT_OVERHEAD ?
                         wc - MIPI_HSA_PKT_OVERHEAD : vmode->hsync_len;
        } else
                hsa_wc = dsim->hpar->hsa_wc;

	msync |= MSYNC_SET_MAINVSA(vmode->vsync_len) |
                 MSYNC_SET_MAINHSA(hsa_wc);
#endif

	dsim_write(dsim, msync, DSIM_MSYNC);
}

修改如下图所示内容:

//esc_prescaler = DIV_ROUND_UP_ULL(byte_clk, MAX_ESC_CLK_FREQ);

esc_prescaler = DIV_ROUND_UP(byte_clk, MAX_ESC_CLK_FREQ);

 添加如下内容,如下图所示:

#if 1
struct dsim_pll_pms *sec_mipi_dsim_calc_pmsk(struct sec_mipi_dsim *dsim)
{
        uint32_t p, m, s;
        uint32_t best_p = 0, best_m = 0, best_s = 0;
        uint32_t fin, fout;
        uint32_t s_pow_2, raw_s;
        uint64_t mfin, pfvco, pfout, psfout;
        uint32_t delta, best_delta = ~0U;
        struct dsim_pll_pms *pll_pms;
        struct device *dev = dsim->dev;
        const struct sec_mipi_dsim_plat_data *pdata = dsim->pdata;
        struct sec_mipi_dsim_pll dpll = *pdata->dphy_pll;

        struct sec_mipi_dsim_range *prange = &dpll.p;
        struct sec_mipi_dsim_range *mrange = &dpll.m;
        struct sec_mipi_dsim_range *srange = &dpll.s;
        struct sec_mipi_dsim_range *krange = &dpll.k;
        struct sec_mipi_dsim_range *fvco_range  = &dpll.fvco;
        struct sec_mipi_dsim_range *fpref_range = &dpll.fpref;
        struct sec_mipi_dsim_range pr_new = *prange;
        struct sec_mipi_dsim_range sr_new = *srange;

        pll_pms = devm_kzalloc(dev, sizeof(*pll_pms), GFP_KERNEL);
        if (!pll_pms) {
                dev_err(dev, "Unable to allocate 'pll_pms'\n");
                return ERR_PTR(-ENOMEM);
        }

        fout = dsim->bit_clk;
        fin  = dsim->pref_clk;

        /* TODO: ignore 'k' for PMS calculation,
         * only use 'p', 'm' and 's' to generate
         * the requested PLL output clock.
         */
        krange->min = 0;
        krange->max = 0;

        /* narrow 'p' range via 'Fpref' limitation:
         * Fpref : [2MHz ~ 30MHz] (Fpref = Fin / p)
         */
        prange->min = max(prange->min, DIV_ROUND_UP(fin, fpref_range->max));
        prange->max = min(prange->max, fin / fpref_range->min);

        /* narrow 'm' range via 'Fvco' limitation:
         * Fvco: [1050MHz ~ 2100MHz] (Fvco = ((m + k / 65536) * Fin) / p)
         * So, m = Fvco * p / Fin and Fvco > Fin;
         */
        pfvco = (uint64_t)fvco_range->min * prange->min;
        mrange->min = max_t(uint32_t, mrange->min,
                            DIV_ROUND_UP_ULL(pfvco, fin));
        pfvco = (uint64_t)fvco_range->max * prange->max;
        mrange->max = min_t(uint32_t, mrange->max,
                            DIV_ROUND_UP_ULL(pfvco, fin));

        dev_dbg(dev, "p: min = %u, max = %u, "
                     "m: min = %u, max = %u, "
                     "s: min = %u, max = %u\n",
                prange->min, prange->max, mrange->min,
                mrange->max, srange->min, srange->max);

        /* first determine 'm', then can determine 'p', last determine 's' */
        for (m = mrange->min; m <= mrange->max; m++) {
                /* p = m * Fin / Fvco */
                mfin = (uint64_t)m * fin;
                pr_new.min = max_t(uint32_t, prange->min,
                                   DIV_ROUND_UP_ULL(mfin, fvco_range->max));
                pr_new.max = min_t(uint32_t, prange->max,
                                   (mfin / fvco_range->min));

                if (pr_new.max < pr_new.min || pr_new.min < prange->min)
                        continue;

                for (p = pr_new.min; p <= pr_new.max; p++) {
                        /* s = order_pow_of_two((m * Fin) / (p * Fout)) */
                        pfout = (uint64_t)p * fout;
                        raw_s = DIV_ROUND_CLOSEST_ULL(mfin, pfout);

                        s_pow_2 = rounddown_pow_of_two(raw_s);
                        sr_new.min = max_t(uint32_t, srange->min,
                                           order_base_2(s_pow_2));

                        s_pow_2 = roundup_pow_of_two(DIV_ROUND_CLOSEST_ULL(mfin, pfout));
                        sr_new.max = min_t(uint32_t, srange->max,
                                           order_base_2(s_pow_2));

                        if (sr_new.max < sr_new.min || sr_new.min < srange->min)
                                continue;

                        for (s = sr_new.min; s <= sr_new.max; s++) {
                                /* fout = m * Fin / (p * 2^s) */
                                psfout = pfout * (1 << s);
                                delta = abs(psfout - mfin);
                                if (delta < best_delta) {
                                        best_p = p;
                                        best_m = m;
                                        best_s = s;
                                        best_delta = delta;
                                }
                        }
                }
        }

        if (best_delta == ~0U) {
                devm_kfree(dev, pll_pms);
                return ERR_PTR(-EINVAL);
        }

        pll_pms->p = best_p;
        pll_pms->m = best_m;
        pll_pms->s = best_s;

        dev_dbg(dev, "fout = %u, fin = %u, m = %u, "
                     "p = %u, s = %u, best_delta = %u\n",
                fout, fin, pll_pms->m, pll_pms->p, pll_pms->s, best_delta);

        return pll_pms;
}
#endif

 

修改如下所示内容。红色部分是修改过的。 

int sec_mipi_dsim_check_pll_out(void *driver_private,
				const struct drm_display_mode *mode)
{
	int bpp;
	uint64_t pix_clk, bit_clk, ref_clk;
	struct sec_mipi_dsim *dsim = driver_private;
	const struct sec_mipi_dsim_plat_data *pdata = dsim->pdata;
	const struct dsim_hblank_par *hpar;
	const struct dsim_pll_pms *pmsk;

	bpp = mipi_dsi_pixel_format_to_bpp(dsim->format);
	if (bpp < 0)
		return -EINVAL;
#if 0
	pix_clk = mode->clock * 1000;
	bit_clk = DIV_ROUND_UP_ULL(pix_clk * bpp, dsim->lanes);

	if (bit_clk > pdata->max_data_rate) {
		dev_err(dsim->dev,
			"reuest bit clk freq exceeds lane's maximum value\n");
		return -EINVAL;
	}

	dsim->pix_clk = DIV_ROUND_UP_ULL(pix_clk, 1000);
	dsim->bit_clk = DIV_ROUND_UP_ULL(bit_clk, 1000);

	dsim->pms = 0x4210;
	dsim->hpar = NULL;
	if (dsim->panel)
		return 0;
#else
	pix_clk = mode->clock;
        bit_clk = DIV_ROUND_UP(pix_clk * bpp, dsim->lanes);

        if (bit_clk * 1000 > pdata->max_data_rate) {
                dev_err(dsim->dev,
                        "reuest bit clk freq exceeds lane's maximum value\n");
                return -EINVAL;
        }

        dsim->pix_clk = pix_clk;
        dsim->bit_clk = bit_clk;
        dsim->hpar = NULL;

        pmsk = sec_mipi_dsim_calc_pmsk(dsim);
        if (IS_ERR(pmsk)) {
                dev_err(dsim->dev,
                        "failed to get pmsk for: fin = %u, fout = %u\n",
                        dsim->pref_clk, dsim->bit_clk);
                return -EINVAL;
        }

        dsim->pms = PLLCTRL_SET_P(pmsk->p) |
                    PLLCTRL_SET_M(pmsk->m) |
                    PLLCTRL_SET_S(pmsk->s);

        /* free 'dsim_pll_pms' structure data which is
         * allocated in 'sec_mipi_dsim_calc_pmsk()'.
         */
        devm_kfree(dsim->dev, (void *)pmsk);
#endif

#if 0
	if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
		hpar = sec_mipi_dsim_get_hblank_par(mode->name,
						    mode->vrefresh,
						    dsim->lanes);
		if (!hpar)
			return -EINVAL;
		dsim->hpar = hpar;

		pms = sec_mipi_dsim_get_pms(dsim->bit_clk);
		if (WARN_ON(!pms))
			return -EINVAL;

		ref_clk = PHY_REF_CLK / 1000;
		/* TODO: add PMS calculate and check
		 * Only support '1080p@60Hz' for now,
		 * add other modes support later
		 */
		dsim->pms = PLLCTRL_SET_P(pms->p) |
			    PLLCTRL_SET_M(pms->m) |
			    PLLCTRL_SET_S(pms->s);
	}
#else
	if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
                hpar = sec_mipi_dsim_get_hblank_par(mode->name,
                                                    mode->vrefresh,
                                                    dsim->lanes);
                dsim->hpar = hpar;
                if (!hpar)
                        dev_dbg(dsim->dev, "no pre-exist hpar can be used\n");
        }
#endif

	return 0;
}

修改为如下图所示,红色部分是修改过的内容。

static void sec_mipi_dsim_set_main_mode(struct sec_mipi_dsim *dsim)
{
	uint32_t bpp, hfp_wc, hbp_wc, hsa_wc, wc;
	uint32_t mdresol = 0, mvporch = 0, mhporch = 0, msync = 0;
	struct videomode *vmode = &dsim->vmode;

	mdresol |= MDRESOL_SET_MAINVRESOL(vmode->vactive) |
		   MDRESOL_SET_MAINHRESOL(vmode->hactive);
	dsim_write(dsim, mdresol, DSIM_MDRESOL);

	mvporch |= MVPORCH_SET_MAINVBP(vmode->vback_porch)    |
		   MVPORCH_SET_STABLEVFP(vmode->vfront_porch) |
		   MVPORCH_SET_CMDALLOW(0x0);
	dsim_write(dsim, mvporch, DSIM_MVPORCH);

	bpp = mipi_dsi_pixel_format_to_bpp(dsim->format);

	/* calculate hfp & hbp word counts */
#if 0
	if (dsim->panel || !dsim->hpar) {
		hfp_wc = vmode->hfront_porch * (bpp >> 3);
		hbp_wc = vmode->hback_porch * (bpp >> 3);
	} else {
		hfp_wc = dsim->hpar->hfp_wc;
		hbp_wc = dsim->hpar->hbp_wc;
	}
#else
	if (!dsim->hpar) {
                wc = DIV_ROUND_UP(vmode->hfront_porch * (bpp >> 3),
                                  dsim->lanes);
                hfp_wc = wc > MIPI_HFP_PKT_OVERHEAD ?
                         wc - MIPI_HFP_PKT_OVERHEAD : vmode->hfront_porch;
                wc = DIV_ROUND_UP(vmode->hback_porch * (bpp >> 3),
                                  dsim->lanes);
                hbp_wc = wc > MIPI_HBP_PKT_OVERHEAD ?
                         wc - MIPI_HBP_PKT_OVERHEAD : vmode->hback_porch;
        } else {
                hfp_wc = dsim->hpar->hfp_wc;
                hbp_wc = dsim->hpar->hbp_wc;
        }
#endif

	mhporch |= MHPORCH_SET_MAINHFP(hfp_wc) |
		   MHPORCH_SET_MAINHBP(hbp_wc);

	dsim_write(dsim, mhporch, DSIM_MHPORCH);

	/* calculate hsa word counts */
#if 0
	if (dsim->panel || !dsim->hpar)
		hsa_wc = vmode->hsync_len * (bpp >> 3);
	else
		hsa_wc = dsim->hpar->hsa_wc;

	msync |= MSYNC_SET_MAINVSA(vmode->vsync_len) |
		 MSYNC_SET_MAINHSA(hsa_wc);
#else
	if (!dsim->hpar) {
                wc = DIV_ROUND_UP(vmode->hsync_len * (bpp >> 3),
                                  dsim->lanes);
                hsa_wc = wc > MIPI_HSA_PKT_OVERHEAD ?
                         wc - MIPI_HSA_PKT_OVERHEAD : vmode->hsync_len;
        } else
                hsa_wc = dsim->hpar->hsa_wc;

	msync |= MSYNC_SET_MAINVSA(vmode->vsync_len) |
                 MSYNC_SET_MAINHSA(hsa_wc);
#endif

	dsim_write(dsim, msync, DSIM_MSYNC);
}

添加如下内容:

#if 0
	{
		//cym
#if 0
		dsim_write(dsim, 0x91f80002, DSIM_CLKCTRL);
		dsim_write(dsim, 0x82570400, DSIM_MDRESOL);
		dsim_write(dsim, 0x20018, DSIM_MVPORCH);
		dsim_write(dsim, 0xc0072, DSIM_MHPORCH);
		dsim_write(dsim, 0xc00051, DSIM_MSYNC);
		dsim_write(dsim, 0x7ac7bfff, DSIM_RXFIFO);
		dsim_write(dsim, 0x825904, DSIM_PLLCTRL);
		dsim_write(dsim, 0x203, DSIM_PHYTIMING);
		dsim_write(dsim, 0x20d0803, DSIM_PHYTIMING1);
		dsim_write(dsim, 0x30305, DSIM_PHYTIMING2);
#endif

		printk("DSIM_STATUS:0x%x\n", dsim_read(dsim, DSIM_STATUS));
		printk("DSIM_RGB_STATUS:0x%x\n", dsim_read(dsim, DSIM_RGB_STATUS));
		printk("DSIM_SWRST:0x%x\n", dsim_read(dsim, DSIM_SWRST));
		printk("DSIM_CLKCTRL:0x%x\n", dsim_read(dsim, DSIM_CLKCTRL));
		printk("DSIM_TIMEOUT:0x%x\n", dsim_read(dsim, DSIM_TIMEOUT));
		printk("DSIM_CONFIG:0x%x\n", dsim_read(dsim, DSIM_CONFIG));
		printk("DSIM_ESCMODE:0x%x\n", dsim_read(dsim, DSIM_ESCMODE));
		printk("DSIM_MDRESOL:0x%x\n", dsim_read(dsim, DSIM_MDRESOL));
		printk("DSIM_MVPORCH:0x%x\n", dsim_read(dsim, DSIM_MVPORCH));
		printk("DSIM_MHPORCH:0x%x\n", dsim_read(dsim, DSIM_MHPORCH));
		printk("DSIM_MSYNC:0x%x\n", dsim_read(dsim, DSIM_MSYNC));
		printk("DSIM_SDRESOL:0x%x\n", dsim_read(dsim, DSIM_SDRESOL));
		printk("DSIM_INTSRC:0x%x\n", dsim_read(dsim, DSIM_INTSRC));
		printk("DSIM_INTMSK:0x%x\n", dsim_read(dsim, DSIM_INTMSK));
		printk("DSIM_PKTHDR:0x%x\n", dsim_read(dsim, DSIM_PKTHDR));
		printk("DSIM_PAYLOAD:0x%x\n", dsim_read(dsim, DSIM_PAYLOAD));
		printk("DSIM_RXFIFO:0x%x\n", dsim_read(dsim, DSIM_RXFIFO));
		printk("DSIM_FIFOTHLD:0x%x\n", dsim_read(dsim, DSIM_FIFOTHLD));
		printk("DSIM_FIFOCTRL:0x%x\n", dsim_read(dsim, DSIM_FIFOCTRL));
		printk("DSIM_MEMACCHR:0x%x\n", dsim_read(dsim, DSIM_MEMACCHR));
		printk("DSIM_MULTI_PKT:0x%x\n", dsim_read(dsim, DSIM_MULTI_PKT));
		printk("DSIM_PLLCTRL_1G:0x%x\n", dsim_read(dsim, DSIM_PLLCTRL_1G));
		printk("DSIM_PLLCTRL:0x%x\n", dsim_read(dsim, DSIM_PLLCTRL));
		printk("DSIM_PLLCTRL1:0x%x\n", dsim_read(dsim, DSIM_PLLCTRL1));
		printk("DSIM_PLLCTRL2:0x%x\n", dsim_read(dsim, DSIM_PLLCTRL2));
		printk("DSIM_PLLTMR:0x%x\n", dsim_read(dsim, DSIM_PLLTMR));
		printk("DSIM_PHYTIMING:0x%x\n", dsim_read(dsim, DSIM_PHYTIMING));
		printk("DSIM_PHYTIMING1:0x%x\n", dsim_read(dsim, DSIM_PHYTIMING1));
		printk("DSIM_PHYTIMING2:0x%x\n", dsim_read(dsim, DSIM_PHYTIMING2));
	}
#endif

修改如下图所示:

#if 0	//modify by cym 20201127
	/* TODO: set pll ref clock rate to be fixed with 27MHz */
	ret = clk_set_rate(dsim->clk_pllref, PHY_REF_CLK);
#else
	/* set suitable rate for phy ref clock */
        ret = sec_mipi_dsim_set_pref_rate(dsim);
#endif

修改/home/topeet/imx8mm/linux/linux/linux-imx/include/drm/bridge/sec_mipi_dsim.h,添加如下所示代码,红色部分是修改过的代码 

#ifndef __SEC_MIPI_DSIM_H__
#define __SEC_MIPI_DSIM_H__

#include <drm/drmP.h>
#include <linux/bsearch.h>

struct sec_mipi_dsim_dphy_timing;
struct sec_mipi_dsim_pll;//add by cym 20201127

struct sec_mipi_dsim_plat_data {
	uint32_t version;
	uint32_t max_data_lanes;
	uint64_t max_data_rate;
	const struct sec_mipi_dsim_dphy_timing *dphy_timing;
	uint32_t num_dphy_timing;
	const struct sec_mipi_dsim_pll *dphy_pll;//add by cym 20201127
	int (*dphy_timing_cmp)(const void *key, const void *elt);
	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
					   struct drm_display_mode *mode);
};

/* add by cym 20201127 */
/* DPHY PLL structure */
struct sec_mipi_dsim_range {
        uint32_t min;
        uint32_t max;
};

struct sec_mipi_dsim_pll {
        struct sec_mipi_dsim_range p;
        struct sec_mipi_dsim_range m;
        struct sec_mipi_dsim_range s;
        struct sec_mipi_dsim_range k;
        struct sec_mipi_dsim_range fin;
        struct sec_mipi_dsim_range fpref;
        struct sec_mipi_dsim_range fvco;
};
/* end add */

/* DPHY timings structure */
struct sec_mipi_dsim_dphy_timing {
	uint32_t bit_clk;	/* MHz */

	uint32_t clk_prepare;
	uint32_t clk_zero;
	uint32_t clk_post;
	uint32_t clk_trail;

	uint32_t hs_prepare;
	uint32_t hs_zero;
	uint32_t hs_trail;

	uint32_t lpx;
	uint32_t hs_exit;
};

#define DSIM_DPHY_TIMING(bclk, cpre, czero, cpost, ctrail,	\
			 hpre, hzero, htrail, lp, hexit)	\
	.bit_clk	= bclk,					\
	.clk_prepare	= cpre,					\
	.clk_zero	= czero,				\
	.clk_post	= cpost,				\
	.clk_trail	= ctrail,				\
	.hs_prepare	= hpre,					\
	.hs_zero	= hzero,				\
	.hs_trail	= htrail,				\
	.lpx		= lp,					\
	.hs_exit	= hexit

static inline int dphy_timing_default_cmp(const void *key, const void *elt)
{
	const struct sec_mipi_dsim_dphy_timing *_key = key;
	const struct sec_mipi_dsim_dphy_timing *_elt = elt;

	/* find an element whose 'bit_clk' is equal to the
	 * the key's 'bit_clk' value or, the difference
	 * between them is less than 5.
	 */
	if (abs((int)(_elt->bit_clk - _key->bit_clk)) <= 5)
		return 0;

	if (_key->bit_clk < _elt->bit_clk)
		/* search bottom half */
		return 1;
	else
		/* search top half */
		return -1;
}

int sec_mipi_dsim_check_pll_out(void *driver_private,
				const struct drm_display_mode *mode);
int sec_mipi_dsim_bind(struct device *dev, struct device *master, void *data,
		       struct drm_encoder *encoder, struct resource *res,
		       int irq, const struct sec_mipi_dsim_plat_data *pdata);
void sec_mipi_dsim_unbind(struct device *dev, struct device *master, void *data);

void sec_mipi_dsim_suspend(struct device *dev);
void sec_mipi_dsim_resume(struct device *dev);

#endif

然后我们修改/home/topeet/bsp_kernel_imx/bsp_kernel_imx/linux-imx/arch/arm64/boot/dts/freescale/itop8mm-evk.dts设备树文件,&mipi_dsi节点如下图所示:

我们只需要在这个节点下配置屏幕设备树节点,因为要支持四种屏幕,所以我们先来设置四种屏幕的宏定义,如下所示,如果我们想要编译某种屏幕,将某种屏幕的宏定义使能就好了。

//#define LCD_TYPE_10_1         1
#define LCD_TYPE_7_0          1
//#define LCD_TYPE_9_7          1
//#define LCD_TYPE_MIPI_7_0       1

首先我们删除原来节点下的内容: 

然后我们写个逻辑框架,如下所示,数字代表是我们要添加的代码,mipi 7寸屏的配置写在数字111处,依次类推。

我们先来配置mipi 7寸屏幕,以下代码填写在111处 

panel@0 {/* 7.0 inch lvds screen */
    /*compatible是系统用来决定绑定到设备的设备驱动的关键,在内核源码driver目录下搜索toshiba,panel-tc358775会找到对应的驱动文件*/
	compatible = "toshiba,panel-tc358775";  	/*匹配驱动的值*/
reg = <0>;
	pinctrl-0 = <&pinctrl_mipi_dsi_en>; /*使用到的IO*/
	reset-gpio = <&gpio1 8 GPIO_ACTIVE_HIGH>; /*重置GPIO*/
	status = "okay";	/*状态为okay*/
	panel-width-mm = <154>; 
	panel-height-mm = <85>; 
	dsi-lanes = <4>;  /*显示通道设置为四通道*/

	backlight = <&backlight0>; /*背光*/
	client-device  = <&tc358775>; /*客户端*/
	display-timings {
					native-mode = <&timing0>;/*时序信息*/
					timing0:timing0{
							clock-frequency = <70000000>;*//*LCD像素时钟,单位是Hz*/
							hactive = <800>; /*LCD  X轴像素个数*/
							hsync-len = <10>;	/*LCD hspw参数*/						
hback-porch = <100>;/* important *//*LCD hbp参数*/
							hfront-porch = <24>;/*LCD hfp参数*/
							vactive = <1280>;/*LCD  Y轴像素个数*/
							vsync-len = <10>;/*LCD vspw参数*/
							vback-porch = <25>;/*LCD vbp参数*/
							vfront-porch = <10>;/*LCD vfp参数*/

							vsync-active = <0>;/*vsync 数据线极性*/
							hsync-active =<0>;/*hsync 数据线极性*/
							de-active =<0>;/*de 数据线极性*/
							pixelclk-active =<0>;/*clk数据线极性*/
					};
	};
};

上面我们已经解释了设备树配置的参数,那么屏幕的时序信息是什么意思呢?

我们把屏幕想象成一幅画,显示的过程其实就是用笔在不同的像素点画上不同的颜色,这根笔按照从左到右,从上到下的顺序画每个像素点,当画完最后一个像素点,一幅画也就画好了。我们画一个示意图,如下所示:

Hsync 是水平同步信号,也叫做行同步信号,当产生此信号的话就表示开始显示新的一行了,所以此信号都是在图的最左边,Vsync是垂直同步信号,也叫做同步信号,当产生此信号的话就表示开始显示新的一帧图像了,所以此信号在上图的左上角。其实真正显示的是中间白色的部分。

显示完一行后会发出Hsync信号,然后电子枪会关闭,然后很快移动到屏幕的左边,hsync信号结束之后便可以显示新的一行数据,电子枪会打开,那么hsync信号结束到开始之间会插入一段延时,这个延时就是hbp,同理,vbp也是这样的道理,HBP HFP VBP VFP就是导致黑边的原因,这四个值的具体值得查阅LCD数据手册。

接下来我们来配置lvds 7寸屏幕,以下代码填写在222处

    panel@0 {/* 7.0 inch lvds screen */
                compatible = "toshiba,panel-tc358775";
                reg = <0>;
                pinctrl-0 = <&pinctrl_mipi_dsi_en>;
                reset-gpio = <&gpio1 8 GPIO_ACTIVE_HIGH>;
                status = "okay";
				panel-width-mm = <154>;
                panel-height-mm = <85>;
                dsi-lanes = <4>;

                backlight = <&backlight0>;
                client-device  = <&tc358775>;
                display-timings {
                                native-mode = <&timing0>;
                                timing0:timing0{
                                        clock-frequency = <70000000>;/*<70000000>;*/
                                        hactive = <800>;
                                        hsync-len = <10>;
                                        hback-porch = <100>;/* important */
                                        hfront-porch = <24>;
                                        vactive = <1280>;
                                        vsync-len = <10>;
                                        vback-porch = <25>;
                                        vfront-porch = <10>;

                                        vsync-active = <0>;
                                        hsync-active =<0>;
                                        de-active =<0>;
                                        pixelclk-active =<0>;
                                };
                };
        };

 接下来我们来配置lvds 9.7寸屏幕,以下代码填写在333处

   panel@1 {/* 9.7 inch screen */
                compatible = "toshiba,panel-tc358775";
                reg = <0>;
                pinctrl-0 = <&pinctrl_mipi_dsi_en>;
                reset-gpio = <&gpio1 8 GPIO_ACTIVE_HIGH>;
                status = "okay";/*"disabled";*/
                panel-width-mm = <154>;
                panel-height-mm = <85>;
                dsi-lanes = <4>;

                backlight = <&backlight0>;
                client-device  = <&tc358775>;

                display-timings {
                        timing {
                                clock-frequency = <65000000>;//<132000000>;
                                hactive = <1024>;
                                vactive = <768>;
                                hfront-porch = <4>;
                                hsync-len = <116>;
                                hback-porch = <20>;
                                vfront-porch = <2>;
                                vsync-len = <10>;//<12>;//<4>;
                                vback-porch = <20>;//<16>;//<6>;
                                hsync-active = <0>;
                                vsync-active = <0>;
                                de-active = <0>;
                                pixelclk-active = <0>;
                        };
                };
        };

接下来我们来配置lvds 10.1寸屏幕,以下代码填写在444处。

 panel@0 {/* 10.1 inch screen */
                compatible = "toshiba,panel-tc358775";
                reg = <0>;
                pinctrl-0 = <&pinctrl_mipi_dsi_en>;
                reset-gpio = <&gpio1 8 GPIO_ACTIVE_HIGH>;
                status = "okay";
                panel-width-mm = <154>;
                panel-height-mm = <85>;
                dsi-lanes = <4>;

                backlight = <&backlight0>;
                client-device  = <&tc358775>;
                display-timings {
                                native-mode = <&timing0>;
                                timing0:timing0{
                                        clock-frequency = <50000000>;
                                        hactive = <1024>;
                                        hsync-len = <116>;
                                        hback-porch = <160>;
                                        hfront-porch = <24>;
                                        vactive = <600>;
                                        vsync-len = <3>;
                                        vback-porch = <24>;
                                        vfront-porch = <2>;

                                        vsync-active = <0>;
                                        hsync-active =<0>;
                                        de-active =<0>;
                                        pixelclk-active =<0>;
                                };
                };
        };

然后在根目录下配置背光信息,添加backlight0节点,如下图所示:

backlight0: backlight@0 {
		compatible = "pwm-backlight";
		pwms = <&pwm1 0 50000 0>;

		brightness-levels = < 0 23 23 23 23 23 23 23 23 23
					23 23 23 23 23 23 23 23 23 23 
					23 23 23 23 24 25 26 27 28 29
					30 31 32 33 34 35 36 37 38 39
					40 41 42 43 44 45 46 47 48 49
					50 51 52 53 54 55 56 57 58 59
					60 61 62 63 64 65 66 67 68 69
					70 71 72 73 74 75 76 77 78 79
					80 81 82 83 84 85 86 87 88 89
					90 91 92 93 94 95 96 97 98 99
					100>;
                default-brightness-level = <89>;
        };

然后在根目录下配置&pwm1节点,如下图所示:

&pwm1 {

        pinctrl-names = "default";

        pinctrl-0 = <&pinctrl_pwm1>;

        status = "okay";

};

然后在pinctrl中添加pwm1信息,如下图所示:

在&iomuxc中默认配置好了引脚

pinctrl_mipi_dsi_en: mipi_dsi_en {
	fsl,pins = <
			MX8MM_IOMUXC_GPIO1_IO08_GPIO1_IO8		0x16
			>;
		};
pinctrl_pwm1: pwm1 {
	fsl,pins = <
			MX8MM_IOMUXC_GPIO1_IO01_PWM1_OUT                0x1d0
			>;
		};

 然后再i2c2下面注释掉以下节点

/*
	adv_bridge: adv7535@3d {
		compatible = "adi,adv7533";
		reg = <0x3d>;
		adi,addr-cec = <0x3b>;
		adi,dsi-lanes = <4>;
		status = "okay";

		port {
			adv7535_from_dsim: endpoint {
				remote-endpoint = <&dsim_to_adv7535>;
			};
		};
	};
*/

然后再i2c2节点下面配置

tc358775:tc358775@0x0f{

                compatible = "toshiba,tc358775";

                reg  = <0x0f>;

                status = "okay";

        };

 

 

72.2配置屏幕驱动

将网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\3.Linux系统移植\8.移植内核需要用到的文件\01 适配屏幕需要使用的文件”目录下的panel-itop_mipi_screen.c和tc358775_i2c.c和panel-tc358775.c拷贝到内核源码的

/linux-imx/drivers/gpu/drm/panel/目录下。

然后修改linux-imx/drivers/gpu/drm/panel/目录下Makefile文件,添加如下内容:

obj-$(CONFIG_DRM_PANEL_ITOP_MIPI) += panel-itop_mipi_screen.o
obj-$(CONFIG_DRM_PANEL_TC358775) += panel-tc358775.o
obj-$(CONFIG_DRM_PANEL_TC358775) += tc358775_i2c.o

然后修改linux-imx/drivers/gpu/drm/panel/目录下kconfig文件,添加如下内容: 

        tristate "itop mipi screen panel"
        depends on OF
        depends on DRM_MIPI_DSI
        depends on BACKLIGHT_CLASS_DEVICE
        help
          Say Y here if you want to enable support for itop mipi screen panel
          DSI panel.

config DRM_PANEL_TC358775
        tristate "TC358775 FHD panel"
        depends on OF
        depends on DRM_MIPI_DSI
        depends on BACKLIGHT_CLASS_DEVICE
        help
          Say Y here if you want to enable support for  TC358775 FHD
          DSI panel.

72.3编译屏幕驱动到内核

在上面小节配置完毕,保存修改,然后重新打开一个终端,输入以下命令,将默认的配置文件写入到.config文件。

make defconfig

然后输入以下命令进入menuconfig进行配置,如下所示:

export ARCH=arm64

make menuconfig

然后按如下图所示的路径,配置上屏幕,如下图所示:

 

72.4使能 Linux logo 显示 

Linux 内核启动的时候可以选择显示小企鹅 logo,只要这个小企鹅 logo 显示没问题那么我们的 LCD 驱动基本就工作正常了。这个 logo显示是要配置的,不过 Linux 内核一般都会默认开启 logo 显示,但是奔着学习的目的,我们还是来看一下如何使能 Linux logo 显示。打开 Linux 内核图形化配置界面,按下路径找到对应的配置项:

-> Device Drivers 
-> Graphics support 
-> Bootup logo (LOGO [=y]) 
-> Standard black and white Linux logo 
-> Standard 16-color Linux logo 
-> Standard 224-color Linux logo 

最后保存配置文件到arch/arm64/configs/defconfig文件,如下图所示: 

72.5配置触摸驱动

Mipi 7寸屏幕,lvds7 寸屏幕 ,lvds9.7寸屏幕使用的触摸芯片是ft5x06, lvds 10.1寸屏幕使用的触摸芯片是gt911。

首先修改/home/topeet/bsp_kernel_imx/bsp_kernel_imx/linux-imx/arch/arm64/boot/dts/freescale/itop8mm-evk.dts设备树文件 ,配置触摸芯片的节点,如下图示,挂载在i2c2上。

&i2c2{
.............................................................................................

     #if defined(LCD_TYPE_7_0) || defined(LCD_TYPE_9_7) || defined(LCD_TYPE_MIPI_7_0)
        ft5x06_ts@38 {
                compatible = "edt,edt-ft5x06";
                reg = <0x38>;
                pinctrl-names = "defaults";
                pinctrl-0 = <&pinctrl_ft5x06_int>;
                interrupt-parent = <&gpio1>;
                interrupts = <15 2>;
                status = "okay";
        };
#elif defined(LCD_TYPE_10_1)
        gt911@14 {
                compatible = "goodix,gt911";
                reg = <0x14>;/*<0x5d>;*//*<0x14>;*/
                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_ft5x06_int>;

                interrupt-parent = <&gpio1>;
                interrupts = <15 2>;/*<15  IRQ_TYPE_EDGE_RISING>;*/
                /*synaptics,y-rotation;*/
                esd-recovery-timeout-ms = <2000>;
                irq-gpios = <&gpio1 15 0>;/*<&gpio1 15 GPIO_ACTIVE_HIGH>;*/
                reset-gpios = <&gpio3 23 0>;/*<&gpio3 23 GPIO_ACTIVE_HIGH>;*/
                status = "okay";

        };
.............................................................................................
};

然后配置pinctrl信息,如下图所示:

&iomuxc {

..................................................................................................
    pinctrl_ft5x06_int: ft5x06_int {
         fsl,pins = <
         /*MX8MM_IOMUXC_GPIO1_IO09_GPIO1_IO9               0x159*/
         MX8MM_IOMUXC_GPIO1_IO15_GPIO1_IO15              0x159
         MX8MM_IOMUXC_SAI5_RXD2_GPIO3_IO23               0x41
         >;
    };

..................................................................................................

接下来拷贝“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\3.Linux系统移植\8.移植内核需要用到的文件\01 适配屏幕需要使用的文件”目录下的edt-ft5x06.c文件拷贝到内核源码linux-imx/drivers/input/touchscreen目录下。 

72.6 编译驱动进内核

在上面小节配置完毕,保存修改,然后重新打开一个终端,输入以下命令,将默认的配置文件写入到.config文件。

make defconfig

然后输入以下命令进入menuconfig进行配置,如下所示:

export ARCH=arm64

make menuconfig

然后按如下图所示的路径,配置上屏幕,如下图所示:

     -> Device Drivers                                                   

         -> Input device support                                           

           -> Generic input layer (needed for keyboard, mouse, ...) (INPUT │

-> Touchscreens

 

最后保存配置文件到arch/arm64/configs/defconfig文件,如下图所示: 

72.7设定屏幕

如果要设置 7 mipi 屏幕,则进入 linux 源码路径下,编辑 itop8mm-evk.dts 设备树。

vim linux-imx/arch/arm64/boot/dts/freescale/itop8mm-evk.dts

#define LCD_TYPE_MIPI_7_0   1 取消注释,其他屏幕的宏定义加上注释,如下图所示:

 

如果要设置 7 lvds 屏幕,则进入 linux 源码路径下,编辑 itop8mm-evk.dts 设备树。

vim linux-imx/arch/arm64/boot/dts/freescale/itop8mm-evk.dts

#define LCD_TYPE_7_0    1 取消注释,其他屏幕的宏定义加上注释,如下图所示:

然后编辑 panel-tc358775.c

vim linux-imx/drivers/gpu/drm/panel/panel-tc358775.c

 

修改触摸文件 edt-ft5x06.c

vim linux-imx/drivers/input/touchscreen/edt-ft5x06.c

#define CONFIG_LVDS_7_0_800x1280    1 取消注释,其他屏幕的宏定义加上注释,如下图所示:

如果要设置 9.7 lvds 屏幕,则进入 linux 源码路径下,编辑 itop8mm-evk.dts 设备树。

vim linux-imx/arch/arm64/boot/dts/freescale/itop8mm-evk.dts

#define LCD_TYPE_9_7  1 取消注释,其他屏幕的宏定义加上注释,如下图所示

 

然后编辑 panel-tc358775.c。

vim linux-imx/drivers/gpu/drm/panel/panel-tc358775.c

#define LCD_TYPE_9_7   1 取消注释,其他屏幕的宏定义加上注释,如下图所示:

 

修改触摸文件 edt-ft5x06.c

vim linux-imx/drivers/input/touchscreen/edt-ft5x06.c

#define CONFIG_LVDS_9_7_1024x768    1 取消注释,其他屏幕的宏定义加上注释,如下图所示:

 

如果要设置 10.1 lvds 屏幕,则进入 linux 源码路径下,编辑 itop8mm-evk.dts 设备树。

vim linux-imx/arch/arm64/boot/dts/freescale/itop8mm-evk.dts

#define LCD_TYPE_10_1    1 取消注释,其他屏幕的宏定义加上注释,如下图所示:

 

然后编辑 panel-tc358775.c

vim linux-imx/drivers/gpu/drm/panel/panel-tc358775.c

#define LCD_TYPE_10_1    1 取消注释,其他屏幕的宏定义加上注释,如下图所示:

 

然后再回到之前的终端窗口进行编译镜像。编译之后烧写镜像,系统启动后如下图所示,作者连接的是mipi屏幕,触摸显示正常。如下图所示: 

输入以下命令查看触摸节点

cat /proc/bus/input/devices

然后输入以下命令,触摸屏幕会上报信息。

hexdump /dev/input/event1

 

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值