This patch series includes the followings: - FIMD I80 interface - DSI command mode interface for Exynos5420 SoC - S6E3FA0 command mode type panel driver
This patch series is based on exynos-drm-next branch.
Previous patch set, RFC v1: http://www.spinics.net/lists/dri-devel/msg57513.html RFC v2: http://www.spinics.net/lists/dri-devel/msg57945.html
Changelog v2: - Moves panel delays and size DT properties to panel probe routine. - Moves CPU timings relevant DT properties from FIMD to panel DT.
Changelog v3: - Splits core and driver patchset. - Renames CPU timings to CPU mode timings. - Enhances panel code readability.
Thank you. Best regards YJ
YoungJun Cho (16): drm/exynos: dsi: move the Eot packets configuration point drm/exynos: use wait_event_timeout() for safety usage ARM: dts: sysreg: add exynos5 compatible to DT bindings ARM: dts: samsung-fimd: add I80 specific properties drm/panel: add CPU mode timings structure drm/exynos: add TE handler to support MIPI DSI command mode interface drm/exynos: dsi: add TE handler to support command mode interface drm/exynos: fimd: support I80 interface ARM: dts: exynos_dsim: add exynos5420 compatible to DT bindings drm/exynos: dsi: add driver data to support Exynos5420 ARM: dts: s6e3fa0: add DT bindings drm/panel: add S6E3FA0 driver ARM: dts: exynos4: add system register node ARM: dts: exynos5: add system register support ARM: dts: exynos5420: add mipi-phy node ARM: dts: exynos5420: add dsi node
.../devicetree/bindings/arm/samsung/sysreg.txt | 1 + .../devicetree/bindings/panel/samsung,s6e3fa0.txt | 68 +++ .../devicetree/bindings/video/exynos_dsim.txt | 4 +- .../devicetree/bindings/video/samsung-fimd.txt | 2 + arch/arm/boot/dts/exynos4.dtsi | 1 + arch/arm/boot/dts/exynos5.dtsi | 6 + arch/arm/boot/dts/exynos5420.dtsi | 21 + drivers/gpu/drm/exynos/Kconfig | 1 + drivers/gpu/drm/exynos/exynos_drm_crtc.c | 16 +- drivers/gpu/drm/exynos/exynos_drm_crtc.h | 7 + drivers/gpu/drm/exynos/exynos_drm_drv.h | 3 + drivers/gpu/drm/exynos/exynos_drm_dsi.c | 172 +++++- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 280 +++++++-- drivers/gpu/drm/panel/Kconfig | 7 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-s6e3fa0.c | 595 ++++++++++++++++++++ include/drm/drm_mipi_dsi.h | 8 + include/drm/drm_panel.h | 18 + include/video/samsung_fimd.h | 3 +- 19 files changed, 1143 insertions(+), 71 deletions(-) create mode 100644 Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt create mode 100644 drivers/gpu/drm/panel/panel-s6e3fa0.c
This configuration could be used in MIPI DSI command mode also.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index eb73e3b..956e5f3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -472,8 +472,6 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH)) reg |= DSIM_MFLUSH_VS; - if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET)) - reg |= DSIM_EOT_DISABLE; if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) reg |= DSIM_SYNC_INFORM; if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) @@ -490,6 +488,9 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi) reg |= DSIM_HSA_MODE; }
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET)) + reg |= DSIM_EOT_DISABLE; + switch (dsi->format) { case MIPI_DSI_FMT_RGB888: reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
There could be the case that the page flip operation isn't finished correctly with some abnormal condition such as panel reset. So this patch replaces wait_event() with wait_event_timeout() to avoid waiting for page flip completion infinitely.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index e930d4f..1419d11 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -69,8 +69,9 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
if (mode > DRM_MODE_DPMS_ON) { /* wait for the completion of page flip. */ - wait_event(exynos_crtc->pending_flip_queue, - atomic_read(&exynos_crtc->pending_flip) == 0); + wait_event_timeout(exynos_crtc->pending_flip_queue, + !atomic_read(&exynos_crtc->pending_flip), + HZ/20); drm_vblank_off(crtc->dev, exynos_crtc->pipe); }
This patch adds relevant to exynos5 compatible for exynos5 SoCs.
Changelog v2: - Changes title and description (commented by Sachin Kamat)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/arm/samsung/sysreg.txt | 1 + 1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/arm/samsung/sysreg.txt b/Documentation/devicetree/bindings/arm/samsung/sysreg.txt index 0ab3251..fd71581 100644 --- a/Documentation/devicetree/bindings/arm/samsung/sysreg.txt +++ b/Documentation/devicetree/bindings/arm/samsung/sysreg.txt @@ -3,6 +3,7 @@ SAMSUNG S5P/Exynos SoC series System Registers (SYSREG) Properties: - compatible : should contain "samsung,<chip name>-sysreg", "syscon"; For Exynos4 SoC series it should be "samsung,exynos4-sysreg", "syscon"; + For Exynos5 SoC series it should be "samsung,exynos5-sysreg", "syscon"; - reg : offset and length of the register set.
Example:
In case of using CPU interface panel, the relevant registers should be set. So this patch adds relevant dt bindings.
Changelog v2: - Changes "samsung,sysreg-phandle" to "samsung,sysreg" Changelog v3: - Moves CPU timings relevant properties to panel DT (commented by Laurent Pinchart, Andrzej Hajda)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/video/samsung-fimd.txt | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt index 2dad41b..6bf93e9 100644 --- a/Documentation/devicetree/bindings/video/samsung-fimd.txt +++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt @@ -44,6 +44,8 @@ Optional Properties: - display-timings: timing settings for FIMD, as described in document [1]. Can be used in case timings cannot be provided otherwise or to override timings provided by the panel. +- samsung,sysreg: handle to syscon used to control the system registers +- vidout-i80-ldi: boolean to support i80 interface instead of rgb one
The device node can contain 'port' child nodes according to the bindings defined in [2]. The following are properties specific to those nodes:
To support MIPI DSI command mode interface, the display controller requires the CPU mode timings which is the panel specific data.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- include/drm/drm_panel.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index c2ab77a..67e6871 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -46,6 +46,24 @@ struct drm_panel { struct list_head list; };
+/** + * struct drm_panel_cpu_mode_timings - describe cpu mode timing settings + * @cs_setup: clock cycles for the active period of address signal is enabled + * until chip select is enabled in CPU mode interface + * @wr_setup: clock cycles for the active period of CS signal is enabled until + * write signal is enabled in CPU mode interface + * @wr_actve: clock cycles for the active period of CS is enabled in CPU mode + * interface + * @wr_hold: clock cycles for the active period of CS is disabled until write + * signal is disabled in CPU mode interface + */ +struct drm_panel_cpu_mode_timings { + unsigned int cs_setup; + unsigned int wr_setup; + unsigned int wr_active; + unsigned int wr_hold; +}; + static inline int drm_panel_disable(struct drm_panel *panel) { if (panel && panel->funcs && panel->funcs->disable)
To support MIPI DSI command mode interface, the panel should generates Tearing Effect synchronization signal between MCU and FB to display video images. And the display controller should trigger to transfer video image at this signal. So the panel receives the TE IRQ, then calls this handler chains to notify it to the display controller.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 11 +++++++++++ drivers/gpu/drm/exynos/exynos_drm_crtc.h | 7 +++++++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 3 +++ include/drm/drm_mipi_dsi.h | 8 ++++++++ 4 files changed, 29 insertions(+)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 1419d11..d902d64 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -491,3 +491,14 @@ void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb) manager->ops->wait_for_vblank(manager); } } + +int exynos_drm_crtc_te_handler(struct drm_crtc *crtc) +{ + struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager; + int ret = 0; + + if (manager->ops->te_handler) + ret = manager->ops->te_handler(manager); + + return ret; +} diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index c27b66c..eb78c16 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -32,4 +32,11 @@ void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos); void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos); void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
+/* + * This function calls the crtc device(manager)'s te_handler() callback + * to trigger to transfer video image at the tearing effect synchronization + * signal. + */ +int exynos_drm_crtc_te_handler(struct drm_crtc *crtc); + #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 4c5cf68..4d8865d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -186,6 +186,8 @@ struct exynos_drm_display { * @win_commit: apply hardware specific overlay data to registers. * @win_enable: enable hardware specific overlay. * @win_disable: disable hardware specific overlay. + * @te_handler: trigger to transfer video image at the tearing effect + * synchronization signal if there is a page flip request. */ struct exynos_drm_manager; struct exynos_drm_manager_ops { @@ -207,6 +209,7 @@ struct exynos_drm_manager_ops { void (*win_commit)(struct exynos_drm_manager *mgr, int zpos); void (*win_enable)(struct exynos_drm_manager *mgr, int zpos); void (*win_disable)(struct exynos_drm_manager *mgr, int zpos); + int (*te_handler)(struct exynos_drm_manager *mgr); };
/* diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 7209df1..f6d4c85 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -49,6 +49,13 @@ struct mipi_dsi_msg { * @detach: detach DSI device from DSI host * @transfer: send and/or receive DSI packet, return number of received bytes, * or error + * @te_handler: call the crtc te_handler() callback from DSI host. + * The panel generates tearing effect synchronization signal + * between MCU and FB to display video images. + * And the display controller should trigger to transfer video + * image at this signal. + * So the panel receives the TE IRQ, then calls this handler + * to notify it to the display controller. */ struct mipi_dsi_host_ops { int (*attach)(struct mipi_dsi_host *host, @@ -57,6 +64,7 @@ struct mipi_dsi_host_ops { struct mipi_dsi_device *dsi); ssize_t (*transfer)(struct mipi_dsi_host *host, struct mipi_dsi_msg *msg); + int (*te_handler)(struct mipi_dsi_host *host); };
/**
To support command mode interface, the DSI host calls this handler to notify the panel tearing effect synchronization signal to the CRTC device manager to trigger to transfer video image.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 956e5f3..4a918ec 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -23,6 +23,7 @@ #include <video/mipi_display.h> #include <video/videomode.h>
+#include "exynos_drm_crtc.h" #include "exynos_drm_drv.h"
/* returns true iff both arguments logically differs */ @@ -1032,10 +1033,22 @@ static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host, return (ret < 0) ? ret : xfer.rx_done; }
+static int exynos_dsi_host_te_handler(struct mipi_dsi_host *host) +{ + struct exynos_dsi *dsi = host_to_dsi(host); + struct drm_encoder *encoder = dsi->encoder; + + if (!(dsi->state & DSIM_STATE_ENABLED)) + return -EPERM; + + return exynos_drm_crtc_te_handler(encoder->crtc); +} + static const struct mipi_dsi_host_ops exynos_dsi_ops = { .attach = exynos_dsi_host_attach, .detach = exynos_dsi_host_detach, .transfer = exynos_dsi_host_transfer, + .te_handler = exynos_dsi_host_te_handler, };
static int exynos_dsi_poweron(struct exynos_dsi *dsi)
To support MIPI DSI command mode interface, FIMD should do followings: - Sets LCD block configuration for I80 interface. - Uses "lcd_sys" as an IRQ resource and sets relevant IRQ configuration. - Implements trigger feature which transfers image date if there is page flip request, and implements TE handler to call trigger function. - Sets CPU mode timings configuration. - Sets ideal(pixel) clock is 2 times faster than the original one to generate frame done IRQ prior to the next TE signal.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/Kconfig | 1 + drivers/gpu/drm/exynos/exynos_drm_fimd.c | 280 +++++++++++++++++++++++++----- include/video/samsung_fimd.h | 3 +- 3 files changed, 240 insertions(+), 44 deletions(-)
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 5bf5bca..f4d34f0 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -28,6 +28,7 @@ config DRM_EXYNOS_FIMD bool "Exynos DRM FIMD" depends on DRM_EXYNOS && !FB_S3C && !ARCH_MULTIPLATFORM select FB_MODE_HELPERS + select MFD_SYSCON help Choose this option if you want to use Exynos FIMD for DRM.
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 40fd6cc..9015cf52 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -19,11 +19,14 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/pm_runtime.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h>
#include <video/of_display_timing.h> #include <video/of_videomode.h> #include <video/samsung_fimd.h> #include <drm/exynos_drm.h> +#include <drm/drm_panel.h>
#include "exynos_drm_drv.h" #include "exynos_drm_fbdev.h" @@ -59,6 +62,22 @@ /* color key value register for hardware window 1 ~ 4. */ #define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8))
+/* i80 / RGB trigger control register */ +#define TRIGCON 0x1A4 + +/* display mode change control register except exynos4 */ +#define VIDOUT_CON 0x000 +#define VIDOUT_CON_F_I80_LDI0 (0x2 << 8) + +/* i80 interface control for main LDI register */ +#define I80IFCONFAx(x) (0x1B0 + (x) * 4) +#define I80IFCONFBx(x) (0x1B8 + (x) * 4) +#define LCD_CS_SETUP(x) ((x) << 16) +#define LCD_WR_SETUP(x) ((x) << 12) +#define LCD_WR_ACT(x) ((x) << 8) +#define LCD_WR_HOLD(x) ((x) << 4) +#define I80IFEN_ENABLE (1 << 0) + /* FIMD has totally five hardware windows. */ #define WINDOWS_NR 5
@@ -66,10 +85,14 @@
struct fimd_driver_data { unsigned int timing_base; + unsigned int lcdblk_off; + unsigned int lcdblk_vt_shift; + unsigned int lcdblk_bypass_shift;
unsigned int has_shadowcon:1; unsigned int has_clksel:1; unsigned int has_limited_fmt:1; + unsigned int has_vidoutcon:1; };
static struct fimd_driver_data s3c64xx_fimd_driver_data = { @@ -80,12 +103,19 @@ static struct fimd_driver_data s3c64xx_fimd_driver_data = {
static struct fimd_driver_data exynos4_fimd_driver_data = { .timing_base = 0x0, + .lcdblk_off = 0x210, + .lcdblk_vt_shift = 10, + .lcdblk_bypass_shift = 1, .has_shadowcon = 1, };
static struct fimd_driver_data exynos5_fimd_driver_data = { .timing_base = 0x20000, + .lcdblk_off = 0x214, + .lcdblk_vt_shift = 24, + .lcdblk_bypass_shift = 15, .has_shadowcon = 1, + .has_vidoutcon = 1, };
struct fimd_win_data { @@ -110,15 +140,23 @@ struct fimd_context { struct clk *bus_clk; struct clk *lcd_clk; void __iomem *regs; + struct regmap *sysreg; struct drm_display_mode mode; struct fimd_win_data win_data[WINDOWS_NR]; unsigned int default_win; unsigned long irq_flags; + u32 vidcon0; u32 vidcon1; + u32 vidout_con; + u32 i80ifcon; + bool i80_if; bool suspended; int pipe; wait_queue_head_t wait_vsync_queue; atomic_t wait_vsync_event; + atomic_t win_updated; + atomic_t triggering; + spinlock_t win_updated_lock;
struct exynos_drm_panel_info panel; struct fimd_driver_data *driver_data; @@ -190,6 +228,14 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx, unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh; u32 clkdiv;
+ if (ctx->i80_if) { + /* + * The frame done interrupt should be occurred prior to the + * next TE signal. + */ + ideal_clk *= 2; + } + /* Find the clock divider value that gets us closest to ideal_clk */ clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
@@ -212,17 +258,28 @@ static void fimd_mode_set(struct exynos_drm_manager *mgr, struct fimd_context *ctx = mgr->ctx;
drm_mode_copy(&ctx->mode, in_mode); + + if (ctx->i80_if) { + struct drm_panel_cpu_mode_timings *cm_timings = + (void *)in_mode->private; + + if (cm_timings) { + ctx->i80ifcon = LCD_CS_SETUP(cm_timings->cs_setup); + ctx->i80ifcon |= LCD_WR_SETUP(cm_timings->wr_setup); + ctx->i80ifcon |= LCD_WR_ACT(cm_timings->wr_active); + ctx->i80ifcon |= LCD_WR_HOLD(cm_timings->wr_hold); + } + } }
static void fimd_commit(struct exynos_drm_manager *mgr) { struct fimd_context *ctx = mgr->ctx; struct drm_display_mode *mode = &ctx->mode; - struct fimd_driver_data *driver_data; - u32 val, clkdiv, vidcon1; - int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; + struct fimd_driver_data *driver_data = ctx->driver_data; + void *timing_base = ctx->regs + driver_data->timing_base; + u32 val, clkdiv;
- driver_data = ctx->driver_data; if (ctx->suspended) return;
@@ -230,33 +287,65 @@ static void fimd_commit(struct exynos_drm_manager *mgr) if (mode->htotal == 0 || mode->vtotal == 0) return;
- /* setup polarity values */ - vidcon1 = ctx->vidcon1; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - vidcon1 |= VIDCON1_INV_VSYNC; - if (mode->flags & DRM_MODE_FLAG_NHSYNC) - vidcon1 |= VIDCON1_INV_HSYNC; - writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); - - /* setup vertical timing values. */ - vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; - vbpd = mode->crtc_vtotal - mode->crtc_vsync_end; - vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay; - - val = VIDTCON0_VBPD(vbpd - 1) | - VIDTCON0_VFPD(vfpd - 1) | - VIDTCON0_VSPW(vsync_len - 1); - writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); - - /* setup horizontal timing values. */ - hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; - hbpd = mode->crtc_htotal - mode->crtc_hsync_end; - hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay; - - val = VIDTCON1_HBPD(hbpd - 1) | - VIDTCON1_HFPD(hfpd - 1) | - VIDTCON1_HSPW(hsync_len - 1); - writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); + if (ctx->i80_if) { + val = ctx->i80ifcon | I80IFEN_ENABLE; + writel(val, timing_base + I80IFCONFAx(0)); + + /* disable auto frame rate */ + writel(0, timing_base + I80IFCONFBx(0)); + + if (ctx->vidout_con) + writel(ctx->vidout_con, timing_base + VIDOUT_CON); + + /* set video type selection to i80 interface */ + if (ctx->sysreg && regmap_update_bits(ctx->sysreg, + driver_data->lcdblk_off, + 0x3 << driver_data->lcdblk_vt_shift, + 0x1 << driver_data->lcdblk_vt_shift)) { + DRM_ERROR("Failed to update sysreg for i80 i/f.\n"); + return; + } + } else { + int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; + u32 vidcon1; + + /* setup polarity values */ + vidcon1 = ctx->vidcon1; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + vidcon1 |= VIDCON1_INV_VSYNC; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + vidcon1 |= VIDCON1_INV_HSYNC; + writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); + + /* setup vertical timing values. */ + vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; + vbpd = mode->crtc_vtotal - mode->crtc_vsync_end; + vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay; + + val = VIDTCON0_VBPD(vbpd - 1) | + VIDTCON0_VFPD(vfpd - 1) | + VIDTCON0_VSPW(vsync_len - 1); + writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); + + /* setup horizontal timing values. */ + hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; + hbpd = mode->crtc_htotal - mode->crtc_hsync_end; + hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay; + + val = VIDTCON1_HBPD(hbpd - 1) | + VIDTCON1_HFPD(hfpd - 1) | + VIDTCON1_HSPW(hsync_len - 1); + writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); + } + + /* set bypass selection */ + if (ctx->sysreg && regmap_update_bits(ctx->sysreg, + driver_data->lcdblk_off, + 0x1 << driver_data->lcdblk_bypass_shift, + 0x1 << driver_data->lcdblk_bypass_shift)) { + DRM_ERROR("Failed to update sysreg for bypass setting.\n"); + return; + }
/* setup horizontal and vertical display size. */ val = VIDTCON2_LINEVAL(mode->vdisplay - 1) | @@ -613,6 +702,14 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) }
win_data->enabled = true; + + if (ctx->i80_if) { + unsigned long flags; + + spin_lock_irqsave(&ctx->win_updated_lock, flags); + atomic_set(&ctx->win_updated, 1); + spin_unlock_irqrestore(&ctx->win_updated_lock, flags); + } }
static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) @@ -802,6 +899,68 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) } }
+static void fimd_trigger(struct device *dev) +{ + struct exynos_drm_manager *mgr = get_fimd_manager(dev); + struct fimd_context *ctx = mgr->ctx; + struct fimd_driver_data *driver_data = ctx->driver_data; + void *timing_base = ctx->regs + driver_data->timing_base; + u32 reg; + + atomic_set(&ctx->triggering, 1); + + reg = readl(ctx->regs + VIDINTCON0); + reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE | + VIDINTCON0_INT_SYSMAINCON); + writel(reg, ctx->regs + VIDINTCON0); + + reg = readl(timing_base + TRIGCON); + reg |= 1 << 0 | 1 << 1; + writel(reg, timing_base + TRIGCON); +} + +static int fimd_te_handler(struct exynos_drm_manager *mgr) +{ + struct fimd_context *ctx = mgr->ctx; + unsigned long flags; + + /* check the crtc is detached already from encoder */ + if (ctx->pipe < 0 || !ctx->drm_dev) + return -EINVAL; + + /* + * Skips to trigger if in triggering state, because multiple triggering + * requests can cause panel reset. + */ + if (atomic_read(&ctx->triggering)) + return 0; + + spin_lock_irqsave(&ctx->win_updated_lock, flags); + + /* + * If there is a page flip request, triggers and handles the page flip + * event so that current fb can be updated into panel GRAM. + */ + if (atomic_read(&ctx->win_updated)) { + atomic_set(&ctx->win_updated, 0); + + fimd_trigger(ctx->dev); + } + + spin_unlock_irqrestore(&ctx->win_updated_lock, flags); + + /* wake up vsync event queue */ + if (atomic_read(&ctx->wait_vsync_event)) { + atomic_set(&ctx->wait_vsync_event, 0); + wake_up(&ctx->wait_vsync_queue); + + if (!atomic_read(&ctx->triggering)) + drm_handle_vblank(ctx->drm_dev, ctx->pipe); + } + + return 0; +} + static struct exynos_drm_manager_ops fimd_manager_ops = { .initialize = fimd_mgr_initialize, .remove = fimd_mgr_remove, @@ -815,6 +974,7 @@ static struct exynos_drm_manager_ops fimd_manager_ops = { .win_mode_set = fimd_win_mode_set, .win_commit = fimd_win_commit, .win_disable = fimd_win_disable, + .te_handler = fimd_te_handler, };
static struct exynos_drm_manager fimd_manager = { @@ -825,26 +985,40 @@ static struct exynos_drm_manager fimd_manager = { static irqreturn_t fimd_irq_handler(int irq, void *dev_id) { struct fimd_context *ctx = (struct fimd_context *)dev_id; - u32 val; + u32 val, clear_bit;
val = readl(ctx->regs + VIDINTCON1);
- if (val & VIDINTCON1_INT_FRAME) - /* VSYNC interrupt */ - writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); + clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME; + if (val & clear_bit) + writel(clear_bit, ctx->regs + VIDINTCON1);
/* check the crtc is detached already from encoder */ if (ctx->pipe < 0 || !ctx->drm_dev) goto out;
- drm_handle_vblank(ctx->drm_dev, ctx->pipe); - exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); + if (ctx->i80_if) { + /* unset i80 frame done interrupt */ + val = readl(ctx->regs + VIDINTCON0); + val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON); + writel(val, ctx->regs + VIDINTCON0);
- /* set wait vsync event to zero and wake up queue. */ - if (atomic_read(&ctx->wait_vsync_event)) { - atomic_set(&ctx->wait_vsync_event, 0); - wake_up(&ctx->wait_vsync_queue); + /* exit triggering mode */ + atomic_set(&ctx->triggering, 0); + + drm_handle_vblank(ctx->drm_dev, ctx->pipe); + exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); + } else { + drm_handle_vblank(ctx->drm_dev, ctx->pipe); + exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); + + /* set wait vsync event to zero and wake up queue. */ + if (atomic_read(&ctx->wait_vsync_event)) { + atomic_set(&ctx->wait_vsync_event, 0); + wake_up(&ctx->wait_vsync_queue); + } } + out: return IRQ_HANDLED; } @@ -866,12 +1040,32 @@ static int fimd_probe(struct platform_device *pdev)
ctx->dev = dev; ctx->suspended = true; + ctx->driver_data = drm_fimd_get_driver_data(pdev);
if (of_property_read_bool(dev->of_node, "samsung,invert-vden")) ctx->vidcon1 |= VIDCON1_INV_VDEN; if (of_property_read_bool(dev->of_node, "samsung,invert-vclk")) ctx->vidcon1 |= VIDCON1_INV_VCLK;
+ if (of_property_read_bool(dev->of_node, "vidout-i80-ldi")) { + ctx->i80_if = true; + + if (ctx->driver_data->has_vidoutcon) + ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0; + else + ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0; + ctx->vidcon0 |= VIDCON0_DSI_EN; + + spin_lock_init(&ctx->win_updated_lock); + } + + ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,sysreg"); + if (IS_ERR(ctx->sysreg)) { + dev_warn(dev, "failed to get system register.\n"); + ctx->sysreg = NULL; + } + ctx->bus_clk = devm_clk_get(dev, "fimd"); if (IS_ERR(ctx->bus_clk)) { dev_err(dev, "failed to get bus clock\n"); @@ -890,7 +1084,8 @@ static int fimd_probe(struct platform_device *pdev) if (IS_ERR(ctx->regs)) return PTR_ERR(ctx->regs);
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync"); + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + ctx->i80_if ? "lcd_sys" : "vsync"); if (!res) { dev_err(dev, "irq request failed.\n"); return -ENXIO; @@ -903,7 +1098,6 @@ static int fimd_probe(struct platform_device *pdev) return ret; }
- ctx->driver_data = drm_fimd_get_driver_data(pdev); init_waitqueue_head(&ctx->wait_vsync_queue); atomic_set(&ctx->wait_vsync_event, 0);
diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h index b039320..eaad58b 100644 --- a/include/video/samsung_fimd.h +++ b/include/video/samsung_fimd.h @@ -19,6 +19,7 @@ /* VIDCON0 */
#define VIDCON0 0x00 +#define VIDCON0_DSI_EN (1 << 30) #define VIDCON0_INTERLACE (1 << 29) #define VIDCON0_VIDOUT_MASK (0x7 << 26) #define VIDCON0_VIDOUT_SHIFT 26 @@ -355,7 +356,7 @@ #define VIDINTCON0_INT_ENABLE (1 << 0)
#define VIDINTCON1 0x134 -#define VIDINTCON1_INT_I180 (1 << 2) +#define VIDINTCON1_INT_I80 (1 << 2) #define VIDINTCON1_INT_FRAME (1 << 1) #define VIDINTCON1_INT_FIFO (1 << 0)
Hi YoungJun,
On 27 April 2014 07:20, YoungJun Cho yj44.cho@samsung.com wrote:
To support MIPI DSI command mode interface, FIMD should do followings:
- Sets LCD block configuration for I80 interface.
- Uses "lcd_sys" as an IRQ resource and sets relevant IRQ configuration.
- Implements trigger feature which transfers image date if there is page flip request, and implements TE handler to call trigger function.
- Sets CPU mode timings configuration.
- Sets ideal(pixel) clock is 2 times faster than the original one to generate frame done IRQ prior to the next TE signal.
<...>
reg = readl(timing_base + TRIGCON);
reg |= 1 << 0 | 1 << 1;
What does this signify? Can't this be OR'd directly with 3?
Hi Sachin,
Thank you for comment.
I'll fix.
Thank you. Best regards YJ
On 04/30/2014 12:35 AM, Sachin Kamat wrote:
Hi YoungJun,
On 27 April 2014 07:20, YoungJun Cho yj44.cho@samsung.com wrote:
To support MIPI DSI command mode interface, FIMD should do followings:
- Sets LCD block configuration for I80 interface.
- Uses "lcd_sys" as an IRQ resource and sets relevant IRQ configuration.
- Implements trigger feature which transfers image date if there is page flip request, and implements TE handler to call trigger function.
- Sets CPU mode timings configuration.
- Sets ideal(pixel) clock is 2 times faster than the original one to generate frame done IRQ prior to the next TE signal.
<...>
reg = readl(timing_base + TRIGCON);
reg |= 1 << 0 | 1 << 1;
What does this signify? Can't this be OR'd directly with 3?
This patch adds relevant to exynos5420 compatible for exynos5420 SoC support.
Changelog v2: - Changes title, description and fixes typo (commented by Sachin Kamat)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/video/exynos_dsim.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/video/exynos_dsim.txt b/Documentation/devicetree/bindings/video/exynos_dsim.txt index 33b5730..29bf3b2 100644 --- a/Documentation/devicetree/bindings/video/exynos_dsim.txt +++ b/Documentation/devicetree/bindings/video/exynos_dsim.txt @@ -1,7 +1,9 @@ Exynos MIPI DSI Master
Required properties: - - compatible: "samsung,exynos4210-mipi-dsi" + - compatible: value should be one of the following + "samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */ + "samsung,exynos5420-mipi-dsi" /* for Exynos5420 SoCs */ - reg: physical base address and length of the registers set for the device - interrupts: should contain DSI interrupt - clocks: list of clock specifiers, must contain an entry for each required
The offset of register DSIM_PLLTMR_REG in Exynos5420 is different from the one in Exynos4 SoC.
In case of Exynos5420 SoC, there is no frequency band bit in DSIM_PLLCTRL_REG, and it uses DSIM_PHYCTRL_REG and DSIM_PHYTIMING*_REG instead. So this patch adds driver data to distinguish it.
Changelog v2: - Moves exynos_dsi_enable_clocks() after exynos_dsi_reset() (commented by Andrzej Hajda) - Splits D-PHY control setting routines from PLL setting one (commented by Andrzej Hajda)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 154 ++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 4a918ec..c18dba3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -17,6 +17,7 @@
#include <linux/clk.h> #include <linux/irq.h> +#include <linux/of_device.h> #include <linux/phy/phy.h> #include <linux/regulator/consumer.h>
@@ -54,9 +55,12 @@
/* FIFO memory AC characteristic register */ #define DSIM_PLLCTRL_REG 0x4c /* PLL control register */ -#define DSIM_PLLTMR_REG 0x50 /* PLL timer register */ #define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */ #define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */ +#define DSIM_PHYCTRL_REG 0x5c +#define DSIM_PHYTIMING_REG 0x64 +#define DSIM_PHYTIMING1_REG 0x68 +#define DSIM_PHYTIMING2_REG 0x6c
/* DSIM_STATUS */ #define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) @@ -200,6 +204,21 @@ #define DSIM_PLL_M(x) ((x) << 4) #define DSIM_PLL_S(x) ((x) << 1)
+/* DSIM_PHYTIMING */ +#define DSIM_PHYTIMING_LPX(x) ((x) << 8) +#define DSIM_PHYTIMING_HS_EXIT(x) ((x) << 0) + +/* DSIM_PHYTIMING1 */ +#define DSIM_PHYTIMING1_CLK_PREPARE(x) ((x) << 24) +#define DSIM_PHYTIMING1_CLK_ZERO(x) ((x) << 16) +#define DSIM_PHYTIMING1_CLK_POST(x) ((x) << 8) +#define DSIM_PHYTIMING1_CLK_TRAIL(x) ((x) << 0) + +/* DSIM_PHYTIMING2 */ +#define DSIM_PHYTIMING2_HS_PREPARE(x) ((x) << 16) +#define DSIM_PHYTIMING2_HS_ZERO(x) ((x) << 8) +#define DSIM_PHYTIMING2_HS_TRAIL(x) ((x) << 0) + #define DSI_MAX_BUS_WIDTH 4 #define DSI_NUM_VIRTUAL_CHANNELS 4 #define DSI_TX_FIFO_SIZE 2048 @@ -233,6 +252,12 @@ struct exynos_dsi_transfer { #define DSIM_STATE_INITIALIZED BIT(1) #define DSIM_STATE_CMD_LPM BIT(2)
+struct exynos_dsi_driver_data { + unsigned int plltmr_reg; + + unsigned int has_freqband:1; +}; + struct exynos_dsi { struct mipi_dsi_host dsi_host; struct drm_connector connector; @@ -262,11 +287,39 @@ struct exynos_dsi {
spinlock_t transfer_lock; /* protects transfer_list */ struct list_head transfer_list; + + struct exynos_dsi_driver_data *driver_data; };
#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host) #define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
+static struct exynos_dsi_driver_data exynos4_dsi_driver_data = { + .plltmr_reg = 0x50, + .has_freqband = 1, +}; + +static struct exynos_dsi_driver_data exynos5_dsi_driver_data = { + .plltmr_reg = 0x58, +}; + +static struct of_device_id exynos_dsi_of_match[] = { + { .compatible = "samsung,exynos4210-mipi-dsi", + .data = &exynos4_dsi_driver_data }, + { .compatible = "samsung,exynos5420-mipi-dsi", + .data = &exynos5_dsi_driver_data }, + { } +}; + +static inline struct exynos_dsi_driver_data *exynos_dsi_get_driver_data( + struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(exynos_dsi_of_match, &pdev->dev); + + return (struct exynos_dsi_driver_data *)of_id->data; +} + static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi) { if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300))) @@ -340,14 +393,9 @@ static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi, static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, unsigned long freq) { - static const unsigned long freq_bands[] = { - 100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ, - 270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ, - 510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ, - 770 * MHZ, 870 * MHZ, 950 * MHZ, - }; + struct exynos_dsi_driver_data *driver_data = dsi->driver_data; unsigned long fin, fout; - int timeout, band; + int timeout; u8 p, s; u16 m; u32 reg; @@ -368,18 +416,30 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, "failed to find PLL PMS for requested frequency\n"); return -EFAULT; } + dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
- for (band = 0; band < ARRAY_SIZE(freq_bands); ++band) - if (fout < freq_bands[band]) - break; + writel(500, dsi->reg_base + driver_data->plltmr_reg); + + reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s); + + if (driver_data->has_freqband) { + static const unsigned long freq_bands[] = { + 100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ, + 270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ, + 510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ, + 770 * MHZ, 870 * MHZ, 950 * MHZ, + }; + int band; + + for (band = 0; band < ARRAY_SIZE(freq_bands); ++band) + if (fout < freq_bands[band]) + break;
- dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d), band %d\n", fout, - p, m, s, band); + dev_dbg(dsi->dev, "band %d\n", band);
- writel(500, dsi->reg_base + DSIM_PLLTMR_REG); + reg |= DSIM_FREQ_BAND(band); + }
- reg = DSIM_FREQ_BAND(band) | DSIM_PLL_EN - | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s); writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
timeout = 1000; @@ -433,6 +493,59 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi) return 0; }
+static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi) +{ + struct exynos_dsi_driver_data *driver_data = dsi->driver_data; + u32 reg; + + if (driver_data->has_freqband) + return; + + /* B D-PHY */ + reg = 0x0af & 0x1ff; + writel(reg, dsi->reg_base + DSIM_PHYCTRL_REG); + + /* + * T LPX: Transmitted length of any Low-Power state period + * T HS-EXIT: Time that the transmitter drives LP-11 following a HS + * burst + */ + reg = DSIM_PHYTIMING_LPX(0x06) | DSIM_PHYTIMING_HS_EXIT(0x0b); + writel(reg, dsi->reg_base + DSIM_PHYTIMING_REG); + + /* + * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00 + * Line state immediately before the HS-0 Line state starting the + * HS transmission + * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to + * transmitting the Clock. + * T CLK_POST: Time that the transmitter continues to send HS clock + * after the last associated Data Lane has transitioned to LP Mode + * Interval is defined as the period from the end of T HS-TRAIL to + * the beginning of T CLK-TRAIL + * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after + * the last payload clock bit of a HS transmission burst + */ + reg = DSIM_PHYTIMING1_CLK_PREPARE(0x07) | + DSIM_PHYTIMING1_CLK_ZERO(0x27) | + DSIM_PHYTIMING1_CLK_POST(0x0d) | + DSIM_PHYTIMING1_CLK_TRAIL(0x08); + writel(reg, dsi->reg_base + DSIM_PHYTIMING1_REG); + + /* + * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00 + * Line state immediately before the HS-0 Line state starting the + * HS transmission + * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to + * transmitting the Sync sequence. + * T HS-TRAIL: Time that the transmitter drives the flipped differential + * state after last payload data bit of a HS transmission burst + */ + reg = DSIM_PHYTIMING2_HS_PREPARE(0x09) | DSIM_PHYTIMING2_HS_ZERO(0x0d) | + DSIM_PHYTIMING2_HS_TRAIL(0x0b); + writel(reg, dsi->reg_base + DSIM_PHYTIMING2_REG); +} + static void exynos_dsi_disable_clock(struct exynos_dsi *dsi) { u32 reg; @@ -947,10 +1060,11 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
static int exynos_dsi_init(struct exynos_dsi *dsi) { - exynos_dsi_enable_clock(dsi); exynos_dsi_reset(dsi); enable_irq(dsi->irq); + exynos_dsi_enable_clock(dsi); exynos_dsi_wait_for_reset(dsi); + exynos_dsi_set_phy_ctrl(dsi); exynos_dsi_init_link(dsi);
return 0; @@ -1412,6 +1526,7 @@ static int exynos_dsi_probe(struct platform_device *pdev) dsi->dsi_host.dev = &pdev->dev;
dsi->dev = &pdev->dev; + dsi->driver_data = exynos_dsi_get_driver_data(pdev);
ret = exynos_dsi_parse_dt(dsi); if (ret) @@ -1516,11 +1631,6 @@ static const struct dev_pm_ops exynos_dsi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume) };
-static struct of_device_id exynos_dsi_of_match[] = { - { .compatible = "samsung,exynos4210-mipi-dsi" }, - { } -}; - struct platform_driver dsi_driver = { .probe = exynos_dsi_probe, .remove = exynos_dsi_remove,
On 27 April 2014 07:20, YoungJun Cho yj44.cho@samsung.com wrote:
The offset of register DSIM_PLLTMR_REG in Exynos5420 is different from the one in Exynos4 SoC.
In case of Exynos5420 SoC, there is no frequency band bit in DSIM_PLLCTRL_REG, and it uses DSIM_PHYCTRL_REG and DSIM_PHYTIMING*_REG instead. So this patch adds driver data to distinguish it.
Changelog v2:
- Moves exynos_dsi_enable_clocks() after exynos_dsi_reset() (commented by Andrzej Hajda)
- Splits D-PHY control setting routines from PLL setting one (commented by Andrzej Hajda)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 154 ++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 4a918ec..c18dba3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -17,6 +17,7 @@
#include <linux/clk.h> #include <linux/irq.h> +#include <linux/of_device.h> #include <linux/phy/phy.h> #include <linux/regulator/consumer.h>
@@ -54,9 +55,12 @@
/* FIFO memory AC characteristic register */ #define DSIM_PLLCTRL_REG 0x4c /* PLL control register */ -#define DSIM_PLLTMR_REG 0x50 /* PLL timer register */ #define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */ #define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */ +#define DSIM_PHYCTRL_REG 0x5c +#define DSIM_PHYTIMING_REG 0x64 +#define DSIM_PHYTIMING1_REG 0x68 +#define DSIM_PHYTIMING2_REG 0x6c
/* DSIM_STATUS */ #define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) @@ -200,6 +204,21 @@ #define DSIM_PLL_M(x) ((x) << 4) #define DSIM_PLL_S(x) ((x) << 1)
+/* DSIM_PHYTIMING */ +#define DSIM_PHYTIMING_LPX(x) ((x) << 8) +#define DSIM_PHYTIMING_HS_EXIT(x) ((x) << 0)
+/* DSIM_PHYTIMING1 */ +#define DSIM_PHYTIMING1_CLK_PREPARE(x) ((x) << 24) +#define DSIM_PHYTIMING1_CLK_ZERO(x) ((x) << 16) +#define DSIM_PHYTIMING1_CLK_POST(x) ((x) << 8) +#define DSIM_PHYTIMING1_CLK_TRAIL(x) ((x) << 0)
+/* DSIM_PHYTIMING2 */ +#define DSIM_PHYTIMING2_HS_PREPARE(x) ((x) << 16) +#define DSIM_PHYTIMING2_HS_ZERO(x) ((x) << 8) +#define DSIM_PHYTIMING2_HS_TRAIL(x) ((x) << 0)
#define DSI_MAX_BUS_WIDTH 4 #define DSI_NUM_VIRTUAL_CHANNELS 4 #define DSI_TX_FIFO_SIZE 2048 @@ -233,6 +252,12 @@ struct exynos_dsi_transfer { #define DSIM_STATE_INITIALIZED BIT(1) #define DSIM_STATE_CMD_LPM BIT(2)
+struct exynos_dsi_driver_data {
Shouldn't this be static?
unsigned int plltmr_reg;
nit: stray blank line
unsigned int has_freqband:1;
+};
<snip>
+static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi) +{
struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
u32 reg;
if (driver_data->has_freqband)
return;
/* B D-PHY */
reg = 0x0af & 0x1ff;
Please use macros instead of magic numbers.
Hi Sachin,
Thank you for comment.
I'll fix.
Thank you. Best regards YJ
On 04/30/2014 12:26 AM, Sachin Kamat wrote:
On 27 April 2014 07:20, YoungJun Cho yj44.cho@samsung.com wrote:
The offset of register DSIM_PLLTMR_REG in Exynos5420 is different from the one in Exynos4 SoC.
In case of Exynos5420 SoC, there is no frequency band bit in DSIM_PLLCTRL_REG, and it uses DSIM_PHYCTRL_REG and DSIM_PHYTIMING*_REG instead. So this patch adds driver data to distinguish it.
Changelog v2:
- Moves exynos_dsi_enable_clocks() after exynos_dsi_reset() (commented by Andrzej Hajda)
- Splits D-PHY control setting routines from PLL setting one (commented by Andrzej Hajda)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 154 ++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 4a918ec..c18dba3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -17,6 +17,7 @@
#include <linux/clk.h> #include <linux/irq.h> +#include <linux/of_device.h> #include <linux/phy/phy.h> #include <linux/regulator/consumer.h>
@@ -54,9 +55,12 @@
/* FIFO memory AC characteristic register */ #define DSIM_PLLCTRL_REG 0x4c /* PLL control register */ -#define DSIM_PLLTMR_REG 0x50 /* PLL timer register */ #define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */ #define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */ +#define DSIM_PHYCTRL_REG 0x5c +#define DSIM_PHYTIMING_REG 0x64 +#define DSIM_PHYTIMING1_REG 0x68 +#define DSIM_PHYTIMING2_REG 0x6c
/* DSIM_STATUS */ #define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) @@ -200,6 +204,21 @@ #define DSIM_PLL_M(x) ((x) << 4) #define DSIM_PLL_S(x) ((x) << 1)
+/* DSIM_PHYTIMING */ +#define DSIM_PHYTIMING_LPX(x) ((x) << 8) +#define DSIM_PHYTIMING_HS_EXIT(x) ((x) << 0)
+/* DSIM_PHYTIMING1 */ +#define DSIM_PHYTIMING1_CLK_PREPARE(x) ((x) << 24) +#define DSIM_PHYTIMING1_CLK_ZERO(x) ((x) << 16) +#define DSIM_PHYTIMING1_CLK_POST(x) ((x) << 8) +#define DSIM_PHYTIMING1_CLK_TRAIL(x) ((x) << 0)
+/* DSIM_PHYTIMING2 */ +#define DSIM_PHYTIMING2_HS_PREPARE(x) ((x) << 16) +#define DSIM_PHYTIMING2_HS_ZERO(x) ((x) << 8) +#define DSIM_PHYTIMING2_HS_TRAIL(x) ((x) << 0)
- #define DSI_MAX_BUS_WIDTH 4 #define DSI_NUM_VIRTUAL_CHANNELS 4 #define DSI_TX_FIFO_SIZE 2048
@@ -233,6 +252,12 @@ struct exynos_dsi_transfer { #define DSIM_STATE_INITIALIZED BIT(1) #define DSIM_STATE_CMD_LPM BIT(2)
+struct exynos_dsi_driver_data {
Shouldn't this be static?
unsigned int plltmr_reg;
nit: stray blank line
unsigned int has_freqband:1;
+};
<snip>
+static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi) +{
struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
u32 reg;
if (driver_data->has_freqband)
return;
/* B D-PHY */
reg = 0x0af & 0x1ff;
Please use macros instead of magic numbers.
On 04/27/2014 03:50 AM, YoungJun Cho wrote:
The offset of register DSIM_PLLTMR_REG in Exynos5420 is different from the one in Exynos4 SoC.
In case of Exynos5420 SoC, there is no frequency band bit in DSIM_PLLCTRL_REG, and it uses DSIM_PHYCTRL_REG and DSIM_PHYTIMING*_REG instead. So this patch adds driver data to distinguish it.
Changelog v2:
- Moves exynos_dsi_enable_clocks() after exynos_dsi_reset() (commented by Andrzej Hajda)
- Splits D-PHY control setting routines from PLL setting one (commented by Andrzej Hajda)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 154 ++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 4a918ec..c18dba3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -17,6 +17,7 @@
#include <linux/clk.h> #include <linux/irq.h> +#include <linux/of_device.h> #include <linux/phy/phy.h> #include <linux/regulator/consumer.h>
@@ -54,9 +55,12 @@
/* FIFO memory AC characteristic register */ #define DSIM_PLLCTRL_REG 0x4c /* PLL control register */ -#define DSIM_PLLTMR_REG 0x50 /* PLL timer register */ #define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */ #define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */ +#define DSIM_PHYCTRL_REG 0x5c +#define DSIM_PHYTIMING_REG 0x64 +#define DSIM_PHYTIMING1_REG 0x68 +#define DSIM_PHYTIMING2_REG 0x6c
/* DSIM_STATUS */ #define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) @@ -200,6 +204,21 @@ #define DSIM_PLL_M(x) ((x) << 4) #define DSIM_PLL_S(x) ((x) << 1)
+/* DSIM_PHYTIMING */ +#define DSIM_PHYTIMING_LPX(x) ((x) << 8) +#define DSIM_PHYTIMING_HS_EXIT(x) ((x) << 0)
+/* DSIM_PHYTIMING1 */ +#define DSIM_PHYTIMING1_CLK_PREPARE(x) ((x) << 24) +#define DSIM_PHYTIMING1_CLK_ZERO(x) ((x) << 16) +#define DSIM_PHYTIMING1_CLK_POST(x) ((x) << 8) +#define DSIM_PHYTIMING1_CLK_TRAIL(x) ((x) << 0)
+/* DSIM_PHYTIMING2 */ +#define DSIM_PHYTIMING2_HS_PREPARE(x) ((x) << 16) +#define DSIM_PHYTIMING2_HS_ZERO(x) ((x) << 8) +#define DSIM_PHYTIMING2_HS_TRAIL(x) ((x) << 0)
#define DSI_MAX_BUS_WIDTH 4 #define DSI_NUM_VIRTUAL_CHANNELS 4 #define DSI_TX_FIFO_SIZE 2048 @@ -233,6 +252,12 @@ struct exynos_dsi_transfer { #define DSIM_STATE_INITIALIZED BIT(1) #define DSIM_STATE_CMD_LPM BIT(2)
+struct exynos_dsi_driver_data {
- unsigned int plltmr_reg;
- unsigned int has_freqband:1;
+};
struct exynos_dsi { struct mipi_dsi_host dsi_host; struct drm_connector connector; @@ -262,11 +287,39 @@ struct exynos_dsi {
spinlock_t transfer_lock; /* protects transfer_list */ struct list_head transfer_list;
- struct exynos_dsi_driver_data *driver_data;
};
#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host) #define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
+static struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
- .plltmr_reg = 0x50,
- .has_freqband = 1,
+};
+static struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
- .plltmr_reg = 0x58,
+};
+static struct of_device_id exynos_dsi_of_match[] = {
- { .compatible = "samsung,exynos4210-mipi-dsi",
.data = &exynos4_dsi_driver_data },
- { .compatible = "samsung,exynos5420-mipi-dsi",
.data = &exynos5_dsi_driver_data },
- { }
+};
I raise again issue of compatible string. Since exynos5410 DSIM contains DSIM_VERSION register, it is better to use this register instead of compatible string to distinguish device versions. So as a compatible we should use the first exynos chipset containing this field. AFAIK it is exynos5410. So the only thing you should do is to change compatible string from samsung,exynos5420-mipi-dsi to samsung,exynos5410-mipi-dsi.
I posted RFC v3 without this try.
Because there is no exynos5410 relevant DTS yet, and making exynos5410 DTS is out of scope for this RFC.
It is irrelevant.
Regards Andrzej
+static inline struct exynos_dsi_driver_data *exynos_dsi_get_driver_data(
struct platform_device *pdev)
+{
- const struct of_device_id *of_id =
of_match_device(exynos_dsi_of_match, &pdev->dev);
- return (struct exynos_dsi_driver_data *)of_id->data;
+}
static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi) { if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300))) @@ -340,14 +393,9 @@ static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi, static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, unsigned long freq) {
- static const unsigned long freq_bands[] = {
100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
770 * MHZ, 870 * MHZ, 950 * MHZ,
- };
- struct exynos_dsi_driver_data *driver_data = dsi->driver_data; unsigned long fin, fout;
- int timeout, band;
- int timeout; u8 p, s; u16 m; u32 reg;
@@ -368,18 +416,30 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, "failed to find PLL PMS for requested frequency\n"); return -EFAULT; }
- dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
- for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
if (fout < freq_bands[band])
break;
- writel(500, dsi->reg_base + driver_data->plltmr_reg);
- reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
- if (driver_data->has_freqband) {
static const unsigned long freq_bands[] = {
100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
770 * MHZ, 870 * MHZ, 950 * MHZ,
};
int band;
for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
if (fout < freq_bands[band])
break;
- dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d), band %d\n", fout,
p, m, s, band);
dev_dbg(dsi->dev, "band %d\n", band);
- writel(500, dsi->reg_base + DSIM_PLLTMR_REG);
reg |= DSIM_FREQ_BAND(band);
- }
reg = DSIM_FREQ_BAND(band) | DSIM_PLL_EN
| DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
timeout = 1000;
@@ -433,6 +493,59 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi) return 0; }
+static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi) +{
- struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
- u32 reg;
- if (driver_data->has_freqband)
return;
- /* B D-PHY */
- reg = 0x0af & 0x1ff;
- writel(reg, dsi->reg_base + DSIM_PHYCTRL_REG);
- /*
* T LPX: Transmitted length of any Low-Power state period
* T HS-EXIT: Time that the transmitter drives LP-11 following a HS
* burst
*/
- reg = DSIM_PHYTIMING_LPX(0x06) | DSIM_PHYTIMING_HS_EXIT(0x0b);
- writel(reg, dsi->reg_base + DSIM_PHYTIMING_REG);
- /*
* T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
* Line state immediately before the HS-0 Line state starting the
* HS transmission
* T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
* transmitting the Clock.
* T CLK_POST: Time that the transmitter continues to send HS clock
* after the last associated Data Lane has transitioned to LP Mode
* Interval is defined as the period from the end of T HS-TRAIL to
* the beginning of T CLK-TRAIL
* T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
* the last payload clock bit of a HS transmission burst
*/
- reg = DSIM_PHYTIMING1_CLK_PREPARE(0x07) |
DSIM_PHYTIMING1_CLK_ZERO(0x27) |
DSIM_PHYTIMING1_CLK_POST(0x0d) |
DSIM_PHYTIMING1_CLK_TRAIL(0x08);
- writel(reg, dsi->reg_base + DSIM_PHYTIMING1_REG);
- /*
* T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
* Line state immediately before the HS-0 Line state starting the
* HS transmission
* T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
* transmitting the Sync sequence.
* T HS-TRAIL: Time that the transmitter drives the flipped differential
* state after last payload data bit of a HS transmission burst
*/
- reg = DSIM_PHYTIMING2_HS_PREPARE(0x09) | DSIM_PHYTIMING2_HS_ZERO(0x0d) |
DSIM_PHYTIMING2_HS_TRAIL(0x0b);
- writel(reg, dsi->reg_base + DSIM_PHYTIMING2_REG);
+}
static void exynos_dsi_disable_clock(struct exynos_dsi *dsi) { u32 reg; @@ -947,10 +1060,11 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
static int exynos_dsi_init(struct exynos_dsi *dsi) {
- exynos_dsi_enable_clock(dsi); exynos_dsi_reset(dsi); enable_irq(dsi->irq);
exynos_dsi_enable_clock(dsi); exynos_dsi_wait_for_reset(dsi);
exynos_dsi_set_phy_ctrl(dsi); exynos_dsi_init_link(dsi);
return 0;
@@ -1412,6 +1526,7 @@ static int exynos_dsi_probe(struct platform_device *pdev) dsi->dsi_host.dev = &pdev->dev;
dsi->dev = &pdev->dev;
dsi->driver_data = exynos_dsi_get_driver_data(pdev);
ret = exynos_dsi_parse_dt(dsi); if (ret)
@@ -1516,11 +1631,6 @@ static const struct dev_pm_ops exynos_dsi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume) };
-static struct of_device_id exynos_dsi_of_match[] = {
- { .compatible = "samsung,exynos4210-mipi-dsi" },
- { }
-};
struct platform_driver dsi_driver = { .probe = exynos_dsi_probe, .remove = exynos_dsi_remove,
Hi Andrzej,
Thank you for comments.
On 05/05/2014 08:27 PM, Andrzej Hajda wrote:
On 04/27/2014 03:50 AM, YoungJun Cho wrote:
The offset of register DSIM_PLLTMR_REG in Exynos5420 is different from the one in Exynos4 SoC.
In case of Exynos5420 SoC, there is no frequency band bit in DSIM_PLLCTRL_REG, and it uses DSIM_PHYCTRL_REG and DSIM_PHYTIMING*_REG instead. So this patch adds driver data to distinguish it.
Changelog v2:
- Moves exynos_dsi_enable_clocks() after exynos_dsi_reset() (commented by Andrzej Hajda)
- Splits D-PHY control setting routines from PLL setting one (commented by Andrzej Hajda)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 154 ++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 4a918ec..c18dba3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -17,6 +17,7 @@
#include <linux/clk.h> #include <linux/irq.h> +#include <linux/of_device.h> #include <linux/phy/phy.h> #include <linux/regulator/consumer.h>
@@ -54,9 +55,12 @@
/* FIFO memory AC characteristic register */ #define DSIM_PLLCTRL_REG 0x4c /* PLL control register */ -#define DSIM_PLLTMR_REG 0x50 /* PLL timer register */ #define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */ #define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */ +#define DSIM_PHYCTRL_REG 0x5c +#define DSIM_PHYTIMING_REG 0x64 +#define DSIM_PHYTIMING1_REG 0x68 +#define DSIM_PHYTIMING2_REG 0x6c
/* DSIM_STATUS */ #define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) @@ -200,6 +204,21 @@ #define DSIM_PLL_M(x) ((x) << 4) #define DSIM_PLL_S(x) ((x) << 1)
+/* DSIM_PHYTIMING */ +#define DSIM_PHYTIMING_LPX(x) ((x) << 8) +#define DSIM_PHYTIMING_HS_EXIT(x) ((x) << 0)
+/* DSIM_PHYTIMING1 */ +#define DSIM_PHYTIMING1_CLK_PREPARE(x) ((x) << 24) +#define DSIM_PHYTIMING1_CLK_ZERO(x) ((x) << 16) +#define DSIM_PHYTIMING1_CLK_POST(x) ((x) << 8) +#define DSIM_PHYTIMING1_CLK_TRAIL(x) ((x) << 0)
+/* DSIM_PHYTIMING2 */ +#define DSIM_PHYTIMING2_HS_PREPARE(x) ((x) << 16) +#define DSIM_PHYTIMING2_HS_ZERO(x) ((x) << 8) +#define DSIM_PHYTIMING2_HS_TRAIL(x) ((x) << 0)
- #define DSI_MAX_BUS_WIDTH 4 #define DSI_NUM_VIRTUAL_CHANNELS 4 #define DSI_TX_FIFO_SIZE 2048
@@ -233,6 +252,12 @@ struct exynos_dsi_transfer { #define DSIM_STATE_INITIALIZED BIT(1) #define DSIM_STATE_CMD_LPM BIT(2)
+struct exynos_dsi_driver_data {
- unsigned int plltmr_reg;
- unsigned int has_freqband:1;
+};
- struct exynos_dsi { struct mipi_dsi_host dsi_host; struct drm_connector connector;
@@ -262,11 +287,39 @@ struct exynos_dsi {
spinlock_t transfer_lock; /* protects transfer_list */ struct list_head transfer_list;
struct exynos_dsi_driver_data *driver_data; };
#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host) #define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
+static struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
- .plltmr_reg = 0x50,
- .has_freqband = 1,
+};
+static struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
- .plltmr_reg = 0x58,
+};
+static struct of_device_id exynos_dsi_of_match[] = {
- { .compatible = "samsung,exynos4210-mipi-dsi",
.data = &exynos4_dsi_driver_data },
- { .compatible = "samsung,exynos5420-mipi-dsi",
.data = &exynos5_dsi_driver_data },
- { }
+};
I raise again issue of compatible string. Since exynos5410 DSIM contains DSIM_VERSION register, it is better to use this register instead of compatible string to distinguish device versions. So as a compatible we should use the first exynos chipset containing this field. AFAIK it is exynos5410. So the only thing you should do is to change compatible string from samsung,exynos5420-mipi-dsi to samsung,exynos5410-mipi-dsi.
I posted RFC v3 without this try.
Because there is no exynos5410 relevant DTS yet, and making exynos5410 DTS is out of scope for this RFC.
It is irrelevant.
Ok, I'll fix it.
Thank you. Best regards YJ
Regards Andrzej
+static inline struct exynos_dsi_driver_data *exynos_dsi_get_driver_data(
struct platform_device *pdev)
+{
- const struct of_device_id *of_id =
of_match_device(exynos_dsi_of_match, &pdev->dev);
- return (struct exynos_dsi_driver_data *)of_id->data;
+}
- static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi) { if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
@@ -340,14 +393,9 @@ static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi, static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, unsigned long freq) {
- static const unsigned long freq_bands[] = {
100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
770 * MHZ, 870 * MHZ, 950 * MHZ,
- };
- struct exynos_dsi_driver_data *driver_data = dsi->driver_data; unsigned long fin, fout;
- int timeout, band;
- int timeout; u8 p, s; u16 m; u32 reg;
@@ -368,18 +416,30 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, "failed to find PLL PMS for requested frequency\n"); return -EFAULT; }
- dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
- for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
if (fout < freq_bands[band])
break;
- writel(500, dsi->reg_base + driver_data->plltmr_reg);
- reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
- if (driver_data->has_freqband) {
static const unsigned long freq_bands[] = {
100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
770 * MHZ, 870 * MHZ, 950 * MHZ,
};
int band;
for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
if (fout < freq_bands[band])
break;
- dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d), band %d\n", fout,
p, m, s, band);
dev_dbg(dsi->dev, "band %d\n", band);
- writel(500, dsi->reg_base + DSIM_PLLTMR_REG);
reg |= DSIM_FREQ_BAND(band);
- }
reg = DSIM_FREQ_BAND(band) | DSIM_PLL_EN
| DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
timeout = 1000;
@@ -433,6 +493,59 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi) return 0; }
+static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi) +{
- struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
- u32 reg;
- if (driver_data->has_freqband)
return;
- /* B D-PHY */
- reg = 0x0af & 0x1ff;
- writel(reg, dsi->reg_base + DSIM_PHYCTRL_REG);
- /*
* T LPX: Transmitted length of any Low-Power state period
* T HS-EXIT: Time that the transmitter drives LP-11 following a HS
* burst
*/
- reg = DSIM_PHYTIMING_LPX(0x06) | DSIM_PHYTIMING_HS_EXIT(0x0b);
- writel(reg, dsi->reg_base + DSIM_PHYTIMING_REG);
- /*
* T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
* Line state immediately before the HS-0 Line state starting the
* HS transmission
* T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
* transmitting the Clock.
* T CLK_POST: Time that the transmitter continues to send HS clock
* after the last associated Data Lane has transitioned to LP Mode
* Interval is defined as the period from the end of T HS-TRAIL to
* the beginning of T CLK-TRAIL
* T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
* the last payload clock bit of a HS transmission burst
*/
- reg = DSIM_PHYTIMING1_CLK_PREPARE(0x07) |
DSIM_PHYTIMING1_CLK_ZERO(0x27) |
DSIM_PHYTIMING1_CLK_POST(0x0d) |
DSIM_PHYTIMING1_CLK_TRAIL(0x08);
- writel(reg, dsi->reg_base + DSIM_PHYTIMING1_REG);
- /*
* T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
* Line state immediately before the HS-0 Line state starting the
* HS transmission
* T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
* transmitting the Sync sequence.
* T HS-TRAIL: Time that the transmitter drives the flipped differential
* state after last payload data bit of a HS transmission burst
*/
- reg = DSIM_PHYTIMING2_HS_PREPARE(0x09) | DSIM_PHYTIMING2_HS_ZERO(0x0d) |
DSIM_PHYTIMING2_HS_TRAIL(0x0b);
- writel(reg, dsi->reg_base + DSIM_PHYTIMING2_REG);
+}
- static void exynos_dsi_disable_clock(struct exynos_dsi *dsi) { u32 reg;
@@ -947,10 +1060,11 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
static int exynos_dsi_init(struct exynos_dsi *dsi) {
- exynos_dsi_enable_clock(dsi); exynos_dsi_reset(dsi); enable_irq(dsi->irq);
exynos_dsi_enable_clock(dsi); exynos_dsi_wait_for_reset(dsi);
exynos_dsi_set_phy_ctrl(dsi); exynos_dsi_init_link(dsi);
return 0;
@@ -1412,6 +1526,7 @@ static int exynos_dsi_probe(struct platform_device *pdev) dsi->dsi_host.dev = &pdev->dev;
dsi->dev = &pdev->dev;
dsi->driver_data = exynos_dsi_get_driver_data(pdev);
ret = exynos_dsi_parse_dt(dsi); if (ret)
@@ -1516,11 +1631,6 @@ static const struct dev_pm_ops exynos_dsi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume) };
-static struct of_device_id exynos_dsi_of_match[] = {
- { .compatible = "samsung,exynos4210-mipi-dsi" },
- { }
-};
- struct platform_driver dsi_driver = { .probe = exynos_dsi_probe, .remove = exynos_dsi_remove,
This patch adds DT bindings for s6e3fa0 panel. The bindings describes panel resources, display timings and cpu mode timings.
Changelog v2: - Adds unit address (commented by Sachin Kamat) Changelog v3: - Removes optional delay, size properties (commented by Laurent Pinchart) - Adds OLED detection, TE gpio properties Changelog v4: - Moves CPU timings relevant properties from FIMD DT (commeted by Laurent Pinchart, Andrzej Hajda) Changelog v5: - Fixes gpio property names (commented by Andrzej Hajda) Changelog v6: - Renames CPU timings to CPU mode timings - Modifies CPU mode timings internal properties relevant things (commeted by Laurent Pinchart, Andrzej Hajda)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/panel/samsung,s6e3fa0.txt | 68 ++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt
diff --git a/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt new file mode 100644 index 0000000..9f06645 --- /dev/null +++ b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt @@ -0,0 +1,68 @@ +Samsung S6E3FA0 AMOLED LCD 5.7 inch panel + +Required properties: + - compatible: "samsung,s6e3fa0" + - reg: the virtual channel number of a DSI peripheral + - vdd3-supply: core voltage supply + - vci-supply: voltage supply for analog circuits + - reset-gpios: a GPIO spec for the reset pin + - det-gpios: a GPIO spec for the OLED detection pin + - te-gpios: a GPIO spec for the TE pin + - display-timings: timings for the connected panel as described by [1] + - cpu-mode-timings: CPU interface timings for the connected panel, + and it contains following properties. + Required properties: + - wr-active: clock cycles for the active period of CS enable in CPU + interface. + Optional properties: + - cs-setup: clock cycles for the active period of address signal + enable until chip select is enable in CPU interface. + If not specified, the default value(0) will be used. + - wr-setup: clock cycles for the active period of CS signal enable + until write signal is enable in CPU interface. + If not specified, the default value(0) will be used. + - wr-hold: clock cycles for the active period of CS disable until + write signal is disable in CPU interface. + If not specified, the default value(0) will be used. + +Optional properties: + +The device node can contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in [2]. This node should describe +panel's video bus. + +[1]: Documentation/devicetree/bindings/video/display-timing.txt +[2]: Documentation/devicetree/bindings/media/video-interfaces.txt + +Example: + + panel@0 { + compatible = "samsung,s6e3fa0"; + reg = <0>; + vdd3-supply = <&vcclcd_reg>; + vci-supply = <&vlcd_reg>; + reset-gpios = <&gpy7 4 0>; + det-gpios = <&gpg0 6 0>; + te-gpios = <&gpd1 7 0>; + + display-timings { + timing0: timing-0 { + clock-frequency = <0>; + hactive = <1080>; + vactive = <1920>; + hfront-porch = <2>; + hback-porch = <2>; + hsync-len = <1>; + vfront-porch = <1>; + vback-porch = <4>; + vsync-len = <1>; + }; + }; + + cpu-mode-timings { + cs-setup = <0>; + wr-setup = <0>; + wr-active = <1>; + wr-hold = <0>; + }; + };
On 04/27/2014 03:50 AM, YoungJun Cho wrote:
This patch adds DT bindings for s6e3fa0 panel. The bindings describes panel resources, display timings and cpu mode timings.
Changelog v2:
- Adds unit address (commented by Sachin Kamat)
Changelog v3:
- Removes optional delay, size properties (commented by Laurent Pinchart)
- Adds OLED detection, TE gpio properties
Changelog v4:
- Moves CPU timings relevant properties from FIMD DT (commeted by Laurent Pinchart, Andrzej Hajda)
Changelog v5:
- Fixes gpio property names (commented by Andrzej Hajda)
Changelog v6:
- Renames CPU timings to CPU mode timings
- Modifies CPU mode timings internal properties relevant things (commeted by Laurent Pinchart, Andrzej Hajda)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com
.../devicetree/bindings/panel/samsung,s6e3fa0.txt | 68 ++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt
diff --git a/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt new file mode 100644 index 0000000..9f06645 --- /dev/null +++ b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt @@ -0,0 +1,68 @@ +Samsung S6E3FA0 AMOLED LCD 5.7 inch panel
+Required properties:
- compatible: "samsung,s6e3fa0"
- reg: the virtual channel number of a DSI peripheral
- vdd3-supply: core voltage supply
- vci-supply: voltage supply for analog circuits
- reset-gpios: a GPIO spec for the reset pin
- det-gpios: a GPIO spec for the OLED detection pin
- te-gpios: a GPIO spec for the TE pin
- display-timings: timings for the connected panel as described by [1]
This still bothers me, it forces users to provide bunch of fake properties (four porches, two syncs and clock-frequency) just because we need to pass somehow pixel width and height. And do we really need pixel dimension to be passed via DT? I guess it could be: - hardcoded into the driver, - derived from the panel id, - maybe read from the panel, this is the best option I guess but I am not sure if panel provides an API for this.
Regards Andrzej
- cpu-mode-timings: CPU interface timings for the connected panel,
and it contains following properties.
Required properties:
- wr-active: clock cycles for the active period of CS enable in CPU
interface.
Optional properties:
- cs-setup: clock cycles for the active period of address signal
enable until chip select is enable in CPU interface.
If not specified, the default value(0) will be used.
- wr-setup: clock cycles for the active period of CS signal enable
until write signal is enable in CPU interface.
If not specified, the default value(0) will be used.
- wr-hold: clock cycles for the active period of CS disable until
write signal is disable in CPU interface.
If not specified, the default value(0) will be used.
+Optional properties:
+The device node can contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in [2]. This node should describe +panel's video bus.
+[1]: Documentation/devicetree/bindings/video/display-timing.txt +[2]: Documentation/devicetree/bindings/media/video-interfaces.txt
+Example:
- panel@0 {
compatible = "samsung,s6e3fa0";
reg = <0>;
vdd3-supply = <&vcclcd_reg>;
vci-supply = <&vlcd_reg>;
reset-gpios = <&gpy7 4 0>;
det-gpios = <&gpg0 6 0>;
te-gpios = <&gpd1 7 0>;
display-timings {
timing0: timing-0 {
clock-frequency = <0>;
hactive = <1080>;
vactive = <1920>;
hfront-porch = <2>;
hback-porch = <2>;
hsync-len = <1>;
vfront-porch = <1>;
vback-porch = <4>;
vsync-len = <1>;
};
};
cpu-mode-timings {
cs-setup = <0>;
wr-setup = <0>;
wr-active = <1>;
wr-hold = <0>;
};
- };
Hi Andrzej
Thank you for comments.
On 05/05/2014 07:35 PM, Andrzej Hajda wrote:
On 04/27/2014 03:50 AM, YoungJun Cho wrote:
This patch adds DT bindings for s6e3fa0 panel. The bindings describes panel resources, display timings and cpu mode timings.
Changelog v2:
- Adds unit address (commented by Sachin Kamat)
Changelog v3:
- Removes optional delay, size properties (commented by Laurent Pinchart)
- Adds OLED detection, TE gpio properties
Changelog v4:
- Moves CPU timings relevant properties from FIMD DT (commeted by Laurent Pinchart, Andrzej Hajda)
Changelog v5:
- Fixes gpio property names (commented by Andrzej Hajda)
Changelog v6:
- Renames CPU timings to CPU mode timings
- Modifies CPU mode timings internal properties relevant things (commeted by Laurent Pinchart, Andrzej Hajda)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com
.../devicetree/bindings/panel/samsung,s6e3fa0.txt | 68 ++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt
diff --git a/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt new file mode 100644 index 0000000..9f06645 --- /dev/null +++ b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt @@ -0,0 +1,68 @@ +Samsung S6E3FA0 AMOLED LCD 5.7 inch panel
+Required properties:
- compatible: "samsung,s6e3fa0"
- reg: the virtual channel number of a DSI peripheral
- vdd3-supply: core voltage supply
- vci-supply: voltage supply for analog circuits
- reset-gpios: a GPIO spec for the reset pin
- det-gpios: a GPIO spec for the OLED detection pin
- te-gpios: a GPIO spec for the TE pin
- display-timings: timings for the connected panel as described by [1]
This still bothers me, it forces users to provide bunch of fake properties (four porches, two syncs and clock-frequency) just because we need to pass somehow pixel width and height. And do we really need pixel dimension to be passed via DT? I guess it could be:
- hardcoded into the driver,
- derived from the panel id,
- maybe read from the panel, this is the best option I guess but I am
not sure if panel provides an API for this.
I have been trying to only use cpu-mode-timings without display-timings for next RFC v4.
As you mentioned, the only required things in display-timings are clock-frequency, hactive and vactive. I put them into cpu-mode-timings, but strictly speaking, cpu-mode-timings is not related with display timing, it is data transmission mode switch timing. And there is no interface for this in drm (display) mode yet. So I'm not sure what is the generic method to treat them.
Thank you. Best regards YJ
Regards Andrzej
- cpu-mode-timings: CPU interface timings for the connected panel,
and it contains following properties.
Required properties:
- wr-active: clock cycles for the active period of CS enable in CPU
interface.
Optional properties:
- cs-setup: clock cycles for the active period of address signal
enable until chip select is enable in CPU interface.
If not specified, the default value(0) will be used.
- wr-setup: clock cycles for the active period of CS signal enable
until write signal is enable in CPU interface.
If not specified, the default value(0) will be used.
- wr-hold: clock cycles for the active period of CS disable until
write signal is disable in CPU interface.
If not specified, the default value(0) will be used.
+Optional properties:
+The device node can contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in [2]. This node should describe +panel's video bus.
+[1]: Documentation/devicetree/bindings/video/display-timing.txt +[2]: Documentation/devicetree/bindings/media/video-interfaces.txt
+Example:
- panel@0 {
compatible = "samsung,s6e3fa0";
reg = <0>;
vdd3-supply = <&vcclcd_reg>;
vci-supply = <&vlcd_reg>;
reset-gpios = <&gpy7 4 0>;
det-gpios = <&gpg0 6 0>;
te-gpios = <&gpd1 7 0>;
display-timings {
timing0: timing-0 {
clock-frequency = <0>;
hactive = <1080>;
vactive = <1920>;
hfront-porch = <2>;
hback-porch = <2>;
hsync-len = <1>;
vfront-porch = <1>;
vback-porch = <4>;
vsync-len = <1>;
};
};
cpu-mode-timings {
cs-setup = <0>;
wr-setup = <0>;
wr-active = <1>;
wr-hold = <0>;
};
- };
On Wednesday 07 May 2014 10:05:46 YoungJun Cho wrote:
Hi Andrzej
Thank you for comments.
On 05/05/2014 07:35 PM, Andrzej Hajda wrote:
On 04/27/2014 03:50 AM, YoungJun Cho wrote:
This patch adds DT bindings for s6e3fa0 panel. The bindings describes panel resources, display timings and cpu mode timings.
Changelog v2:
- Adds unit address (commented by Sachin Kamat)
Changelog v3:
- Removes optional delay, size properties (commented by Laurent Pinchart)
- Adds OLED detection, TE gpio properties
Changelog v4:
Moves CPU timings relevant properties from FIMD DT
(commeted by Laurent Pinchart, Andrzej Hajda)
Changelog v5:
- Fixes gpio property names (commented by Andrzej Hajda)
Changelog v6:
Renames CPU timings to CPU mode timings
Modifies CPU mode timings internal properties relevant things
(commeted by Laurent Pinchart, Andrzej Hajda)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com
.../devicetree/bindings/panel/samsung,s6e3fa0.txt | 68 ++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt>> diff --git a/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt new file mode 100644 index 0000000..9f06645 --- /dev/null +++ b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt @@ -0,0 +1,68 @@ +Samsung S6E3FA0 AMOLED LCD 5.7 inch panel
+Required properties:
- compatible: "samsung,s6e3fa0"
- reg: the virtual channel number of a DSI peripheral
- vdd3-supply: core voltage supply
- vci-supply: voltage supply for analog circuits
- reset-gpios: a GPIO spec for the reset pin
- det-gpios: a GPIO spec for the OLED detection pin
- te-gpios: a GPIO spec for the TE pin
- display-timings: timings for the connected panel as described by [1]
This still bothers me, it forces users to provide bunch of fake properties (four porches, two syncs and clock-frequency) just because we need to pass somehow pixel width and height. And do we really need pixel dimension to be passed via DT? I guess it could be:
- hardcoded into the driver,
- derived from the panel id,
- maybe read from the panel, this is the best option I guess but I am
not sure if panel provides an API for this.
I have been trying to only use cpu-mode-timings without display-timings for next RFC v4.
As you mentioned, the only required things in display-timings are clock-frequency, hactive and vactive. I put them into cpu-mode-timings, but strictly speaking, cpu-mode-timings is not related with display timing, it is data transmission mode switch timing. And there is no interface for this in drm (display) mode yet. So I'm not sure what is the generic method to treat them.
If the sync-related properties are not used, they should not be specified. I don't really like bundling the timing-related properties with the bus timings though, I would rather make the sync-related properties optional in the display timing node.
However, I'm pretty sure that your panel does have horizontal and vertical blanking, and that information is needed in the display timings to compute the frame rate accurately. The porch and sync length properties might not be needed individually, but their total value is required.
- cpu-mode-timings: CPU interface timings for the connected panel,
and it contains following properties.
Required properties:
- wr-active: clock cycles for the active period of CS enable
in CPU + interface.
Optional properties:
- cs-setup: clock cycles for the active period of address
signal
enable until chip select is enable in CPU interface.
If not specified, the default value(0) will be used.
- wr-setup: clock cycles for the active period of CS signal
enable + until write signal is enable in CPU interface.
If not specified, the default value(0) will be used.
- wr-hold: clock cycles for the active period of CS disable
until + write signal is disable in CPU interface.
If not specified, the default value(0) will be used.
+Optional properties:
+The device node can contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in [2]. This node should describe +panel's video bus.
+[1]: Documentation/devicetree/bindings/video/display-timing.txt +[2]: Documentation/devicetree/bindings/media/video-interfaces.txt
+Example:
- panel@0 {
compatible = "samsung,s6e3fa0";
reg = <0>;
vdd3-supply = <&vcclcd_reg>;
vci-supply = <&vlcd_reg>;
reset-gpios = <&gpy7 4 0>;
det-gpios = <&gpg0 6 0>;
te-gpios = <&gpd1 7 0>;
display-timings {
timing0: timing-0 {
clock-frequency = <0>;
hactive = <1080>;
vactive = <1920>;
hfront-porch = <2>;
hback-porch = <2>;
hsync-len = <1>;
vfront-porch = <1>;
vback-porch = <4>;
vsync-len = <1>;
};
};
cpu-mode-timings {
cs-setup = <0>;
wr-setup = <0>;
wr-active = <1>;
wr-hold = <0>;
};
- };
Hi Laurent,
Thank you for comments.
On 05/08/2014 01:00 AM, Laurent Pinchart wrote:
On Wednesday 07 May 2014 10:05:46 YoungJun Cho wrote:
Hi Andrzej
Thank you for comments.
On 05/05/2014 07:35 PM, Andrzej Hajda wrote:
On 04/27/2014 03:50 AM, YoungJun Cho wrote:
This patch adds DT bindings for s6e3fa0 panel. The bindings describes panel resources, display timings and cpu mode timings.
Changelog v2:
- Adds unit address (commented by Sachin Kamat)
Changelog v3:
- Removes optional delay, size properties (commented by Laurent Pinchart)
- Adds OLED detection, TE gpio properties
Changelog v4:
Moves CPU timings relevant properties from FIMD DT
(commeted by Laurent Pinchart, Andrzej Hajda)
Changelog v5:
- Fixes gpio property names (commented by Andrzej Hajda)
Changelog v6:
Renames CPU timings to CPU mode timings
Modifies CPU mode timings internal properties relevant things
(commeted by Laurent Pinchart, Andrzej Hajda)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com
.../devicetree/bindings/panel/samsung,s6e3fa0.txt | 68 ++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt>> diff --git a/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt new file mode 100644 index 0000000..9f06645 --- /dev/null +++ b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt @@ -0,0 +1,68 @@ +Samsung S6E3FA0 AMOLED LCD 5.7 inch panel
+Required properties:
- compatible: "samsung,s6e3fa0"
- reg: the virtual channel number of a DSI peripheral
- vdd3-supply: core voltage supply
- vci-supply: voltage supply for analog circuits
- reset-gpios: a GPIO spec for the reset pin
- det-gpios: a GPIO spec for the OLED detection pin
- te-gpios: a GPIO spec for the TE pin
- display-timings: timings for the connected panel as described by [1]
This still bothers me, it forces users to provide bunch of fake properties (four porches, two syncs and clock-frequency) just because we need to pass somehow pixel width and height. And do we really need pixel dimension to be passed via DT? I guess it could be:
- hardcoded into the driver,
- derived from the panel id,
- maybe read from the panel, this is the best option I guess but I am
not sure if panel provides an API for this.
I have been trying to only use cpu-mode-timings without display-timings for next RFC v4.
As you mentioned, the only required things in display-timings are clock-frequency, hactive and vactive. I put them into cpu-mode-timings, but strictly speaking, cpu-mode-timings is not related with display timing, it is data transmission mode switch timing. And there is no interface for this in drm (display) mode yet. So I'm not sure what is the generic method to treat them.
If the sync-related properties are not used, they should not be specified. I don't really like bundling the timing-related properties with the bus timings though, I would rather make the sync-related properties optional in the display timing node.
Yes, I agree with you. So in RFC v4, I used cmdmode-display-timings instead of display-timings and cpu-mode-timings. Please refer to http://www.spinics.net/lists/dri-devel/msg58916.html.
However, I'm pretty sure that your panel does have horizontal and vertical blanking, and that information is needed in the display timings to compute the
Yes, this is also right. However the horizontal / vertical blanking and relevant porch times are calculated by command mode panel inside. The command mode panel has a internal graphic ram and shows(refreshes) it by itself. So the only thing the user(display controller) has to do is to transfer video data to update the internal graphic ram in TE signal time.
frame rate accurately. The porch and sync length properties might not be needed individually, but their total value is required.
Yes, the total values are required to drm display mode. So I did like below command mode helper function.
+int drm_display_mode_from_cmdmode(const struct cmdmode *cm, + struct drm_display_mode *dmode) +{ + dmode->hdisplay = cm->hactive; + dmode->htotal = dmode->hsync_end = dmode->hsync_start = dmode->hdisplay; + + dmode->vdisplay = cm->vactive; + dmode->vtotal = dmode->vsync_end = dmode->vsync_start = dmode->vdisplay; + + dmode->clock = cm->pixelclock / 1000; + + dmode->cs_setup = cm->cs_setup; + dmode->wr_setup = cm->wr_setup; + dmode->wr_active = cm->wr_active; + dmode->wr_hold = cm->wr_hold; + + dmode->flags = 0; + drm_mode_set_name(dmode); + + return 0; +}
Please refer to http://www.spinics.net/lists/dri-devel/msg58909.html
Thank you.
Best regards YJ
- cpu-mode-timings: CPU interface timings for the connected panel,
and it contains following properties.
Required properties:
- wr-active: clock cycles for the active period of CS enable
in CPU + interface.
Optional properties:
- cs-setup: clock cycles for the active period of address
signal
enable until chip select is enable in CPU interface.
If not specified, the default value(0) will be used.
- wr-setup: clock cycles for the active period of CS signal
enable + until write signal is enable in CPU interface.
If not specified, the default value(0) will be used.
- wr-hold: clock cycles for the active period of CS disable
until + write signal is disable in CPU interface.
If not specified, the default value(0) will be used.
+Optional properties:
+The device node can contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in [2]. This node should describe +panel's video bus.
+[1]: Documentation/devicetree/bindings/video/display-timing.txt +[2]: Documentation/devicetree/bindings/media/video-interfaces.txt
+Example:
- panel@0 {
compatible = "samsung,s6e3fa0";
reg = <0>;
vdd3-supply = <&vcclcd_reg>;
vci-supply = <&vlcd_reg>;
reset-gpios = <&gpy7 4 0>;
det-gpios = <&gpg0 6 0>;
te-gpios = <&gpd1 7 0>;
display-timings {
timing0: timing-0 {
clock-frequency = <0>;
hactive = <1080>;
vactive = <1920>;
hfront-porch = <2>;
hback-porch = <2>;
hsync-len = <1>;
vfront-porch = <1>;
vback-porch = <4>;
vsync-len = <1>;
};
};
cpu-mode-timings {
cs-setup = <0>;
wr-setup = <0>;
wr-active = <1>;
wr-hold = <0>;
};
- };
This patch adds MIPI-DSI command mode based S6E3FA0 AMOLED LCD Panel driver.
Changelog v2: - Declares delay, size properties in probe routine instead of DT Changelog v3: - Moves CPU timings relevant properties from FIMD DT (commented by Laurent Pinchart, Andrzej Hajda) Changelog v4: - Enhances readability, vddm readi failure case and removes duplicated power off (commented by Andrzej Hajda)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/panel/Kconfig | 7 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-s6e3fa0.c | 595 +++++++++++++++++++++++++++++++++ 3 files changed, 603 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-s6e3fa0.c
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 4ec874d..be1392e 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -30,4 +30,11 @@ config DRM_PANEL_S6E8AA0 select DRM_MIPI_DSI select VIDEOMODE_HELPERS
+config DRM_PANEL_S6E3FA0 + tristate "S6E3FA0 DSI command mode panel" + depends on DRM && DRM_PANEL + depends on OF + select DRM_MIPI_DSI + select VIDEOMODE_HELPERS + endmenu diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 8b92921..85c6738 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o +obj-$(CONFIG_DRM_PANEL_S6E3FA0) += panel-s6e3fa0.o diff --git a/drivers/gpu/drm/panel/panel-s6e3fa0.c b/drivers/gpu/drm/panel/panel-s6e3fa0.c new file mode 100644 index 0000000..ca0cc6b --- /dev/null +++ b/drivers/gpu/drm/panel/panel-s6e3fa0.c @@ -0,0 +1,595 @@ +/* + * MIPI-DSI based s6e3fa0 AMOLED LCD 5.7 inch panel driver. + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * + * YoungJun Cho yj44.cho@samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <drm/drmP.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> + +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> +#include <video/of_videomode.h> +#include <video/videomode.h> + +/* Manufacturer Command Set */ +#define MCS_GLOBAL_PARAMETER 0xb0 +#define MCS_AID 0xb2 +#define MCS_ELVSSOPT 0xb6 +#define MCS_TEMPERATURE_SET 0xb8 +#define MCS_PENTILE_CTRL 0xc0 +#define MCS_GAMMA_MODE 0xca +#define MCS_VDDM 0xd7 +#define MCS_ALS 0xe3 +#define MCS_ERR_FG 0xed +#define MCS_KEY_LEV1 0xf0 +#define MCS_PANEL_UPDATE 0xf7 +#define MCS_KEY_LEV2 0xfc +#define MCS_RE 0xfe +#define MCS_TOUT2_HSYNC 0xff + +/* Content Adaptive Brightness Control */ +#define DCS_WRITE_CABC 0x55 + +#define MTP_ID_LEN 3 +#define GAMMA_LEVEL_NUM 30 + +#define DEFAULT_VDDM_VAL 0x15 + +struct s6e3fa0 { + struct device *dev; + struct drm_panel panel; + + struct regulator_bulk_data supplies[2]; + struct gpio_desc *reset_gpio; + struct gpio_desc *det_gpio; + struct gpio_desc *te_gpio; + struct videomode vm; + struct drm_panel_cpu_mode_timings cm_timings; + + unsigned int power_on_delay; + unsigned int reset_delay; + unsigned int init_delay; + unsigned int width_mm; + unsigned int height_mm; + + unsigned char id; + unsigned char vddm; + unsigned int brightness; +}; + +#define panel_to_s6e3fa0(p) container_of(p, struct s6e3fa0, panel) + +/* VDD Memory Lookup Table contains paires of {ReadValue, WriteValue} */ +static const unsigned char s6e3fa0_vddm_lut[][2] = { + {0x00, 0x0d}, {0x01, 0x0d}, {0x02, 0x0e}, {0x03, 0x0f}, {0x04, 0x10}, + {0x05, 0x11}, {0x06, 0x12}, {0x07, 0x13}, {0x08, 0x14}, {0x09, 0x15}, + {0x0a, 0x16}, {0x0b, 0x17}, {0x0c, 0x18}, {0x0d, 0x19}, {0x0e, 0x1a}, + {0x0f, 0x1b}, {0x10, 0x1c}, {0x11, 0x1d}, {0x12, 0x1e}, {0x13, 0x1f}, + {0x14, 0x20}, {0x15, 0x21}, {0x16, 0x22}, {0x17, 0x23}, {0x18, 0x24}, + {0x19, 0x25}, {0x1a, 0x26}, {0x1b, 0x27}, {0x1c, 0x28}, {0x1d, 0x29}, + {0x1e, 0x2a}, {0x1f, 0x2b}, {0x20, 0x2c}, {0x21, 0x2d}, {0x22, 0x2e}, + {0x23, 0x2f}, {0x24, 0x30}, {0x25, 0x31}, {0x26, 0x32}, {0x27, 0x33}, + {0x28, 0x34}, {0x29, 0x35}, {0x2a, 0x36}, {0x2b, 0x37}, {0x2c, 0x38}, + {0x2d, 0x39}, {0x2e, 0x3a}, {0x2f, 0x3b}, {0x30, 0x3c}, {0x31, 0x3d}, + {0x32, 0x3e}, {0x33, 0x3f}, {0x34, 0x3f}, {0x35, 0x3f}, {0x36, 0x3f}, + {0x37, 0x3f}, {0x38, 0x3f}, {0x39, 0x3f}, {0x3a, 0x3f}, {0x3b, 0x3f}, + {0x3c, 0x3f}, {0x3d, 0x3f}, {0x3e, 0x3f}, {0x3f, 0x3f}, {0x40, 0x0c}, + {0x41, 0x0b}, {0x42, 0x0a}, {0x43, 0x09}, {0x44, 0x08}, {0x45, 0x07}, + {0x46, 0x06}, {0x47, 0x05}, {0x48, 0x04}, {0x49, 0x03}, {0x4a, 0x02}, + {0x4b, 0x01}, {0x4c, 0x40}, {0x4d, 0x41}, {0x4e, 0x42}, {0x4f, 0x43}, + {0x50, 0x44}, {0x51, 0x45}, {0x52, 0x46}, {0x53, 0x47}, {0x54, 0x48}, + {0x55, 0x49}, {0x56, 0x4a}, {0x57, 0x4b}, {0x58, 0x4c}, {0x59, 0x4d}, + {0x5a, 0x4e}, {0x5b, 0x4f}, {0x5c, 0x50}, {0x5d, 0x51}, {0x5e, 0x52}, + {0x5f, 0x53}, {0x60, 0x54}, {0x61, 0x55}, {0x62, 0x56}, {0x63, 0x57}, + {0x64, 0x58}, {0x65, 0x59}, {0x66, 0x5a}, {0x67, 0x5b}, {0x68, 0x5c}, + {0x69, 0x5d}, {0x6a, 0x5e}, {0x6b, 0x5f}, {0x6c, 0x60}, {0x6d, 0x61}, + {0x6e, 0x62}, {0x6f, 0x63}, {0x70, 0x64}, {0x71, 0x65}, {0x72, 0x66}, + {0x73, 0x67}, {0x74, 0x68}, {0x75, 0x69}, {0x76, 0x6a}, {0x77, 0x6b}, + {0x78, 0x6c}, {0x79, 0x6d}, {0x7a, 0x6e}, {0x7b, 0x6f}, {0x7c, 0x70}, + {0x7d, 0x71}, {0x7e, 0x72}, {0x7f, 0x73}, +}; + +static int s6e3fa0_dcs_read(struct s6e3fa0 *ctx, unsigned char cmd, + void *data, size_t len) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + + return mipi_dsi_dcs_read(dsi, dsi->channel, cmd, data, len); +} + +static void s6e3fa0_dcs_write(struct s6e3fa0 *ctx, const void *data, size_t len) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + + mipi_dsi_dcs_write(dsi, dsi->channel, data, len); +} + +#define s6e3fa0_dcs_write_seq(ctx, seq...) \ +do { \ + const unsigned char d[] = { seq }; \ + BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "too big seq for stack"); \ + s6e3fa0_dcs_write(ctx, d, ARRAY_SIZE(d)); \ +} while (0) + +#define s6e3fa0_dcs_write_seq_static(ctx, seq...) \ +do { \ + static const unsigned char d[] = { seq }; \ + s6e3fa0_dcs_write(ctx, d, ARRAY_SIZE(d)); \ +} while (0) + +static void s6e3fa0_apply_level_1_key(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, MCS_KEY_LEV1, 0x5a, 0x5a); +} + +static void s6e3fa0_apply_level_2_key(struct s6e3fa0 *ctx, bool on) +{ + if (on) + s6e3fa0_dcs_write_seq_static(ctx, MCS_KEY_LEV2, 0x5a, 0x5a); + else + s6e3fa0_dcs_write_seq_static(ctx, MCS_KEY_LEV2, 0xa5, 0xa5); +} + +static void s6e3fa0_set_maximum_return_packet_size(struct s6e3fa0 *ctx, + unsigned int size) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + const struct mipi_dsi_host_ops *ops = dsi->host->ops; + + if (ops && ops->transfer) { + unsigned char buf[] = {size, 0}; + struct mipi_dsi_msg msg = { + .channel = dsi->channel, + .type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, + .tx_len = sizeof(buf), + .tx_buf = buf + }; + + ops->transfer(dsi->host, &msg); + } +} + +static void s6e3fa0_read_mtp_id(struct s6e3fa0 *ctx) +{ + unsigned char id[MTP_ID_LEN]; + int ret; + + s6e3fa0_set_maximum_return_packet_size(ctx, MTP_ID_LEN); + ret = s6e3fa0_dcs_read(ctx, MIPI_DCS_GET_DISPLAY_ID, id, MTP_ID_LEN); + if (ret < MTP_ID_LEN || id[0] == 0x00) { + dev_err(ctx->dev, "failed to read id\n"); + return; + } + + dev_info(ctx->dev, "ID: 0x%02x, 0x%02x, 0x%02x\n", id[0], id[1], id[2]); + + ctx->id = id[2]; +} + +static void s6e3fa0_read_vddm(struct s6e3fa0 *ctx) +{ + unsigned char vddm; + int ret; + + s6e3fa0_apply_level_2_key(ctx, true); + s6e3fa0_dcs_write_seq_static(ctx, MCS_GLOBAL_PARAMETER, 0x16); + s6e3fa0_set_maximum_return_packet_size(ctx, 1); + ret = s6e3fa0_dcs_read(ctx, MCS_VDDM, &vddm, 1); + s6e3fa0_apply_level_2_key(ctx, false); + + if (ret < 1 || vddm > 0x7f) { + dev_err(ctx->dev, "failed to read vddm, use default val.\n"); + vddm = DEFAULT_VDDM_VAL; + } + + ctx->vddm = s6e3fa0_vddm_lut[vddm][1]; +} + +static void s6e3fa0_set_pentile_control(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, MCS_PENTILE_CTRL, 0x00, 0x02, 0x03, + 0x32, 0x03, 0x44, 0x44, 0xc0, 0x00, + 0x1c, 0x20, 0xe8); +} + +static void s6e3fa0_write_ambient_light_sensor(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, MCS_ALS, 0xff, 0xff, 0xff, 0xff); +} + +static void s6e3fa0_set_readability_enhancement(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, MCS_RE, 0x00, 0x03, + MCS_GLOBAL_PARAMETER, 0x2b, + MCS_RE, 0xe4); +} + +static void s6e3fa0_set_common_control(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_pentile_control(ctx); + s6e3fa0_write_ambient_light_sensor(ctx); + s6e3fa0_set_readability_enhancement(ctx); +} + +static void s6e3fa0_set_gamma(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, MCS_GAMMA_MODE, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x00, 0x00); +} + +static void s6e3fa0_set_amoled_impulse_driving(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, MCS_AID, 0x00, 0x06, 0x00, 0x06); +} + +static void s6e3fa0_set_elvss(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, MCS_ELVSSOPT, 0x88, 0x0a); +} + +static void s6e3fa0_update_panel(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, MCS_PANEL_UPDATE, 0x03); +} + +static void s6e3fa0_write_automatic_current_limit(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, DCS_WRITE_CABC, 0x02); +} + +static void s6e3fa0_set_brightness_control(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_gamma(ctx); + s6e3fa0_set_amoled_impulse_driving(ctx); + s6e3fa0_set_elvss(ctx); + s6e3fa0_update_panel(ctx); + s6e3fa0_write_automatic_current_limit(ctx); +} + +static void s6e3fa0_set_temperature(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, MCS_GLOBAL_PARAMETER, 0x05, + MCS_TEMPERATURE_SET, 0x19); +} + +static void s6e3fa0_set_elvss_control(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_temperature(ctx); + s6e3fa0_set_elvss(ctx); +} + +static void s6e3fa0_set_te_on(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_SET_TEAR_ON, 0x00); +} + +static void s6e3fa0_set_etc_and_write_vddm(struct s6e3fa0 *ctx) +{ + s6e3fa0_apply_level_2_key(ctx, true); + s6e3fa0_dcs_write_seq(ctx, MCS_ERR_FG, 0x0c, 0x04, + MCS_TOUT2_HSYNC, 0x01, + MCS_GLOBAL_PARAMETER, 0x16, + MCS_VDDM, ctx->vddm); + s6e3fa0_apply_level_2_key(ctx, false); +} + +static void s6e3fa0_set_etc_condition(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_te_on(ctx); + s6e3fa0_set_etc_and_write_vddm(ctx); +} + +static void s6e3fa0_panel_init(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_common_control(ctx); + s6e3fa0_set_brightness_control(ctx); + s6e3fa0_set_elvss_control(ctx); + s6e3fa0_set_etc_condition(ctx); + + msleep(ctx->init_delay); +} + +static int s6e3fa0_power_off(struct s6e3fa0 *ctx) +{ + gpiod_set_value(ctx->reset_gpio, 0); + + return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); +} + +static int s6e3fa0_power_on(struct s6e3fa0 *ctx) +{ + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + if (ret) + return ret; + + msleep(ctx->power_on_delay); + + gpiod_set_value(ctx->reset_gpio, 1); + gpiod_set_value(ctx->reset_gpio, 0); + usleep_range(1000, 2000); + gpiod_set_value(ctx->reset_gpio, 1); + + msleep(ctx->reset_delay); + + return 0; +} + +static void s6e3fa0_set_sequence(struct s6e3fa0 *ctx) +{ + s6e3fa0_apply_level_1_key(ctx); + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); + msleep(20); + + s6e3fa0_read_mtp_id(ctx); + s6e3fa0_read_vddm(ctx); + + s6e3fa0_panel_init(ctx); + + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON); +} + +static int s6e3fa0_disable(struct drm_panel *panel) +{ + struct s6e3fa0 *ctx = panel_to_s6e3fa0(panel); + + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE); + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF); + msleep(35); + + return s6e3fa0_power_off(ctx); +} + +static int s6e3fa0_enable(struct drm_panel *panel) +{ + struct s6e3fa0 *ctx = panel_to_s6e3fa0(panel); + int ret; + + ret = s6e3fa0_power_on(ctx); + if (ret) + return ret; + + s6e3fa0_set_sequence(ctx); + + return ret; +} + +static int s6e3fa0_get_modes(struct drm_panel *panel) +{ + struct drm_connector *connector = panel->connector; + struct s6e3fa0 *ctx = panel_to_s6e3fa0(panel); + struct drm_display_mode *mode; + + mode = drm_mode_create(connector->dev); + if (!mode) { + DRM_ERROR("failed to create a new display mode\n"); + return 0; + } + + drm_display_mode_from_videomode(&ctx->vm, mode); + mode->width_mm = ctx->width_mm; + mode->height_mm = ctx->height_mm; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + mode->private = (void *)&ctx->cm_timings; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs s6e3fa0_drm_funcs = { + .disable = s6e3fa0_disable, + .enable = s6e3fa0_enable, + .get_modes = s6e3fa0_get_modes, +}; + +static int s6e3fa0_parse_dt(struct s6e3fa0 *ctx) +{ + struct device *dev = ctx->dev; + struct device_node *np = dev->of_node, *cm_timings_np; + struct drm_panel_cpu_mode_timings *cm_timings = &ctx->cm_timings; + int ret; + + ret = of_get_videomode(np, &ctx->vm, 0); + if (ret) { + dev_err(dev, "failed to get video mode: %d\n", ret); + return ret; + } + + cm_timings_np = of_find_node_by_name(np, "cpu-mode-timings"); + if (!cm_timings_np) { + dev_err(dev, "failed to get cpu mode timings\n"); + return -EINVAL; + } + if (of_property_read_u32(cm_timings_np, "wr-active", + &cm_timings->wr_active)) { + dev_err(dev, "failed to get wr-active timings\n"); + return -EINVAL; + } + if (of_property_read_u32(cm_timings_np, "cs-setup", + &cm_timings->cs_setup)) + cm_timings->cs_setup = 0; + if (of_property_read_u32(cm_timings_np, "wr-setup", + &cm_timings->wr_setup)) + cm_timings->wr_setup = 0; + if (of_property_read_u32(cm_timings_np, "wr-hold", + &cm_timings->wr_hold)) + cm_timings->wr_hold = 0; + of_node_put(cm_timings_np); + + return 0; +} + +irqreturn_t s6e3fa0_det_interrupt(int irq, void *dev_id) +{ + /* TODO */ + return IRQ_HANDLED; +} + +irqreturn_t s6e3fa0_te_interrupt(int irq, void *dev_id) +{ + struct s6e3fa0 *ctx = dev_id; + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + struct mipi_dsi_host *host = dsi->host; + const struct mipi_dsi_host_ops *ops = host->ops; + + if (ops && ops->te_handler) + ops->te_handler(host); + + return IRQ_HANDLED; +} + +static int s6e3fa0_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct s6e3fa0 *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(struct s6e3fa0), GFP_KERNEL); + if (!ctx) { + dev_err(dev, "failed to allocate s6e3fa0 structure.\n"); + return -ENOMEM; + } + + mipi_dsi_set_drvdata(dsi, ctx); + + ctx->dev = dev; + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + + ret = s6e3fa0_parse_dt(ctx); + if (ret) + return ret; + + ctx->supplies[0].supply = "vdd3"; + ctx->supplies[1].supply = "vci"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), + ctx->supplies); + if (ret) { + dev_err(dev, "failed to get regulators: %d\n", ret); + return ret; + } + + ctx->reset_gpio = devm_gpiod_get(dev, "reset"); + if (IS_ERR(ctx->reset_gpio)) { + dev_err(dev, "failed to get reset gpio: %ld\n", + PTR_ERR(ctx->reset_gpio)); + return PTR_ERR(ctx->reset_gpio); + } + ret = gpiod_direction_output(ctx->reset_gpio, 1); + if (ret < 0) { + dev_err(dev, "failed to configure reset gpio: %d\n", ret); + return ret; + } + + ctx->det_gpio = devm_gpiod_get(dev, "det"); + if (IS_ERR(ctx->det_gpio)) { + dev_err(dev, "failed to get det gpio: %ld\n", + PTR_ERR(ctx->det_gpio)); + return PTR_ERR(ctx->det_gpio); + } + ret = gpiod_direction_input(ctx->det_gpio); + if (ret < 0) { + dev_err(dev, "failed to configure det gpio: %d\n", ret); + return ret; + } + ret = devm_request_irq(dev, gpiod_to_irq(ctx->det_gpio), + s6e3fa0_det_interrupt, + IRQF_TRIGGER_FALLING, + "oled-det", ctx); + if (ret) { + dev_err(dev, "failed to request det irq: %d\n", ret); + return ret; + } + + ctx->te_gpio = devm_gpiod_get(dev, "te"); + if (IS_ERR(ctx->te_gpio)) { + dev_err(dev, "failed to get te gpio: %ld\n", + PTR_ERR(ctx->te_gpio)); + return PTR_ERR(ctx->te_gpio); + } + ret = gpiod_direction_input(ctx->te_gpio); + if (ret < 0) { + dev_err(dev, "failed to configure te gpio: %d\n", ret); + return ret; + } + ret = devm_request_irq(dev, gpiod_to_irq(ctx->te_gpio), + s6e3fa0_te_interrupt, + IRQF_TRIGGER_RISING, + "TE", ctx); + if (ret) { + dev_err(dev, "failed to request det irq: %d\n", ret); + return ret; + } + + ctx->power_on_delay = 10; + ctx->reset_delay = 5; + ctx->init_delay = 120; + ctx->width_mm = 71; + ctx->height_mm = 126; + + ctx->brightness = GAMMA_LEVEL_NUM - 1; + + drm_panel_init(&ctx->panel); + ctx->panel.dev = dev; + ctx->panel.funcs = &s6e3fa0_drm_funcs; + + ret = drm_panel_add(&ctx->panel); + if (ret) + return ret; + + ret = mipi_dsi_attach(dsi); + if (ret) + drm_panel_remove(&ctx->panel); + + return ret; +} + +static int s6e3fa0_remove(struct mipi_dsi_device *dsi) +{ + struct s6e3fa0 *ctx = mipi_dsi_get_drvdata(dsi); + + mipi_dsi_detach(dsi); + drm_panel_remove(&ctx->panel); + + return 0; +} + +static struct of_device_id s6e3fa0_of_match[] = { + { .compatible = "samsung,s6e3fa0" }, + { } +}; +MODULE_DEVICE_TABLE(of, s6e3fa0_of_match); + +static struct mipi_dsi_driver s6e3fa0_driver = { + .probe = s6e3fa0_probe, + .remove = s6e3fa0_remove, + .driver = { + .name = "panel_s6e3fa0", + .owner = THIS_MODULE, + .of_match_table = s6e3fa0_of_match, + }, +}; +module_mipi_dsi_driver(s6e3fa0_driver); + +MODULE_AUTHOR("YoungJun Cho yj44.cho@samsung.com"); +MODULE_DESCRIPTION("MIPI-DSI based s6e3fa0 AMOLED LCD Panel Driver"); +MODULE_LICENSE("GPL v2");
This patch adds sysreg property to fimd device node which is required to use I80 interface.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- arch/arm/boot/dts/exynos4.dtsi | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 3d14cdb..a10aa50 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -528,6 +528,7 @@ clocks = <&clock 140>, <&clock 283>; clock-names = "sclk_fimd", "fimd"; samsung,power-domain = <&pd_lcd0>; + samsung,sysreg = <&sys_reg>; status = "disabled"; }; };
This patch adds sysreg device node, and sysreg property to fimd device node which is required to use I80 interface.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- arch/arm/boot/dts/exynos5.dtsi | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5.dtsi b/arch/arm/boot/dts/exynos5.dtsi index 258dca4..f938bbb 100644 --- a/arch/arm/boot/dts/exynos5.dtsi +++ b/arch/arm/boot/dts/exynos5.dtsi @@ -88,12 +88,18 @@ status = "disabled"; };
+ sys_reg: syscon@10050000 { + compatible = "samsung,exynos5-sysreg", "syscon"; + reg = <0x10050000 0x500>; + }; + fimd@14400000 { compatible = "samsung,exynos5250-fimd"; interrupt-parent = <&combiner>; reg = <0x14400000 0x40000>; interrupt-names = "fifo", "vsync", "lcd_sys"; interrupts = <18 4>, <18 5>, <18 6>; + samsung,sysreg = <&sys_reg>; status = "disabled"; };
This patch adds mipi-phy node for MIPI-DSI device.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- arch/arm/boot/dts/exynos5420.dtsi | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index 8db792b..f0184c7 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -416,6 +416,12 @@ phy-names = "dp"; };
+ mipi_phy: video-phy@10040714 { + compatible = "samsung,s5pv210-mipi-video-phy"; + reg = <0x10040714 12>; + #phy-cells = <1>; + }; + fimd@14400000 { samsung,power-domain = <&disp_pd>; clocks = <&clock 147>, <&clock 421>;
This patch adds common part of dsi node.
Changelog v2: - Uses clock macros instead of numbers (commented by Sachin Kamat)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Acked-by: Inki Dae inki.dae@samsung.com Acked-by: Kyungmin Park kyungmin.park@samsung.com --- arch/arm/boot/dts/exynos5420.dtsi | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index f0184c7..f1030f5 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -17,6 +17,7 @@ #include "exynos5420-pinctrl.dtsi"
#include <dt-bindings/clk/exynos-audss-clk.h> +#include <dt-bindings/clock/exynos5420.h>
/ { compatible = "samsung,exynos5420"; @@ -422,6 +423,20 @@ #phy-cells = <1>; };
+ dsi@14500000 { + compatible = "samsung,exynos5420-mipi-dsi"; + reg = <0x14500000 0x10000>; + interrupts = <0 82 0>; + samsung,power-domain = <&disp_pd>; + phys = <&mipi_phy 1>; + phy-names = "dsim"; + clocks = <&clock CLK_DSIM1>, <&clock CLK_SCLK_MIPI1>; + clock-names = "bus_clk", "pll_clk"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + fimd@14400000 { samsung,power-domain = <&disp_pd>; clocks = <&clock 147>, <&clock 421>;
dri-devel@lists.freedesktop.org