Message ID | 1395238975-24600-4-git-send-email-ajaykumar.rs@samsung.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 19 March 2014 19:52, Ajay Kumar <ajaykumar.rs@samsung.com> wrote: > Add post processor ops for IELCD and their support functions. > Expose an interface for the FIMD to register IELCD PP. [snip] > + > +#define exynos_ielcd_readl(addr) readl(ielcd->exynos_ielcd_base + addr) > +#define exynos_ielcd_writel(addr, val) \ nit: The order of arguments could be retained same as writel (i.e., val, addr) for ease of reading. > + writel(val, ielcd->exynos_ielcd_base + addr) > +#define IELCD_TIMEOUT_CNT 1000 > + > +struct ielcd_context { > + void __iomem *exynos_ielcd_base; > + unsigned vdisplay; > + unsigned vsync_len; > + unsigned vbpd; > + unsigned vfpd; > + unsigned hdisplay; > + unsigned hsync_len; > + unsigned hbpd; > + unsigned hfpd; > + unsigned vidcon0; > + unsigned vidcon1; > +}; > + > +static int ielcd_logic_start(struct ielcd_context *ielcd) > +{ > + exynos_ielcd_writel(IELCD_AUXCON, IELCD_MAGIC_KEY); > + > + return 0; > +} The return type could be made void. > + > +static int exynos_ielcd_hw_trigger_check(struct ielcd_context *ielcd) > +{ > + unsigned int cfg; > + unsigned int count = 0; > + > + cfg = exynos_ielcd_readl(IELCD_TRIGCON); > + cfg &= ~(SWTRGCMD_W0BUF | TRGMODE_W0BUF); > + cfg |= (SWTRGCMD_W0BUF | TRGMODE_W0BUF); > + exynos_ielcd_writel(IELCD_TRIGCON, cfg); > + > + do { > + cfg = exynos_ielcd_readl(IELCD_SHADOWCON); > + cfg |= IELCD_W0_SW_SHADOW_UPTRIG; > + exynos_ielcd_writel(IELCD_SHADOWCON, cfg); > + > + cfg = exynos_ielcd_readl(IELCD_VIDCON0); > + cfg |= IELCD_SW_SHADOW_UPTRIG; > + exynos_ielcd_writel(IELCD_VIDCON0, cfg); > + > + count++; > + if (count > IELCD_TIMEOUT_CNT) { The 2 lines could be combined as: if (++count > IELCD_TIMEOUT_CNT) { > + DRM_ERROR("ielcd start fail\n"); > + return -EBUSY; > + } > + udelay(10); > + } while (exynos_ielcd_readl(IELCD_WINCON0) & IELCD_TRGSTATUS); Also this check could be moved inside the loop and break if 0 whereas this could while (1) here. > + > + return 0; > +} > + > +static int exynos_ielcd_display_on(struct ielcd_context *ielcd) > +{ > + unsigned int cfg; > + > + cfg = exynos_ielcd_readl(IELCD_VIDCON0); > + cfg |= (VIDCON0_ENVID | VIDCON0_ENVID_F); > + exynos_ielcd_writel(IELCD_VIDCON0, cfg); > + > + return exynos_ielcd_hw_trigger_check(ielcd); > +} > + > +int exynos_ielcd_display_off(void *pp_ctx) > +{ > + struct ielcd_context *ielcd = pp_ctx; > + unsigned int cfg, ielcd_count = 0; > + int ret = 0; initialization not needed. > + > + cfg = exynos_ielcd_readl(IELCD_VIDCON0); > + cfg &= ~(VIDCON0_ENVID | VIDCON0_ENVID_F); > + > + exynos_ielcd_writel(IELCD_VIDCON0, cfg); > + > + do { > + if (++ielcd_count > IELCD_TIMEOUT_CNT) { > + DRM_ERROR("ielcd off TIMEDOUT\n"); > + ret = -ETIMEDOUT; > + break; > + } > + > + if (!(exynos_ielcd_readl(IELCD_VIDCON1) & > + VIDCON1_LINECNT_MASK)) { > + ret = 0; > + break; > + } > + udelay(200); > + } while (1); > + > + return ret; > +} > + > +static void exynos_ielcd_config_rgb(struct ielcd_context *ielcd) > +{ > + unsigned int cfg; > + > + cfg = exynos_ielcd_readl(IELCD_VIDCON0); > + cfg &= ~(VIDCON0_VIDOUT_MASK | VIDCON0_VCLK_MASK); > + cfg |= VIDCON0_VIDOUT_RGB; > + cfg |= VIDCON0_VCLK_NORMAL; > + > + exynos_ielcd_writel(IELCD_VIDCON0, cfg); > +} > + > +int exynos_ielcd_power_on(void *pp_ctx) > +{ > + struct ielcd_context *ielcd = pp_ctx; > + unsigned int cfg; > + int ret = 0; > + > + ielcd_logic_start(ielcd); > + ielcd_set_polarity(ielcd); > + > + ielcd_set_lcd_size(ielcd); > + ielcd_set_timing(ielcd); > + > + /* window0 setting , fixed */ > + cfg = WINCONx_ENLOCAL | WINCON0_BPPMODE_24BPP_888 | WINCONx_ENWIN; > + exynos_ielcd_writel(IELCD_WINCON0, cfg); > + > + exynos_ielcd_config_rgb(ielcd); > + > + ret = exynos_ielcd_display_on(ielcd); > + if (ret) { > + DRM_ERROR("IELCD failed to start\n"); > + return ret; > + } > + > + return 0; The above block could be simplified as: ret = exynos_ielcd_display_on(ielcd); if (ret) DRM_ERROR(...); return ret; > +} > + > +static void exynos_ielcd_mode_set(void *pp_ctx, > + const struct drm_display_mode *in_mode) > +{ > + struct ielcd_context *ielcd = pp_ctx; > + > + ielcd->vdisplay = in_mode->crtc_vdisplay; > + ielcd->vsync_len = in_mode->crtc_vsync_end - in_mode->crtc_vsync_start; > + ielcd->vbpd = in_mode->crtc_vtotal - in_mode->crtc_vsync_end; > + ielcd->vfpd = in_mode->crtc_vsync_start - in_mode->crtc_vdisplay; > + > + ielcd->hdisplay = in_mode->crtc_hdisplay; > + ielcd->hsync_len = in_mode->crtc_hsync_end - in_mode->crtc_hsync_start; > + ielcd->hbpd = in_mode->crtc_htotal - in_mode->crtc_hsync_end; > + ielcd->hfpd = in_mode->crtc_hsync_start - in_mode->crtc_hdisplay; > +} > + > +static struct exynos_fimd_pp_ops ielcd_ops = { > + .power_on = exynos_ielcd_power_on, > + .power_off = exynos_ielcd_display_off, > + .mode_set = exynos_ielcd_mode_set, > +}; > + > +static struct exynos_fimd_pp ielcd_pp = { > + .ops = &ielcd_ops, > +}; > + > +int exynos_ielcd_init(struct device *dev, struct exynos_fimd_pp **pp) > +{ > + struct device_node *ielcd_np; > + struct ielcd_context *ielcd; > + u32 addr[2]; > + int ret = 0; > + > + ielcd = kzalloc(sizeof(struct ielcd_context), GFP_KERNEL); > + if (!ielcd) { > + DRM_ERROR("failed to allocate ielcd\n"); > + ret = -ENOMEM; > + goto error0; return directly from here and delete the label.
Hi Sachin, On Fri, Mar 21, 2014 at 2:12 PM, Sachin Kamat <sachin.kamat@linaro.org>wrote: > On 19 March 2014 19:52, Ajay Kumar <ajaykumar.rs@samsung.com> wrote: > > Add post processor ops for IELCD and their support functions. > > Expose an interface for the FIMD to register IELCD PP. > [snip] > > + > > +#define exynos_ielcd_readl(addr) readl(ielcd->exynos_ielcd_base + > addr) > > +#define exynos_ielcd_writel(addr, val) \ > > nit: The order of arguments could be retained same as writel (i.e., > val, addr) for ease of > reading. > > Right. I will change it. > > + writel(val, ielcd->exynos_ielcd_base + > addr) > > > +#define IELCD_TIMEOUT_CNT 1000 > > + > > +struct ielcd_context { > > + void __iomem *exynos_ielcd_base; > > + unsigned vdisplay; > > + unsigned vsync_len; > > + unsigned vbpd; > > + unsigned vfpd; > > + unsigned hdisplay; > > + unsigned hsync_len; > > + unsigned hbpd; > > + unsigned hfpd; > > + unsigned vidcon0; > > + unsigned vidcon1; > > +}; > > + > > +static int ielcd_logic_start(struct ielcd_context *ielcd) > > +{ > > + exynos_ielcd_writel(IELCD_AUXCON, IELCD_MAGIC_KEY); > > + > > + return 0; > > +} > > The return type could be made void. > > Ok. > > + > > +static int exynos_ielcd_hw_trigger_check(struct ielcd_context *ielcd) > > +{ > > + unsigned int cfg; > > + unsigned int count = 0; > > + > > + cfg = exynos_ielcd_readl(IELCD_TRIGCON); > > + cfg &= ~(SWTRGCMD_W0BUF | TRGMODE_W0BUF); > > + cfg |= (SWTRGCMD_W0BUF | TRGMODE_W0BUF); > > + exynos_ielcd_writel(IELCD_TRIGCON, cfg); > > + > > + do { > > + cfg = exynos_ielcd_readl(IELCD_SHADOWCON); > > + cfg |= IELCD_W0_SW_SHADOW_UPTRIG; > > + exynos_ielcd_writel(IELCD_SHADOWCON, cfg); > > + > > + cfg = exynos_ielcd_readl(IELCD_VIDCON0); > > + cfg |= IELCD_SW_SHADOW_UPTRIG; > > + exynos_ielcd_writel(IELCD_VIDCON0, cfg); > > + > > + count++; > > + if (count > IELCD_TIMEOUT_CNT) { > > The 2 lines could be combined as: > if (++count > IELCD_TIMEOUT_CNT) { > > > + DRM_ERROR("ielcd start fail\n"); > > + return -EBUSY; > > + } > > + udelay(10); > > + } while (exynos_ielcd_readl(IELCD_WINCON0) & IELCD_TRGSTATUS); > > Also this check could be moved inside the loop and break if 0 whereas this > could > while (1) here. > > Ok. I will change the above loop. > > + > > + return 0; > > +} > > + > > +static int exynos_ielcd_display_on(struct ielcd_context *ielcd) > > +{ > > + unsigned int cfg; > > + > > + cfg = exynos_ielcd_readl(IELCD_VIDCON0); > > + cfg |= (VIDCON0_ENVID | VIDCON0_ENVID_F); > > + exynos_ielcd_writel(IELCD_VIDCON0, cfg); > > + > > + return exynos_ielcd_hw_trigger_check(ielcd); > > +} > > + > > +int exynos_ielcd_display_off(void *pp_ctx) > > +{ > > + struct ielcd_context *ielcd = pp_ctx; > > + unsigned int cfg, ielcd_count = 0; > > + int ret = 0; > > initialization not needed. Ok. > > + > > + cfg = exynos_ielcd_readl(IELCD_VIDCON0); > > + cfg &= ~(VIDCON0_ENVID | VIDCON0_ENVID_F); > > + > > + exynos_ielcd_writel(IELCD_VIDCON0, cfg); > > + > > + do { > > + if (++ielcd_count > IELCD_TIMEOUT_CNT) { > > + DRM_ERROR("ielcd off TIMEDOUT\n"); > > + ret = -ETIMEDOUT; > > + break; > > + } > > + > > + if (!(exynos_ielcd_readl(IELCD_VIDCON1) & > > + VIDCON1_LINECNT_MASK)) { > > + ret = 0; > > + break; > > + } > > + udelay(200); > > + } while (1); > > + > > + return ret; > > +} > > + > > +static void exynos_ielcd_config_rgb(struct ielcd_context *ielcd) > > +{ > > + unsigned int cfg; > > + > > + cfg = exynos_ielcd_readl(IELCD_VIDCON0); > > + cfg &= ~(VIDCON0_VIDOUT_MASK | VIDCON0_VCLK_MASK); > > + cfg |= VIDCON0_VIDOUT_RGB; > > + cfg |= VIDCON0_VCLK_NORMAL; > > + > > + exynos_ielcd_writel(IELCD_VIDCON0, cfg); > > +} > > + > > +int exynos_ielcd_power_on(void *pp_ctx) > > +{ > > + struct ielcd_context *ielcd = pp_ctx; > > + unsigned int cfg; > > + int ret = 0; > > + > > + ielcd_logic_start(ielcd); > > + ielcd_set_polarity(ielcd); > > + > > + ielcd_set_lcd_size(ielcd); > > + ielcd_set_timing(ielcd); > > + > > + /* window0 setting , fixed */ > > + cfg = WINCONx_ENLOCAL | WINCON0_BPPMODE_24BPP_888 | > WINCONx_ENWIN; > > + exynos_ielcd_writel(IELCD_WINCON0, cfg); > > + > > + exynos_ielcd_config_rgb(ielcd); > > + > > + ret = exynos_ielcd_display_on(ielcd); > > + if (ret) { > > + DRM_ERROR("IELCD failed to start\n"); > > + return ret; > > + } > > + > > + return 0; > > The above block could be simplified as: > ret = exynos_ielcd_display_on(ielcd); > if (ret) > DRM_ERROR(...); > > return ret; > Ok. I will change it. > > +} > > + > > +static void exynos_ielcd_mode_set(void *pp_ctx, > > + const struct drm_display_mode *in_mode) > > +{ > > + struct ielcd_context *ielcd = pp_ctx; > > + > > + ielcd->vdisplay = in_mode->crtc_vdisplay; > > + ielcd->vsync_len = in_mode->crtc_vsync_end - > in_mode->crtc_vsync_start; > > + ielcd->vbpd = in_mode->crtc_vtotal - in_mode->crtc_vsync_end; > > + ielcd->vfpd = in_mode->crtc_vsync_start - in_mode->crtc_vdisplay; > > + > > + ielcd->hdisplay = in_mode->crtc_hdisplay; > > + ielcd->hsync_len = in_mode->crtc_hsync_end - > in_mode->crtc_hsync_start; > > + ielcd->hbpd = in_mode->crtc_htotal - in_mode->crtc_hsync_end; > > + ielcd->hfpd = in_mode->crtc_hsync_start - in_mode->crtc_hdisplay; > > +} > > + > > +static struct exynos_fimd_pp_ops ielcd_ops = { > > + .power_on = exynos_ielcd_power_on, > > + .power_off = exynos_ielcd_display_off, > > + .mode_set = exynos_ielcd_mode_set, > > +}; > > + > > +static struct exynos_fimd_pp ielcd_pp = { > > + .ops = &ielcd_ops, > > +}; > > + > > +int exynos_ielcd_init(struct device *dev, struct exynos_fimd_pp **pp) > > +{ > > + struct device_node *ielcd_np; > > + struct ielcd_context *ielcd; > > + u32 addr[2]; > > + int ret = 0; > > + > > + ielcd = kzalloc(sizeof(struct ielcd_context), GFP_KERNEL); > > + if (!ielcd) { > > + DRM_ERROR("failed to allocate ielcd\n"); > > + ret = -ENOMEM; > > + goto error0; > > return directly from here and delete the label. > > Ok. > -- > With warm regards, > Sachin > Will address all your comments in next patchset. Thanks and regards, Ajay kumar
Hi, Thanks for the patch. On 03/19/2014 03:22 PM, Ajay Kumar wrote: > Add post processor ops for IELCD and their support functions. > Expose an interface for the FIMD to register IELCD PP. > > Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com> > Signed-off-by: Shirish S <s.shirish@samsung.com> > Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com> > --- > drivers/gpu/drm/exynos/Makefile | 3 +- > drivers/gpu/drm/exynos/exynos_ielcd.c | 295 ++++++++++++++++++++++++++++++++++ > include/video/samsung_fimd.h | 43 +++++ > 3 files changed, 340 insertions(+), 1 deletion(-) > create mode 100644 drivers/gpu/drm/exynos/exynos_ielcd.c > > diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile > index 653eab5..f3d7314 100644 > --- a/drivers/gpu/drm/exynos/Makefile > +++ b/drivers/gpu/drm/exynos/Makefile > @@ -10,7 +10,8 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o \ > > exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o > exynosdrm-$(CONFIG_DRM_EXYNOS_DMABUF) += exynos_drm_dmabuf.o > -exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o exynos_mdnie.o > +exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o exynos_mdnie.o \ > + exynos_ielcd.o Kconfig option would be nice. > exynosdrm-$(CONFIG_DRM_EXYNOS_DSI) += exynos_drm_dsi.o > exynosdrm-$(CONFIG_DRM_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o > exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o > diff --git a/drivers/gpu/drm/exynos/exynos_ielcd.c b/drivers/gpu/drm/exynos/exynos_ielcd.c > new file mode 100644 > index 0000000..33d0d34 > --- /dev/null > +++ b/drivers/gpu/drm/exynos/exynos_ielcd.c > @@ -0,0 +1,295 @@ > +/* drivers/gpu/drm/exynos/exynos_ielcd.c > + * > + * Samsung IELCD driver > + * > + * Copyright (C) 2014 Samsung Electronics Co., Ltd. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2 of the License, or (at your > + * option) any later version. > +*/ > + > +#include <linux/errno.h> > +#include <linux/of_device.h> > +#include <linux/delay.h> > + > +#include <video/samsung_fimd.h> > + > +#include <drm/drmP.h> > + > +#include "exynos_drm_drv.h" > +#include "exynos_fimd_pp.h" > + > +#define exynos_ielcd_readl(addr) readl(ielcd->exynos_ielcd_base + addr) > +#define exynos_ielcd_writel(addr, val) \ > + writel(val, ielcd->exynos_ielcd_base + addr) > +#define IELCD_TIMEOUT_CNT 1000 > + > +struct ielcd_context { > + void __iomem *exynos_ielcd_base; > + unsigned vdisplay; > + unsigned vsync_len; > + unsigned vbpd; > + unsigned vfpd; > + unsigned hdisplay; > + unsigned hsync_len; > + unsigned hbpd; > + unsigned hfpd; > + unsigned vidcon0; > + unsigned vidcon1; > +}; > + > +static int ielcd_logic_start(struct ielcd_context *ielcd) > +{ > + exynos_ielcd_writel(IELCD_AUXCON, IELCD_MAGIC_KEY); > + > + return 0; > +} > + > +static void ielcd_set_polarity(struct ielcd_context *ielcd) > +{ > + unsigned int cfg; > + > + cfg = exynos_ielcd_readl(IELCD_VIDCON1); > + > + cfg &= ~(VIDCON1_INV_VCLK | VIDCON1_INV_HSYNC > + | VIDCON1_INV_VSYNC | VIDCON1_INV_VDEN); > + cfg |= ielcd->vidcon1; > + exynos_ielcd_writel(IELCD_VIDCON1, cfg); Isn't it suppose to be configurable, read from drm_mode + DT properties. BTW how it works with the same settings in FIMD? FIMD settings are ignored or xor-ed ? > +} > + > +static void ielcd_set_timing(struct ielcd_context *ielcd) > +{ > + unsigned int cfg; > + > + /*LCD verical porch setting*/ > + cfg = VIDTCON0_VBPD(ielcd->vbpd - 1) | > + VIDTCON0_VFPD(ielcd->vfpd - 1) | > + VIDTCON0_VSPW(ielcd->vsync_len - 1); > + > + exynos_ielcd_writel(IELCD_VIDTCON0, cfg); > + /*LCD horizontal porch setting*/ > + cfg = VIDTCON1_HBPD(ielcd->hbpd - 1) | > + VIDTCON1_HFPD(ielcd->hfpd - 1) | > + VIDTCON1_HSPW(ielcd->hsync_len - 1); > + > + exynos_ielcd_writel(IELCD_VIDTCON1, cfg); > +} > + > +static void ielcd_set_lcd_size(struct ielcd_context *ielcd) > +{ > + unsigned int cfg; > + > + cfg = (IELCD_VIDTCON2_LINEVAL(ielcd->vdisplay - 1) | > + IELCD_VIDTCON2_HOZVAL(ielcd->hdisplay - 1)); > + exynos_ielcd_writel(IELCD_VIDTCON2, cfg); > + > + /* window0 position setting */ > + exynos_ielcd_writel(IELCD_VIDOSD0A, 0); > + cfg = (IELCD_VIDOSDB_XPOS_END(ielcd->hdisplay - 1)) | > + (IELCD_VIDOSDB_YPOS_END(ielcd->vdisplay - 1)); > + exynos_ielcd_writel(IELCD_VIDOSD0B, cfg); > + > + /* window0 osd size setting */ > + exynos_ielcd_writel(IELCD_VIDOSD0C, ielcd->hdisplay * > + ielcd->vdisplay); > + > + /* window0 page size(bytes) */ > + exynos_ielcd_writel(IELCD_VIDW00ADD2, ielcd->hdisplay * 4); > +} > + > +static int exynos_ielcd_hw_trigger_check(struct ielcd_context *ielcd) > +{ > + unsigned int cfg; > + unsigned int count = 0; > + > + cfg = exynos_ielcd_readl(IELCD_TRIGCON); > + cfg &= ~(SWTRGCMD_W0BUF | TRGMODE_W0BUF); > + cfg |= (SWTRGCMD_W0BUF | TRGMODE_W0BUF); One of two lines above should be removed. > + exynos_ielcd_writel(IELCD_TRIGCON, cfg); > + > + do { > + cfg = exynos_ielcd_readl(IELCD_SHADOWCON); > + cfg |= IELCD_W0_SW_SHADOW_UPTRIG; > + exynos_ielcd_writel(IELCD_SHADOWCON, cfg); > + > + cfg = exynos_ielcd_readl(IELCD_VIDCON0); > + cfg |= IELCD_SW_SHADOW_UPTRIG; > + exynos_ielcd_writel(IELCD_VIDCON0, cfg); > + > + count++; > + if (count > IELCD_TIMEOUT_CNT) { > + DRM_ERROR("ielcd start fail\n"); > + return -EBUSY; > + } > + udelay(10); > + } while (exynos_ielcd_readl(IELCD_WINCON0) & IELCD_TRGSTATUS); > + > + return 0; > +} > + > +static int exynos_ielcd_display_on(struct ielcd_context *ielcd) > +{ > + unsigned int cfg; > + > + cfg = exynos_ielcd_readl(IELCD_VIDCON0); > + cfg |= (VIDCON0_ENVID | VIDCON0_ENVID_F); > + exynos_ielcd_writel(IELCD_VIDCON0, cfg); > + > + return exynos_ielcd_hw_trigger_check(ielcd); > +} > + > +int exynos_ielcd_display_off(void *pp_ctx) > +{ > + struct ielcd_context *ielcd = pp_ctx; > + unsigned int cfg, ielcd_count = 0; > + int ret = 0; > + > + cfg = exynos_ielcd_readl(IELCD_VIDCON0); > + cfg &= ~(VIDCON0_ENVID | VIDCON0_ENVID_F); > + > + exynos_ielcd_writel(IELCD_VIDCON0, cfg); > + > + do { > + if (++ielcd_count > IELCD_TIMEOUT_CNT) { > + DRM_ERROR("ielcd off TIMEDOUT\n"); > + ret = -ETIMEDOUT; > + break; > + } > + > + if (!(exynos_ielcd_readl(IELCD_VIDCON1) & > + VIDCON1_LINECNT_MASK)) { > + ret = 0; > + break; > + } > + udelay(200); > + } while (1); > + > + return ret; > +} > + > +static void exynos_ielcd_config_rgb(struct ielcd_context *ielcd) > +{ > + unsigned int cfg; > + > + cfg = exynos_ielcd_readl(IELCD_VIDCON0); > + cfg &= ~(VIDCON0_VIDOUT_MASK | VIDCON0_VCLK_MASK); > + cfg |= VIDCON0_VIDOUT_RGB; > + cfg |= VIDCON0_VCLK_NORMAL; > + > + exynos_ielcd_writel(IELCD_VIDCON0, cfg); > +} I guess it should depend on device connected to the output. > + > +int exynos_ielcd_power_on(void *pp_ctx) > +{ > + struct ielcd_context *ielcd = pp_ctx; > + unsigned int cfg; > + int ret = 0; > + > + ielcd_logic_start(ielcd); > + ielcd_set_polarity(ielcd); > + > + ielcd_set_lcd_size(ielcd); > + ielcd_set_timing(ielcd); > + > + /* window0 setting , fixed */ > + cfg = WINCONx_ENLOCAL | WINCON0_BPPMODE_24BPP_888 | WINCONx_ENWIN; > + exynos_ielcd_writel(IELCD_WINCON0, cfg); Ditto > + > + exynos_ielcd_config_rgb(ielcd); > + > + ret = exynos_ielcd_display_on(ielcd); > + if (ret) { > + DRM_ERROR("IELCD failed to start\n"); > + return ret; > + } > + > + return 0; > +} > + > +static void exynos_ielcd_mode_set(void *pp_ctx, > + const struct drm_display_mode *in_mode) > +{ > + struct ielcd_context *ielcd = pp_ctx; > + > + ielcd->vdisplay = in_mode->crtc_vdisplay; > + ielcd->vsync_len = in_mode->crtc_vsync_end - in_mode->crtc_vsync_start; > + ielcd->vbpd = in_mode->crtc_vtotal - in_mode->crtc_vsync_end; > + ielcd->vfpd = in_mode->crtc_vsync_start - in_mode->crtc_vdisplay; > + > + ielcd->hdisplay = in_mode->crtc_hdisplay; > + ielcd->hsync_len = in_mode->crtc_hsync_end - in_mode->crtc_hsync_start; > + ielcd->hbpd = in_mode->crtc_htotal - in_mode->crtc_hsync_end; > + ielcd->hfpd = in_mode->crtc_hsync_start - in_mode->crtc_hdisplay; > +} > + > +static struct exynos_fimd_pp_ops ielcd_ops = { > + .power_on = exynos_ielcd_power_on, > + .power_off = exynos_ielcd_display_off, > + .mode_set = exynos_ielcd_mode_set, > +}; > + > +static struct exynos_fimd_pp ielcd_pp = { > + .ops = &ielcd_ops, > +}; > + > +int exynos_ielcd_init(struct device *dev, struct exynos_fimd_pp **pp) > +{ > + struct device_node *ielcd_np; > + struct ielcd_context *ielcd; > + u32 addr[2]; > + int ret = 0; > + > + ielcd = kzalloc(sizeof(struct ielcd_context), GFP_KERNEL); > + if (!ielcd) { > + DRM_ERROR("failed to allocate ielcd\n"); > + ret = -ENOMEM; > + goto error0; > + } > + > + ielcd_np = of_parse_phandle(dev->of_node, "samsung,ielcd", 0); > + if (!ielcd_np) { > + DRM_ERROR("No ielcd node present, " > + "MDNIE feature will be disabled\n"); > + ret = -ENODEV; > + goto error1; > + } > + > + if (of_property_read_u32_array(ielcd_np, "reg", addr, 2)) { > + DRM_ERROR("failed to get base address for IELCD\n"); > + ret = -ENOMEM; > + goto error1; > + } > + > + ielcd->exynos_ielcd_base = ioremap(addr[0], addr[1]); > + if (!ielcd->exynos_ielcd_base) { > + DRM_ERROR("failed to ioremap ielcd device\n"); > + ret = -ENOMEM; > + goto error1; > + } > + > + if (of_get_property(dev->of_node, "samsung,fimd-vidout-rgb", NULL)) > + ielcd->vidcon0 |= VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB; > + if (of_get_property(dev->of_node, "samsung,fimd-inv-hsync", NULL)) > + ielcd->vidcon1 |= VIDCON1_INV_HSYNC; > + if (of_get_property(dev->of_node, "samsung,fimd-inv-vsync", NULL)) > + ielcd->vidcon1 |= VIDCON1_INV_VSYNC; > + if (of_get_property(dev->of_node, "samsung,fimd-inv-vclk", NULL)) > + ielcd->vidcon1 |= VIDCON1_INV_VCLK; > + if (of_get_property(dev->of_node, "samsung,fimd-inv-vden", NULL)) > + ielcd->vidcon1 |= VIDCON1_INV_VDEN; vidcon0, vidcon1 are not used. > + > + ielcd_pp.ctx = ielcd; > + > + *pp = &ielcd_pp; > + > + DRM_INFO("IELCD initialzation done\n"); > + > + return 0; > +error1: > + kfree(ielcd); > +error0: > + return ret; of_node_put on ielcd_np missing. iounmap missing. No remove routine at all, this is also true for mDNIe. > +} Again, platform_device fits better here, IMHO. > +EXPORT_SYMBOL(exynos_ielcd_init); > diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h > index 3ff7cad..cc64757 100644 > --- a/include/video/samsung_fimd.h > +++ b/include/video/samsung_fimd.h > @@ -60,7 +60,9 @@ > #define VIDCON0_CLKVAL_F_SHIFT 6 > #define VIDCON0_CLKVAL_F_LIMIT 0xff > #define VIDCON0_CLKVAL_F(_x) ((_x) << 6) > +#define VIDCON0_VCLK_MASK (1 << 5) > #define VIDCON0_VCLKFREE (1 << 5) > +#define VIDCON0_VCLK_NORMAL (0 << 5) > #define VIDCON0_CLKDIR (1 << 4) > > #define VIDCON0_CLKSEL_MASK (0x3 << 2) > @@ -466,3 +468,44 @@ > #define FIMD_V8_VIDTCON2 0x20018 > #define FIMD_V8_VIDTCON3 0x2001C > #define FIMD_V8_VIDCON1 0x20004 > + > +/* IELCD Register Offsets */ > +#define IELCD_VIDCON0 (0x0000) > +#define IELCD_VIDCON1 (0x0004) > + > +#define IELCD_VIDTCON0 (0x0010) > +#define IELCD_VIDTCON1 (0x0014) > +#define IELCD_VIDTCON2 (0x0018) > + > +#define IELCD_WINCON0 (0x0020) > +#define IELCD_TRGSTATUS (1 << 25) > + > +#define IELCD_SHADOWCON (0x0034) > + > +#define IELCD_VIDOSD0A (0x0040) > +#define IELCD_VIDOSD0B (0x0044) > +#define IELCD_VIDOSD0C (0x0048) > +#define IELCD_VIDW00ADD2 (0x0100) > + > +#define IELCD_TRIGCON (0x01A4) > +#define IELCD_AUXCON (0x0278) > + > +/* Value */ > +#define IELCD_MAGIC_KEY (0x2ff47) > + > +/* Register bit */ > +#define IELCD_VIDTCON2_LINEVAL(_x) ((_x) << 12) > +#define IELCD_VIDTCON2_HOZVAL(_x) ((_x) << 0) > + > +/* IELCD_VIDCON0 */ > +#define IELCD_SW_SHADOW_UPTRIG (1 << 14) > + > +/* IELCD_SHADOWCON */ > +#define IELCD_W0_SW_SHADOW_UPTRIG (1 << 16) > + > +/* IELCD_IELCD_VIDOSD */ > +#define IELCD_VIDOSDB_XPOS_END(_x) ((_x) << 11) > +#define IELCD_VIDOSDB_YPOS_END(_x) ((_x) << 0) > + > +#define SWTRGCMD_W0BUF (1 << 6) > +#define TRGMODE_W0BUF (1 << 5) > On which Exynos SoCs it was tested? What about compatibility with other SoCs? Regards Andrzej
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 653eab5..f3d7314 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -10,7 +10,8 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o \ exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o exynosdrm-$(CONFIG_DRM_EXYNOS_DMABUF) += exynos_drm_dmabuf.o -exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o exynos_mdnie.o +exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o exynos_mdnie.o \ + exynos_ielcd.o exynosdrm-$(CONFIG_DRM_EXYNOS_DSI) += exynos_drm_dsi.o exynosdrm-$(CONFIG_DRM_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o diff --git a/drivers/gpu/drm/exynos/exynos_ielcd.c b/drivers/gpu/drm/exynos/exynos_ielcd.c new file mode 100644 index 0000000..33d0d34 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_ielcd.c @@ -0,0 +1,295 @@ +/* drivers/gpu/drm/exynos/exynos_ielcd.c + * + * Samsung IELCD driver + * + * Copyright (C) 2014 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. +*/ + +#include <linux/errno.h> +#include <linux/of_device.h> +#include <linux/delay.h> + +#include <video/samsung_fimd.h> + +#include <drm/drmP.h> + +#include "exynos_drm_drv.h" +#include "exynos_fimd_pp.h" + +#define exynos_ielcd_readl(addr) readl(ielcd->exynos_ielcd_base + addr) +#define exynos_ielcd_writel(addr, val) \ + writel(val, ielcd->exynos_ielcd_base + addr) +#define IELCD_TIMEOUT_CNT 1000 + +struct ielcd_context { + void __iomem *exynos_ielcd_base; + unsigned vdisplay; + unsigned vsync_len; + unsigned vbpd; + unsigned vfpd; + unsigned hdisplay; + unsigned hsync_len; + unsigned hbpd; + unsigned hfpd; + unsigned vidcon0; + unsigned vidcon1; +}; + +static int ielcd_logic_start(struct ielcd_context *ielcd) +{ + exynos_ielcd_writel(IELCD_AUXCON, IELCD_MAGIC_KEY); + + return 0; +} + +static void ielcd_set_polarity(struct ielcd_context *ielcd) +{ + unsigned int cfg; + + cfg = exynos_ielcd_readl(IELCD_VIDCON1); + + cfg &= ~(VIDCON1_INV_VCLK | VIDCON1_INV_HSYNC + | VIDCON1_INV_VSYNC | VIDCON1_INV_VDEN); + cfg |= ielcd->vidcon1; + exynos_ielcd_writel(IELCD_VIDCON1, cfg); +} + +static void ielcd_set_timing(struct ielcd_context *ielcd) +{ + unsigned int cfg; + + /*LCD verical porch setting*/ + cfg = VIDTCON0_VBPD(ielcd->vbpd - 1) | + VIDTCON0_VFPD(ielcd->vfpd - 1) | + VIDTCON0_VSPW(ielcd->vsync_len - 1); + + exynos_ielcd_writel(IELCD_VIDTCON0, cfg); + /*LCD horizontal porch setting*/ + cfg = VIDTCON1_HBPD(ielcd->hbpd - 1) | + VIDTCON1_HFPD(ielcd->hfpd - 1) | + VIDTCON1_HSPW(ielcd->hsync_len - 1); + + exynos_ielcd_writel(IELCD_VIDTCON1, cfg); +} + +static void ielcd_set_lcd_size(struct ielcd_context *ielcd) +{ + unsigned int cfg; + + cfg = (IELCD_VIDTCON2_LINEVAL(ielcd->vdisplay - 1) | + IELCD_VIDTCON2_HOZVAL(ielcd->hdisplay - 1)); + exynos_ielcd_writel(IELCD_VIDTCON2, cfg); + + /* window0 position setting */ + exynos_ielcd_writel(IELCD_VIDOSD0A, 0); + cfg = (IELCD_VIDOSDB_XPOS_END(ielcd->hdisplay - 1)) | + (IELCD_VIDOSDB_YPOS_END(ielcd->vdisplay - 1)); + exynos_ielcd_writel(IELCD_VIDOSD0B, cfg); + + /* window0 osd size setting */ + exynos_ielcd_writel(IELCD_VIDOSD0C, ielcd->hdisplay * + ielcd->vdisplay); + + /* window0 page size(bytes) */ + exynos_ielcd_writel(IELCD_VIDW00ADD2, ielcd->hdisplay * 4); +} + +static int exynos_ielcd_hw_trigger_check(struct ielcd_context *ielcd) +{ + unsigned int cfg; + unsigned int count = 0; + + cfg = exynos_ielcd_readl(IELCD_TRIGCON); + cfg &= ~(SWTRGCMD_W0BUF | TRGMODE_W0BUF); + cfg |= (SWTRGCMD_W0BUF | TRGMODE_W0BUF); + exynos_ielcd_writel(IELCD_TRIGCON, cfg); + + do { + cfg = exynos_ielcd_readl(IELCD_SHADOWCON); + cfg |= IELCD_W0_SW_SHADOW_UPTRIG; + exynos_ielcd_writel(IELCD_SHADOWCON, cfg); + + cfg = exynos_ielcd_readl(IELCD_VIDCON0); + cfg |= IELCD_SW_SHADOW_UPTRIG; + exynos_ielcd_writel(IELCD_VIDCON0, cfg); + + count++; + if (count > IELCD_TIMEOUT_CNT) { + DRM_ERROR("ielcd start fail\n"); + return -EBUSY; + } + udelay(10); + } while (exynos_ielcd_readl(IELCD_WINCON0) & IELCD_TRGSTATUS); + + return 0; +} + +static int exynos_ielcd_display_on(struct ielcd_context *ielcd) +{ + unsigned int cfg; + + cfg = exynos_ielcd_readl(IELCD_VIDCON0); + cfg |= (VIDCON0_ENVID | VIDCON0_ENVID_F); + exynos_ielcd_writel(IELCD_VIDCON0, cfg); + + return exynos_ielcd_hw_trigger_check(ielcd); +} + +int exynos_ielcd_display_off(void *pp_ctx) +{ + struct ielcd_context *ielcd = pp_ctx; + unsigned int cfg, ielcd_count = 0; + int ret = 0; + + cfg = exynos_ielcd_readl(IELCD_VIDCON0); + cfg &= ~(VIDCON0_ENVID | VIDCON0_ENVID_F); + + exynos_ielcd_writel(IELCD_VIDCON0, cfg); + + do { + if (++ielcd_count > IELCD_TIMEOUT_CNT) { + DRM_ERROR("ielcd off TIMEDOUT\n"); + ret = -ETIMEDOUT; + break; + } + + if (!(exynos_ielcd_readl(IELCD_VIDCON1) & + VIDCON1_LINECNT_MASK)) { + ret = 0; + break; + } + udelay(200); + } while (1); + + return ret; +} + +static void exynos_ielcd_config_rgb(struct ielcd_context *ielcd) +{ + unsigned int cfg; + + cfg = exynos_ielcd_readl(IELCD_VIDCON0); + cfg &= ~(VIDCON0_VIDOUT_MASK | VIDCON0_VCLK_MASK); + cfg |= VIDCON0_VIDOUT_RGB; + cfg |= VIDCON0_VCLK_NORMAL; + + exynos_ielcd_writel(IELCD_VIDCON0, cfg); +} + +int exynos_ielcd_power_on(void *pp_ctx) +{ + struct ielcd_context *ielcd = pp_ctx; + unsigned int cfg; + int ret = 0; + + ielcd_logic_start(ielcd); + ielcd_set_polarity(ielcd); + + ielcd_set_lcd_size(ielcd); + ielcd_set_timing(ielcd); + + /* window0 setting , fixed */ + cfg = WINCONx_ENLOCAL | WINCON0_BPPMODE_24BPP_888 | WINCONx_ENWIN; + exynos_ielcd_writel(IELCD_WINCON0, cfg); + + exynos_ielcd_config_rgb(ielcd); + + ret = exynos_ielcd_display_on(ielcd); + if (ret) { + DRM_ERROR("IELCD failed to start\n"); + return ret; + } + + return 0; +} + +static void exynos_ielcd_mode_set(void *pp_ctx, + const struct drm_display_mode *in_mode) +{ + struct ielcd_context *ielcd = pp_ctx; + + ielcd->vdisplay = in_mode->crtc_vdisplay; + ielcd->vsync_len = in_mode->crtc_vsync_end - in_mode->crtc_vsync_start; + ielcd->vbpd = in_mode->crtc_vtotal - in_mode->crtc_vsync_end; + ielcd->vfpd = in_mode->crtc_vsync_start - in_mode->crtc_vdisplay; + + ielcd->hdisplay = in_mode->crtc_hdisplay; + ielcd->hsync_len = in_mode->crtc_hsync_end - in_mode->crtc_hsync_start; + ielcd->hbpd = in_mode->crtc_htotal - in_mode->crtc_hsync_end; + ielcd->hfpd = in_mode->crtc_hsync_start - in_mode->crtc_hdisplay; +} + +static struct exynos_fimd_pp_ops ielcd_ops = { + .power_on = exynos_ielcd_power_on, + .power_off = exynos_ielcd_display_off, + .mode_set = exynos_ielcd_mode_set, +}; + +static struct exynos_fimd_pp ielcd_pp = { + .ops = &ielcd_ops, +}; + +int exynos_ielcd_init(struct device *dev, struct exynos_fimd_pp **pp) +{ + struct device_node *ielcd_np; + struct ielcd_context *ielcd; + u32 addr[2]; + int ret = 0; + + ielcd = kzalloc(sizeof(struct ielcd_context), GFP_KERNEL); + if (!ielcd) { + DRM_ERROR("failed to allocate ielcd\n"); + ret = -ENOMEM; + goto error0; + } + + ielcd_np = of_parse_phandle(dev->of_node, "samsung,ielcd", 0); + if (!ielcd_np) { + DRM_ERROR("No ielcd node present, " + "MDNIE feature will be disabled\n"); + ret = -ENODEV; + goto error1; + } + + if (of_property_read_u32_array(ielcd_np, "reg", addr, 2)) { + DRM_ERROR("failed to get base address for IELCD\n"); + ret = -ENOMEM; + goto error1; + } + + ielcd->exynos_ielcd_base = ioremap(addr[0], addr[1]); + if (!ielcd->exynos_ielcd_base) { + DRM_ERROR("failed to ioremap ielcd device\n"); + ret = -ENOMEM; + goto error1; + } + + if (of_get_property(dev->of_node, "samsung,fimd-vidout-rgb", NULL)) + ielcd->vidcon0 |= VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB; + if (of_get_property(dev->of_node, "samsung,fimd-inv-hsync", NULL)) + ielcd->vidcon1 |= VIDCON1_INV_HSYNC; + if (of_get_property(dev->of_node, "samsung,fimd-inv-vsync", NULL)) + ielcd->vidcon1 |= VIDCON1_INV_VSYNC; + if (of_get_property(dev->of_node, "samsung,fimd-inv-vclk", NULL)) + ielcd->vidcon1 |= VIDCON1_INV_VCLK; + if (of_get_property(dev->of_node, "samsung,fimd-inv-vden", NULL)) + ielcd->vidcon1 |= VIDCON1_INV_VDEN; + + ielcd_pp.ctx = ielcd; + + *pp = &ielcd_pp; + + DRM_INFO("IELCD initialzation done\n"); + + return 0; +error1: + kfree(ielcd); +error0: + return ret; +} +EXPORT_SYMBOL(exynos_ielcd_init); diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h index 3ff7cad..cc64757 100644 --- a/include/video/samsung_fimd.h +++ b/include/video/samsung_fimd.h @@ -60,7 +60,9 @@ #define VIDCON0_CLKVAL_F_SHIFT 6 #define VIDCON0_CLKVAL_F_LIMIT 0xff #define VIDCON0_CLKVAL_F(_x) ((_x) << 6) +#define VIDCON0_VCLK_MASK (1 << 5) #define VIDCON0_VCLKFREE (1 << 5) +#define VIDCON0_VCLK_NORMAL (0 << 5) #define VIDCON0_CLKDIR (1 << 4) #define VIDCON0_CLKSEL_MASK (0x3 << 2) @@ -466,3 +468,44 @@ #define FIMD_V8_VIDTCON2 0x20018 #define FIMD_V8_VIDTCON3 0x2001C #define FIMD_V8_VIDCON1 0x20004 + +/* IELCD Register Offsets */ +#define IELCD_VIDCON0 (0x0000) +#define IELCD_VIDCON1 (0x0004) + +#define IELCD_VIDTCON0 (0x0010) +#define IELCD_VIDTCON1 (0x0014) +#define IELCD_VIDTCON2 (0x0018) + +#define IELCD_WINCON0 (0x0020) +#define IELCD_TRGSTATUS (1 << 25) + +#define IELCD_SHADOWCON (0x0034) + +#define IELCD_VIDOSD0A (0x0040) +#define IELCD_VIDOSD0B (0x0044) +#define IELCD_VIDOSD0C (0x0048) +#define IELCD_VIDW00ADD2 (0x0100) + +#define IELCD_TRIGCON (0x01A4) +#define IELCD_AUXCON (0x0278) + +/* Value */ +#define IELCD_MAGIC_KEY (0x2ff47) + +/* Register bit */ +#define IELCD_VIDTCON2_LINEVAL(_x) ((_x) << 12) +#define IELCD_VIDTCON2_HOZVAL(_x) ((_x) << 0) + +/* IELCD_VIDCON0 */ +#define IELCD_SW_SHADOW_UPTRIG (1 << 14) + +/* IELCD_SHADOWCON */ +#define IELCD_W0_SW_SHADOW_UPTRIG (1 << 16) + +/* IELCD_IELCD_VIDOSD */ +#define IELCD_VIDOSDB_XPOS_END(_x) ((_x) << 11) +#define IELCD_VIDOSDB_YPOS_END(_x) ((_x) << 0) + +#define SWTRGCMD_W0BUF (1 << 6) +#define TRGMODE_W0BUF (1 << 5)