Hello,
Here's a set of patches that adds support for the DU found in the R8A7790 SoC.
Compared to the R8A7779 DU, the R8A7790 has a third CRTC, internal LVDS encoders and different output routing options.
These patches are targetted at v3.12 so there's no rush (but please don't take that as an excuse to skip reviewing them :-)).
Laurent Pinchart (24): drm/rcar-du: Add missing alpha plane register definitions drm/rcar-du: Use devm_ioremap_resource() drm/rcar-du: Add platform module device table drm/rcar-du: Don't ignore rcar_du_crtc_create() return value drm/rcar-du: Support per-CRTC clock and IRQ drm/rcar-du: Fix buffer pitch alignment drm/rcar-du: Clarify comment regarding plane Y source coordinate drm/rcar-du: Split LVDS encoder and connector drm/rcar-du: Split VGA encoder and connector drm/rcar-du: Merge LVDS and VGA encoder code drm/rcar-du: Rename platform data fields to match what they describe drm/rcar-du: Create rcar_du_planes structure drm/rcar-du: Rename rcar_du_plane_(init|register) to rcar_du_planes_* drm/rcar-du: Introduce CRTCs groups drm/rcar-du: Use dynamic number of CRTCs instead of CRTCs array size drm/rcar-du: Remove register definitions for the second channel drm/rcar-du: Move output routing configuration to group drm/rcar-du: Add support for the R8A7790 DU drm/rcar-du: Fix buffer pitch alignment for R8A7790 DU drm/rcar-du: Add support for multiple groups drm/rcar-du: Add support for DEFR8 register drm/rcar-du: Rework output routing support drm/rcar-du: Configure RGB output routing to DPAD0 drm/rcar-du: Add internal LVDS encoder support
drivers/gpu/drm/rcar-du/Kconfig | 7 + drivers/gpu/drm/rcar-du/Makefile | 10 +- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 255 ++++++++++++++++-------------- drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 13 +- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 169 ++++++++------------ drivers/gpu/drm/rcar-du/rcar_du_drv.h | 61 +++++-- drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 202 +++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 49 ++++++ drivers/gpu/drm/rcar-du/rcar_du_group.c | 187 ++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_group.h | 50 ++++++ drivers/gpu/drm/rcar-du/rcar_du_kms.c | 135 +++++++++------- drivers/gpu/drm/rcar-du/rcar_du_kms.h | 32 +--- drivers/gpu/drm/rcar-du/rcar_du_lvds.c | 216 ------------------------- drivers/gpu/drm/rcar-du/rcar_du_lvds.h | 24 --- drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | 130 +++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h | 25 +++ drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c | 194 +++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h | 46 ++++++ drivers/gpu/drm/rcar-du/rcar_du_plane.c | 170 ++++++++++---------- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 26 ++- drivers/gpu/drm/rcar-du/rcar_du_regs.h | 94 +++++++++-- drivers/gpu/drm/rcar-du/rcar_du_vga.c | 149 ----------------- drivers/gpu/drm/rcar-du/rcar_du_vga.h | 24 --- drivers/gpu/drm/rcar-du/rcar_du_vgacon.c | 96 +++++++++++ drivers/gpu/drm/rcar-du/rcar_du_vgacon.h | 23 +++ drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | 69 ++++++++ include/linux/platform_data/rcar-du.h | 34 +++- 27 files changed, 1639 insertions(+), 851 deletions(-) create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_encoder.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_encoder.h create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_group.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_group.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvds.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvds.h create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vga.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vga.h create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vgacon.h create mode 100644 drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
Several alpha plane register definitions are missing, add them.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_regs.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h index 69f21f1..3aba27f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h @@ -349,10 +349,25 @@ #define APnMR_BM_AD (2 << 4) /* Auto Display Change Mode */
#define APnMWR 0x0a104 + +#define APnDSXR 0x0a110 +#define APnDSYR 0x0a114 +#define APnDPXR 0x0a118 +#define APnDPYR 0x0a11c + #define APnDSA0R 0x0a120 #define APnDSA1R 0x0a124 #define APnDSA2R 0x0a128 + +#define APnSPXR 0x0a130 +#define APnSPYR 0x0a134 +#define APnWASPR 0x0a138 +#define APnWAMWR 0x0a13c + +#define APnBTR 0x0a140 + #define APnMLR 0x0a150 +#define APnSWAPR 0x0a180
/* ----------------------------------------------------------------------------- * Display Capture Registers
Replace the devm_request_mem_region() and devm_ioremap_nocache() calls with devm_ioremap_resource().
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 003b34e..24ab0ca 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -107,7 +107,6 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) struct platform_device *pdev = dev->platformdev; struct rcar_du_platform_data *pdata = pdev->dev.platform_data; struct rcar_du_device *rcdu; - struct resource *ioarea; struct resource *mem; int ret;
@@ -129,24 +128,9 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
/* I/O resources and clocks */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (mem == NULL) { - dev_err(&pdev->dev, "failed to get memory resource\n"); - return -EINVAL; - } - - ioarea = devm_request_mem_region(&pdev->dev, mem->start, - resource_size(mem), pdev->name); - if (ioarea == NULL) { - dev_err(&pdev->dev, "failed to request memory region\n"); - return -EBUSY; - } - - rcdu->mmio = devm_ioremap_nocache(&pdev->dev, ioarea->start, - resource_size(ioarea)); - if (rcdu->mmio == NULL) { - dev_err(&pdev->dev, "failed to remap memory resource\n"); + rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(rcdu->mmio)) return -ENOMEM; - }
rcdu->clock = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(rcdu->clock)) {
Hello.
On 27-06-2013 13:49, Laurent Pinchart wrote:
Replace the devm_request_mem_region() and devm_ioremap_nocache() calls with devm_ioremap_resource().
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com
drivers/gpu/drm/rcar-du/rcar_du_drv.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 003b34e..24ab0ca 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
[...]
@@ -129,24 +128,9 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
/* I/O resources and clocks */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
[...]
- rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(rcdu->mmio)) return -ENOMEM;
You should return PTR_ERR(rcdu->mmio).
WBR, Sergei
Hi Sergei,
On Thursday 27 June 2013 17:04:45 Sergei Shtylyov wrote:
On 27-06-2013 13:49, Laurent Pinchart wrote:
Replace the devm_request_mem_region() and devm_ioremap_nocache() calls with devm_ioremap_resource().
Signed-off-by: Laurent Pinchart
laurent.pinchart+renesas@ideasonboard.com
drivers/gpu/drm/rcar-du/rcar_du_drv.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 003b34e..24ab0ca 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
[...]
@@ -129,24 +128,9 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)> /* I/O resources and clocks */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
[...]
rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(rcdu->mmio))
return -ENOMEM;
You should return PTR_ERR(rcdu->mmio).
Good point, I'll fix that. Thank you.
The platform device id driver data field points to a device information structure that only contains a (currently empty) features field for now. Support for additional model-dependent features will be added later.
Only the R8A7779 variant is currently supported.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 13 +++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_drv.h | 15 +++++++++++++++ 2 files changed, 28 insertions(+)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 24ab0ca..d705990 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -123,6 +123,7 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
rcdu->dev = &pdev->dev; rcdu->pdata = pdata; + rcdu->info = (struct rcar_du_device_info *)pdev->id_entry->driver_data; rcdu->ddev = dev; dev->dev_private = rcdu;
@@ -292,6 +293,17 @@ static int rcar_du_remove(struct platform_device *pdev) return 0; }
+static const struct rcar_du_device_info rcar_du_r8a7779_info = { + .features = 0, +}; + +static const struct platform_device_id rcar_du_id_table[] = { + { "rcar-du-r8a7779", (kernel_ulong_t)&rcar_du_r8a7779_info }, + { } +}; + +MODULE_DEVICE_TABLE(platform, rcar_du_id_table); + static struct platform_driver rcar_du_platform_driver = { .probe = rcar_du_probe, .remove = rcar_du_remove, @@ -300,6 +312,7 @@ static struct platform_driver rcar_du_platform_driver = { .name = "rcar-du", .pm = &rcar_du_pm_ops, }, + .id_table = rcar_du_id_table, };
module_platform_driver(rcar_du_platform_driver); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 193cc59..06dbf4f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -25,9 +25,18 @@ struct clk; struct device; struct drm_device;
+/* + * struct rcar_du_device_info - DU model-specific information + * @features: device features (RCAR_DU_FEATURE_*) + */ +struct rcar_du_device_info { + unsigned int features; +}; + struct rcar_du_device { struct device *dev; const struct rcar_du_platform_data *pdata; + const struct rcar_du_device_info *info;
void __iomem *mmio; struct clk *clock; @@ -50,6 +59,12 @@ struct rcar_du_device { } planes; };
+static inline bool rcar_du_has(struct rcar_du_device *rcdu, + unsigned int feature) +{ + return rcdu->info->features & feature; +} + int rcar_du_get(struct rcar_du_device *rcdu); void rcar_du_put(struct rcar_du_device *rcdu);
Handle error cases correctly.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 9c63f39..06cacf6 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -191,8 +191,11 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) if (ret < 0) return ret;
- for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) - rcar_du_crtc_create(rcdu, i); + for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) { + ret = rcar_du_crtc_create(rcdu, i); + if (ret < 0) + return ret; + }
rcdu->used_crtcs = 0; rcdu->num_crtcs = i;
Some of the DU revisions use one clock and IRQ per CRTC instead of one clock and IRQ per device. Retrieve the correct clock and register the correct IRQ for each CRTC.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 120 +++++++++++++++++++++++++-------- drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 2 +- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 52 +++----------- drivers/gpu/drm/rcar-du/rcar_du_drv.h | 3 +- 4 files changed, 103 insertions(+), 74 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 24183fb..aefc8a0 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -69,6 +69,30 @@ static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg, rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set); }
+static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc) +{ + struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + int ret; + + ret = clk_prepare_enable(rcrtc->clock); + if (ret < 0) + return ret; + + ret = rcar_du_get(rcdu); + if (ret < 0) + clk_disable_unprepare(rcrtc->clock); + + return ret; +} + +static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) +{ + struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + + rcar_du_put(rcdu); + clk_disable_unprepare(rcrtc->clock); +} + static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) { struct drm_crtc *crtc = &rcrtc->crtc; @@ -79,7 +103,7 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) u32 div;
/* Dot clock */ - clk = clk_get_rate(rcdu->clock); + clk = clk_get_rate(rcrtc->clock); div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000); div = clamp(div, 1U, 64U) - 1;
@@ -313,20 +337,16 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; - rcar_du_crtc_stop(rcrtc); - rcar_du_put(rcdu); + rcar_du_crtc_put(rcrtc); }
void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; - if (rcrtc->dpms != DRM_MODE_DPMS_ON) return;
- rcar_du_get(rcdu); + rcar_du_crtc_get(rcrtc); rcar_du_crtc_start(rcrtc); }
@@ -340,18 +360,17 @@ static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc)
static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode) { - struct rcar_du_device *rcdu = crtc->dev->dev_private; struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
if (rcrtc->dpms == mode) return;
if (mode == DRM_MODE_DPMS_ON) { - rcar_du_get(rcdu); + rcar_du_crtc_get(rcrtc); rcar_du_crtc_start(rcrtc); } else { rcar_du_crtc_stop(rcrtc); - rcar_du_put(rcdu); + rcar_du_crtc_put(rcrtc); }
rcrtc->dpms = mode; @@ -367,13 +386,12 @@ static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc,
static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc) { - struct rcar_du_device *rcdu = crtc->dev->dev_private; struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
/* We need to access the hardware during mode set, acquire a reference - * to the DU. + * to the CRTC. */ - rcar_du_get(rcdu); + rcar_du_crtc_get(rcrtc);
/* Stop the CRTC and release the plane. Force the DPMS mode to off as a * result. @@ -423,10 +441,10 @@ static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
error: /* There's no rollback/abort operation to clean up in case of error. We - * thus need to release the reference to the DU acquired in prepare() + * thus need to release the reference to the CRTC acquired in prepare() * here. */ - rcar_du_put(rcdu); + rcar_du_crtc_put(rcrtc); return ret; }
@@ -514,6 +532,24 @@ static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc) drm_vblank_put(dev, rcrtc->index); }
+static irqreturn_t rcar_du_crtc_irq(int irq, void *arg) +{ + struct rcar_du_crtc *rcrtc = arg; + irqreturn_t ret = IRQ_NONE; + u32 status; + + status = rcar_du_crtc_read(rcrtc, DSSR); + rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK); + + if (status & DSSR_VBK) { + drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index); + rcar_du_crtc_finish_page_flip(rcrtc); + ret = IRQ_HANDLED; + } + + return ret; +} + static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event) @@ -551,10 +587,29 @@ static const struct drm_crtc_funcs crtc_funcs = {
int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index) { + struct platform_device *pdev = to_platform_device(rcdu->dev); struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index]; struct drm_crtc *crtc = &rcrtc->crtc; + unsigned int irqflags; + char clk_name[5]; + char *name; + int irq; int ret;
+ /* Get the CRTC clock. */ + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { + sprintf(clk_name, "du.%u", index); + name = clk_name; + } else { + name = NULL; + } + + rcrtc->clock = devm_clk_get(rcdu->dev, name); + if (IS_ERR(rcrtc->clock)) { + dev_err(rcdu->dev, "no clock for CRTC %u\n", index); + return PTR_ERR(rcrtc->clock); + } + rcrtc->mmio_offset = index ? DISP2_REG_OFFSET : 0; rcrtc->index = index; rcrtc->dpms = DRM_MODE_DPMS_OFF; @@ -568,6 +623,28 @@ int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index)
drm_crtc_helper_add(crtc, &crtc_helper_funcs);
+ /* Register the interrupt handler. */ + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { + irq = platform_get_irq(pdev, index); + irqflags = 0; + } else { + irq = platform_get_irq(pdev, 0); + irqflags = IRQF_SHARED; + } + + if (irq < 0) { + dev_err(rcdu->dev, "no IRQ for CRTC %u\n", index); + return ret; + } + + ret = devm_request_irq(rcdu->dev, irq, rcar_du_crtc_irq, irqflags, + dev_name(rcdu->dev), rcrtc); + if (ret < 0) { + dev_err(rcdu->dev, + "failed to register IRQ for CRTC %u\n", index); + return ret; + } + return 0; }
@@ -580,16 +657,3 @@ void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable) rcar_du_crtc_clr(rcrtc, DIER, DIER_VBE); } } - -void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc) -{ - u32 status; - - status = rcar_du_crtc_read(rcrtc, DSSR); - rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK); - - if (status & DSSR_VBK) { - drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index); - rcar_du_crtc_finish_page_flip(rcrtc); - } -} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 2a0365b..5b69e98 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -25,6 +25,7 @@ struct rcar_du_plane; struct rcar_du_crtc { struct drm_crtc crtc;
+ struct clk *clock; unsigned int mmio_offset; unsigned int index; bool started; @@ -38,7 +39,6 @@ struct rcar_du_crtc {
int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index); void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable); -void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, struct drm_file *file); void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index d705990..2a85056 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -35,8 +35,8 @@ /* * rcar_du_get - Acquire a reference to the DU * - * Acquiring a reference enables the device clock and setup core registers. A - * reference must be held before accessing any hardware registers. + * Acquiring the first reference setups core registers. A reference must be + * held before accessing any hardware registers. * * This function must be called with the DRM mode_config lock held. * @@ -44,16 +44,9 @@ */ int rcar_du_get(struct rcar_du_device *rcdu) { - int ret; - if (rcdu->use_count) goto done;
- /* Enable clocks before accessing the hardware. */ - ret = clk_prepare_enable(rcdu->clock); - if (ret < 0) - return ret; - /* Enable extended features */ rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE); rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G); @@ -74,16 +67,11 @@ done: /* * rcar_du_put - Release a reference to the DU * - * Releasing the last reference disables the device clock. - * * This function must be called with the DRM mode_config lock held. */ void rcar_du_put(struct rcar_du_device *rcdu) { - if (--rcdu->use_count) - return; - - clk_disable_unprepare(rcdu->clock); + --rcdu->use_count; }
/* ----------------------------------------------------------------------------- @@ -95,8 +83,8 @@ static int rcar_du_unload(struct drm_device *dev) drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); drm_vblank_cleanup(dev); - drm_irq_uninstall(dev);
+ dev->irq_enabled = 0; dev->dev_private = NULL;
return 0; @@ -127,18 +115,12 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) rcdu->ddev = dev; dev->dev_private = rcdu;
- /* I/O resources and clocks */ + /* I/O resources */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(rcdu->mmio)) return -ENOMEM;
- rcdu->clock = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(rcdu->clock)) { - dev_err(&pdev->dev, "failed to get clock\n"); - return -ENOENT; - } - /* DRM/KMS objects */ ret = rcar_du_modeset_init(rcdu); if (ret < 0) { @@ -146,18 +128,14 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) goto done; }
- /* IRQ and vblank handling */ + /* vblank handling */ ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1); if (ret < 0) { dev_err(&pdev->dev, "failed to initialize vblank\n"); goto done; }
- ret = drm_irq_install(dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to install IRQ handler\n"); - goto done; - } + dev->irq_enabled = 1;
platform_set_drvdata(pdev, rcdu);
@@ -177,18 +155,6 @@ static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file) rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file); }
-static irqreturn_t rcar_du_irq(int irq, void *arg) -{ - struct drm_device *dev = arg; - struct rcar_du_device *rcdu = dev->dev_private; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) - rcar_du_crtc_irq(&rcdu->crtcs[i]); - - return IRQ_HANDLED; -} - static int rcar_du_enable_vblank(struct drm_device *dev, int crtc) { struct rcar_du_device *rcdu = dev->dev_private; @@ -221,12 +187,10 @@ static const struct file_operations rcar_du_fops = { };
static struct drm_driver rcar_du_driver = { - .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET - | DRIVER_PRIME, + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME, .load = rcar_du_load, .unload = rcar_du_unload, .preclose = rcar_du_preclose, - .irq_handler = rcar_du_irq, .get_vblank_counter = drm_vblank_count, .enable_vblank = rcar_du_enable_vblank, .disable_vblank = rcar_du_disable_vblank, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 06dbf4f..7d2320f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -25,6 +25,8 @@ struct clk; struct device; struct drm_device;
+#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */ + /* * struct rcar_du_device_info - DU model-specific information * @features: device features (RCAR_DU_FEATURE_*) @@ -39,7 +41,6 @@ struct rcar_du_device { const struct rcar_du_device_info *info;
void __iomem *mmio; - struct clk *clock; unsigned int use_count;
struct drm_device *ddev;
The DU requires a 16 pixels pitch alignement. Make sure dumb buffers are allocated with the correct pitch, and validate the pitch when creating frame buffers.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 2 +- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 19 ++++++++++++++++++- drivers/gpu/drm/rcar-du/rcar_du_kms.h | 3 +++ 3 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 2a85056..bcda2e0 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -200,7 +200,7 @@ static struct drm_driver rcar_du_driver = { .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = drm_gem_cma_dmabuf_import, .gem_prime_export = drm_gem_cma_dmabuf_export, - .dumb_create = drm_gem_cma_dumb_create, + .dumb_create = rcar_du_dumb_create, .dumb_map_offset = drm_gem_cma_dumb_map_offset, .dumb_destroy = drm_gem_cma_dumb_destroy, .fops = &rcar_du_fops, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 06cacf6..d30c2e2 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -138,11 +138,25 @@ void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) * Frame buffer */
+int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + unsigned int align; + + /* The pitch must be aligned to a 16 pixels boundary. */ + align = 16 * args->bpp / 8; + args->pitch = roundup(max(args->pitch, min_pitch), align); + + return drm_gem_cma_dumb_create(file, dev, args); +} + static struct drm_framebuffer * rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) { const struct rcar_du_format_info *format; + unsigned int align;
format = rcar_du_format_info(mode_cmd->pixel_format); if (format == NULL) { @@ -151,7 +165,10 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, return ERR_PTR(-EINVAL); }
- if (mode_cmd->pitches[0] & 15 || mode_cmd->pitches[0] >= 8192) { + align = 16 * format->bpp / 8; + + if (mode_cmd->pitches[0] & (align - 1) || + mode_cmd->pitches[0] >= 8192) { dev_dbg(dev->dev, "invalid pitch value %u\n", mode_cmd->pitches[0]); return ERR_PTR(-EINVAL); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h index e4d8db0..dba4722 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h @@ -56,4 +56,7 @@ void rcar_du_encoder_mode_commit(struct drm_encoder *encoder);
int rcar_du_modeset_init(struct rcar_du_device *rcdu);
+int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args); + #endif /* __RCAR_DU_KMS_H__ */
The R8A7790 DU documentation contains further information regarding the plane Y source coordinate. Update the comment accordingly.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index a65f81d..38ebd20 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -103,9 +103,12 @@ void rcar_du_plane_update_base(struct rcar_du_plane *plane) struct rcar_du_device *rcdu = plane->dev; unsigned int index = plane->hwindex;
- /* According to the datasheet the Y position is expressed in raster line - * units. However, 32bpp formats seem to require a doubled Y position - * value. Similarly, for the second plane, NV12 and NV21 formats seem to + /* The Y position is expressed in raster line units and must be doubled + * for 32bpp formats, according to the R8A7790 datasheet. No mention of + * doubling the Y position is found in the R8A7779 datasheet, but the + * rule seems to apply there as well. + * + * Similarly, for the second plane, NV12 and NV21 formats seem to * require a halved Y position value. */ rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x);
This prepares for the encoders rework.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/Makefile | 1 + drivers/gpu/drm/rcar-du/rcar_du_lvds.c | 120 +-------------------------- drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | 129 ++++++++++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h | 25 ++++++ 4 files changed, 157 insertions(+), 118 deletions(-) create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile index 7333c00..5def510 100644 --- a/drivers/gpu/drm/rcar-du/Makefile +++ b/drivers/gpu/drm/rcar-du/Makefile @@ -2,6 +2,7 @@ rcar-du-drm-y := rcar_du_crtc.o \ rcar_du_drv.o \ rcar_du_kms.o \ rcar_du_lvds.o \ + rcar_du_lvdscon.o \ rcar_du_plane.o \ rcar_du_vga.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.c b/drivers/gpu/drm/rcar-du/rcar_du_lvds.c index 7aefe72..82e5157 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvds.c @@ -1,5 +1,5 @@ /* - * rcar_du_lvds.c -- R-Car Display Unit LVDS Encoder and Connector + * rcar_du_lvds.c -- R-Car Display Unit LVDS Encoder * * Copyright (C) 2013 Renesas Corporation * @@ -18,123 +18,7 @@ #include "rcar_du_drv.h" #include "rcar_du_kms.h" #include "rcar_du_lvds.h" - -struct rcar_du_lvds_connector { - struct rcar_du_connector connector; - - const struct rcar_du_panel_data *panel; -}; - -#define to_rcar_lvds_connector(c) \ - container_of(c, struct rcar_du_lvds_connector, connector.connector) - -/* ----------------------------------------------------------------------------- - * Connector - */ - -static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector) -{ - struct rcar_du_lvds_connector *lvdscon = to_rcar_lvds_connector(connector); - struct drm_display_mode *mode; - - mode = drm_mode_create(connector->dev); - if (mode == NULL) - return 0; - - mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; - mode->clock = lvdscon->panel->mode.clock; - mode->hdisplay = lvdscon->panel->mode.hdisplay; - mode->hsync_start = lvdscon->panel->mode.hsync_start; - mode->hsync_end = lvdscon->panel->mode.hsync_end; - mode->htotal = lvdscon->panel->mode.htotal; - mode->vdisplay = lvdscon->panel->mode.vdisplay; - mode->vsync_start = lvdscon->panel->mode.vsync_start; - mode->vsync_end = lvdscon->panel->mode.vsync_end; - mode->vtotal = lvdscon->panel->mode.vtotal; - mode->flags = lvdscon->panel->mode.flags; - - drm_mode_set_name(mode); - drm_mode_probed_add(connector, mode); - - return 1; -} - -static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - -static const struct drm_connector_helper_funcs connector_helper_funcs = { - .get_modes = rcar_du_lvds_connector_get_modes, - .mode_valid = rcar_du_lvds_connector_mode_valid, - .best_encoder = rcar_du_connector_best_encoder, -}; - -static void rcar_du_lvds_connector_destroy(struct drm_connector *connector) -{ - drm_sysfs_connector_remove(connector); - drm_connector_cleanup(connector); -} - -static enum drm_connector_status -rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force) -{ - return connector_status_connected; -} - -static const struct drm_connector_funcs connector_funcs = { - .dpms = drm_helper_connector_dpms, - .detect = rcar_du_lvds_connector_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = rcar_du_lvds_connector_destroy, -}; - -static int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, - struct rcar_du_encoder *renc, - const struct rcar_du_panel_data *panel) -{ - struct rcar_du_lvds_connector *lvdscon; - struct drm_connector *connector; - int ret; - - lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL); - if (lvdscon == NULL) - return -ENOMEM; - - lvdscon->panel = panel; - - connector = &lvdscon->connector.connector; - connector->display_info.width_mm = panel->width_mm; - connector->display_info.height_mm = panel->height_mm; - - ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs, - DRM_MODE_CONNECTOR_LVDS); - if (ret < 0) - return ret; - - drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_sysfs_connector_add(connector); - if (ret < 0) - return ret; - - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); - drm_object_property_set_value(&connector->base, - rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); - - ret = drm_mode_connector_attach_encoder(connector, &renc->encoder); - if (ret < 0) - return ret; - - connector->encoder = &renc->encoder; - lvdscon->connector.encoder = renc; - - return 0; -} - -/* ----------------------------------------------------------------------------- - * Encoder - */ +#include "rcar_du_lvdscon.h"
static void rcar_du_lvds_encoder_dpms(struct drm_encoder *encoder, int mode) { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c new file mode 100644 index 0000000..b563e86 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -0,0 +1,129 @@ +/* + * rcar_du_lvdscon.c -- R-Car Display Unit LVDS Connector + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> + +#include "rcar_du_drv.h" +#include "rcar_du_kms.h" +#include "rcar_du_lvdscon.h" + +struct rcar_du_lvds_connector { + struct rcar_du_connector connector; + + const struct rcar_du_panel_data *panel; +}; + +#define to_rcar_lvds_connector(c) \ + container_of(c, struct rcar_du_lvds_connector, connector.connector) + +static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector) +{ + struct rcar_du_lvds_connector *lvdscon = to_rcar_lvds_connector(connector); + struct drm_display_mode *mode; + + mode = drm_mode_create(connector->dev); + if (mode == NULL) + return 0; + + mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; + mode->clock = lvdscon->panel->mode.clock; + mode->hdisplay = lvdscon->panel->mode.hdisplay; + mode->hsync_start = lvdscon->panel->mode.hsync_start; + mode->hsync_end = lvdscon->panel->mode.hsync_end; + mode->htotal = lvdscon->panel->mode.htotal; + mode->vdisplay = lvdscon->panel->mode.vdisplay; + mode->vsync_start = lvdscon->panel->mode.vsync_start; + mode->vsync_end = lvdscon->panel->mode.vsync_end; + mode->vtotal = lvdscon->panel->mode.vtotal; + mode->flags = lvdscon->panel->mode.flags; + + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + + return 1; +} + +static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static const struct drm_connector_helper_funcs connector_helper_funcs = { + .get_modes = rcar_du_lvds_connector_get_modes, + .mode_valid = rcar_du_lvds_connector_mode_valid, + .best_encoder = rcar_du_connector_best_encoder, +}; + +static void rcar_du_lvds_connector_destroy(struct drm_connector *connector) +{ + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); +} + +static enum drm_connector_status +rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static const struct drm_connector_funcs connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = rcar_du_lvds_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = rcar_du_lvds_connector_destroy, +}; + +int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, + struct rcar_du_encoder *renc, + const struct rcar_du_panel_data *panel) +{ + struct rcar_du_lvds_connector *lvdscon; + struct drm_connector *connector; + int ret; + + lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL); + if (lvdscon == NULL) + return -ENOMEM; + + lvdscon->panel = panel; + + connector = &lvdscon->connector.connector; + connector->display_info.width_mm = panel->width_mm; + connector->display_info.height_mm = panel->height_mm; + + ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + if (ret < 0) + return ret; + + drm_connector_helper_add(connector, &connector_helper_funcs); + ret = drm_sysfs_connector_add(connector); + if (ret < 0) + return ret; + + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + drm_object_property_set_value(&connector->base, + rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); + + ret = drm_mode_connector_attach_encoder(connector, &renc->encoder); + if (ret < 0) + return ret; + + connector->encoder = &renc->encoder; + lvdscon->connector.encoder = renc; + + return 0; +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h new file mode 100644 index 0000000..bff8683 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h @@ -0,0 +1,25 @@ +/* + * rcar_du_lvdscon.h -- R-Car Display Unit LVDS Connector + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_LVDSCON_H__ +#define __RCAR_DU_LVDSCON_H__ + +struct rcar_du_device; +struct rcar_du_encoder; +struct rcar_du_panel_data; + +int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, + struct rcar_du_encoder *renc, + const struct rcar_du_panel_data *panel); + +#endif /* __RCAR_DU_LVDSCON_H__ */
This prepares for the encoders rework.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/Makefile | 3 +- drivers/gpu/drm/rcar-du/rcar_du_vga.c | 86 +---------------------------- drivers/gpu/drm/rcar-du/rcar_du_vga.h | 2 +- drivers/gpu/drm/rcar-du/rcar_du_vgacon.c | 95 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_vgacon.h | 23 ++++++++ 5 files changed, 123 insertions(+), 86 deletions(-) create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vgacon.h
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile index 5def510..45a8479 100644 --- a/drivers/gpu/drm/rcar-du/Makefile +++ b/drivers/gpu/drm/rcar-du/Makefile @@ -4,6 +4,7 @@ rcar-du-drm-y := rcar_du_crtc.o \ rcar_du_lvds.o \ rcar_du_lvdscon.o \ rcar_du_plane.o \ - rcar_du_vga.o + rcar_du_vga.o \ + rcar_du_vgacon.o
obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.c b/drivers/gpu/drm/rcar-du/rcar_du_vga.c index 327289e..369ab32 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vga.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vga.c @@ -1,5 +1,5 @@ /* - * rcar_du_vga.c -- R-Car Display Unit VGA DAC and Connector + * rcar_du_vga.c -- R-Car Display Unit VGA DAC * * Copyright (C) 2013 Renesas Corporation * @@ -18,89 +18,7 @@ #include "rcar_du_drv.h" #include "rcar_du_kms.h" #include "rcar_du_vga.h" - -/* ----------------------------------------------------------------------------- - * Connector - */ - -static int rcar_du_vga_connector_get_modes(struct drm_connector *connector) -{ - return 0; -} - -static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - -static const struct drm_connector_helper_funcs connector_helper_funcs = { - .get_modes = rcar_du_vga_connector_get_modes, - .mode_valid = rcar_du_vga_connector_mode_valid, - .best_encoder = rcar_du_connector_best_encoder, -}; - -static void rcar_du_vga_connector_destroy(struct drm_connector *connector) -{ - drm_sysfs_connector_remove(connector); - drm_connector_cleanup(connector); -} - -static enum drm_connector_status -rcar_du_vga_connector_detect(struct drm_connector *connector, bool force) -{ - return connector_status_unknown; -} - -static const struct drm_connector_funcs connector_funcs = { - .dpms = drm_helper_connector_dpms, - .detect = rcar_du_vga_connector_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = rcar_du_vga_connector_destroy, -}; - -static int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, - struct rcar_du_encoder *renc) -{ - struct rcar_du_connector *rcon; - struct drm_connector *connector; - int ret; - - rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL); - if (rcon == NULL) - return -ENOMEM; - - connector = &rcon->connector; - connector->display_info.width_mm = 0; - connector->display_info.height_mm = 0; - - ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs, - DRM_MODE_CONNECTOR_VGA); - if (ret < 0) - return ret; - - drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_sysfs_connector_add(connector); - if (ret < 0) - return ret; - - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); - drm_object_property_set_value(&connector->base, - rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); - - ret = drm_mode_connector_attach_encoder(connector, &renc->encoder); - if (ret < 0) - return ret; - - connector->encoder = &renc->encoder; - rcon->encoder = renc; - - return 0; -} - -/* ----------------------------------------------------------------------------- - * Encoder - */ +#include "rcar_du_vgacon.h"
static void rcar_du_vga_encoder_dpms(struct drm_encoder *encoder, int mode) { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.h b/drivers/gpu/drm/rcar-du/rcar_du_vga.h index 66b4d2d..b969b20 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vga.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_vga.h @@ -1,5 +1,5 @@ /* - * rcar_du_vga.h -- R-Car Display Unit VGA DAC and Connector + * rcar_du_vga.h -- R-Car Display Unit VGA DAC * * Copyright (C) 2013 Renesas Corporation * diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c new file mode 100644 index 0000000..2ee3203 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -0,0 +1,95 @@ +/* + * rcar_du_vgacon.c -- R-Car Display Unit VGA Connector + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> + +#include "rcar_du_drv.h" +#include "rcar_du_kms.h" +#include "rcar_du_vgacon.h" + +static int rcar_du_vga_connector_get_modes(struct drm_connector *connector) +{ + return 0; +} + +static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static const struct drm_connector_helper_funcs connector_helper_funcs = { + .get_modes = rcar_du_vga_connector_get_modes, + .mode_valid = rcar_du_vga_connector_mode_valid, + .best_encoder = rcar_du_connector_best_encoder, +}; + +static void rcar_du_vga_connector_destroy(struct drm_connector *connector) +{ + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); +} + +static enum drm_connector_status +rcar_du_vga_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_unknown; +} + +static const struct drm_connector_funcs connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = rcar_du_vga_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = rcar_du_vga_connector_destroy, +}; + +int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, + struct rcar_du_encoder *renc) +{ + struct rcar_du_connector *rcon; + struct drm_connector *connector; + int ret; + + rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL); + if (rcon == NULL) + return -ENOMEM; + + connector = &rcon->connector; + connector->display_info.width_mm = 0; + connector->display_info.height_mm = 0; + + ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs, + DRM_MODE_CONNECTOR_VGA); + if (ret < 0) + return ret; + + drm_connector_helper_add(connector, &connector_helper_funcs); + ret = drm_sysfs_connector_add(connector); + if (ret < 0) + return ret; + + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + drm_object_property_set_value(&connector->base, + rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); + + ret = drm_mode_connector_attach_encoder(connector, &renc->encoder); + if (ret < 0) + return ret; + + connector->encoder = &renc->encoder; + rcon->encoder = renc; + + return 0; +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h new file mode 100644 index 0000000..b12b0cf --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h @@ -0,0 +1,23 @@ +/* + * rcar_du_vgacon.h -- R-Car Display Unit VGA Connector + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_VGACON_H__ +#define __RCAR_DU_VGACON_H__ + +struct rcar_du_device; +struct rcar_du_encoder; + +int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, + struct rcar_du_encoder *renc); + +#endif /* __RCAR_DU_VGACON_H__ */
Create a single rcar_du_encoder structure that implements a KMS encoder. The current implementation is straightforward and only configures CRTC output routing.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/Makefile | 3 +- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 2 - drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 148 ++++++++++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 45 +++++++++ drivers/gpu/drm/rcar-du/rcar_du_kms.c | 49 ++-------- drivers/gpu/drm/rcar-du/rcar_du_kms.h | 29 +----- drivers/gpu/drm/rcar-du/rcar_du_lvds.c | 100 -------------------- drivers/gpu/drm/rcar-du/rcar_du_lvds.h | 24 ----- drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | 1 + drivers/gpu/drm/rcar-du/rcar_du_vga.c | 67 -------------- drivers/gpu/drm/rcar-du/rcar_du_vga.h | 24 ----- drivers/gpu/drm/rcar-du/rcar_du_vgacon.c | 1 + 12 files changed, 205 insertions(+), 288 deletions(-) create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_encoder.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_encoder.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvds.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvds.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vga.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vga.h
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile index 45a8479..57b0fe1 100644 --- a/drivers/gpu/drm/rcar-du/Makefile +++ b/drivers/gpu/drm/rcar-du/Makefile @@ -1,10 +1,9 @@ rcar-du-drm-y := rcar_du_crtc.o \ rcar_du_drv.o \ + rcar_du_encoder.o \ rcar_du_kms.o \ - rcar_du_lvds.o \ rcar_du_lvdscon.o \ rcar_du_plane.o \ - rcar_du_vga.o \ rcar_du_vgacon.o
obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index aefc8a0..03dd701 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -23,10 +23,8 @@ #include "rcar_du_crtc.h" #include "rcar_du_drv.h" #include "rcar_du_kms.h" -#include "rcar_du_lvds.h" #include "rcar_du_plane.h" #include "rcar_du_regs.h" -#include "rcar_du_vga.h"
#define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c new file mode 100644 index 0000000..15a5643 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -0,0 +1,148 @@ +/* + * rcar_du_encoder.c -- R-Car Display Unit Encoder + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> + +#include "rcar_du_drv.h" +#include "rcar_du_encoder.h" +#include "rcar_du_kms.h" +#include "rcar_du_lvdscon.h" +#include "rcar_du_vgacon.h" + +/* ----------------------------------------------------------------------------- + * Common connector functions + */ + +struct drm_encoder * +rcar_du_connector_best_encoder(struct drm_connector *connector) +{ + struct rcar_du_connector *rcon = to_rcar_connector(connector); + + return &rcon->encoder->encoder; +} + +/* ----------------------------------------------------------------------------- + * Encoder + */ + +static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +} + +static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + const struct drm_display_mode *panel_mode; + struct drm_device *dev = encoder->dev; + struct drm_connector *connector; + bool found = false; + + /* DAC encoders have currently no restriction on the mode. */ + if (encoder->encoder_type == DRM_MODE_ENCODER_DAC) + return true; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder == encoder) { + found = true; + break; + } + } + + if (!found) { + dev_dbg(dev->dev, "mode_fixup: no connector found\n"); + return false; + } + + if (list_empty(&connector->modes)) { + dev_dbg(dev->dev, "mode_fixup: empty modes list\n"); + return false; + } + + panel_mode = list_first_entry(&connector->modes, + struct drm_display_mode, head); + + /* We're not allowed to modify the resolution. */ + if (mode->hdisplay != panel_mode->hdisplay || + mode->vdisplay != panel_mode->vdisplay) + return false; + + /* The flat panel mode is fixed, just copy it to the adjusted mode. */ + drm_mode_copy(adjusted_mode, panel_mode); + + return true; +} + +static void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder) +{ +} + +static void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) +{ +} + +static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + + rcar_du_crtc_route_output(encoder->crtc, renc->output); +} + +static const struct drm_encoder_helper_funcs encoder_helper_funcs = { + .dpms = rcar_du_encoder_dpms, + .mode_fixup = rcar_du_encoder_mode_fixup, + .prepare = rcar_du_encoder_mode_prepare, + .commit = rcar_du_encoder_mode_commit, + .mode_set = rcar_du_encoder_mode_set, +}; + +static const struct drm_encoder_funcs encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +int rcar_du_encoder_init(struct rcar_du_device *rcdu, + enum rcar_du_encoder_type type, unsigned int output, + const struct rcar_du_encoder_data *data) +{ + struct rcar_du_encoder *renc; + int ret; + + renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); + if (renc == NULL) + return -ENOMEM; + + renc->output = output; + + ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs, + type); + if (ret < 0) + return ret; + + drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); + + switch (type) { + case RCAR_DU_ENCODER_LVDS: + return rcar_du_lvds_connector_init(rcdu, renc, + &data->u.lvds.panel); + + case RCAR_DU_ENCODER_VGA: + return rcar_du_vga_connector_init(rcdu, renc); + + default: + return -EINVAL; + } +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h new file mode 100644 index 0000000..4f76e16 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -0,0 +1,45 @@ +/* + * rcar_du_encoder.h -- R-Car Display Unit Encoder + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_ENCODER_H__ +#define __RCAR_DU_ENCODER_H__ + +#include <drm/drm_crtc.h> + +struct rcar_du_device; +struct rcar_du_encoder_data; + +struct rcar_du_encoder { + struct drm_encoder encoder; + unsigned int output; +}; + +#define to_rcar_encoder(e) \ + container_of(e, struct rcar_du_encoder, encoder) + +struct rcar_du_connector { + struct drm_connector connector; + struct rcar_du_encoder *encoder; +}; + +#define to_rcar_connector(c) \ + container_of(c, struct rcar_du_connector, connector) + +struct drm_encoder * +rcar_du_connector_best_encoder(struct drm_connector *connector); + +int rcar_du_encoder_init(struct rcar_du_device *rcdu, + enum rcar_du_encoder_type type, unsigned int output, + const struct rcar_du_encoder_data *data); + +#endif /* __RCAR_DU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index d30c2e2..3f8483c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -19,10 +19,9 @@
#include "rcar_du_crtc.h" #include "rcar_du_drv.h" +#include "rcar_du_encoder.h" #include "rcar_du_kms.h" -#include "rcar_du_lvds.h" #include "rcar_du_regs.h" -#include "rcar_du_vga.h"
/* ----------------------------------------------------------------------------- * Format helpers @@ -106,35 +105,6 @@ const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc) }
/* ----------------------------------------------------------------------------- - * Common connector and encoder functions - */ - -struct drm_encoder * -rcar_du_connector_best_encoder(struct drm_connector *connector) -{ - struct rcar_du_connector *rcon = to_rcar_connector(connector); - - return &rcon->encoder->encoder; -} - -void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder) -{ -} - -void rcar_du_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct rcar_du_encoder *renc = to_rcar_encoder(encoder); - - rcar_du_crtc_route_output(encoder->crtc, renc->output); -} - -void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) -{ -} - -/* ----------------------------------------------------------------------------- * Frame buffer */
@@ -221,6 +191,9 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) const struct rcar_du_encoder_data *pdata = &rcdu->pdata->encoders[i];
+ if (pdata->encoder == RCAR_DU_ENCODER_UNUSED) + continue; + if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) { dev_warn(rcdu->dev, "encoder %u references unexisting output %u, skipping\n", @@ -228,18 +201,8 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) continue; }
- switch (pdata->encoder) { - case RCAR_DU_ENCODER_VGA: - rcar_du_vga_init(rcdu, &pdata->u.vga, pdata->output); - break; - - case RCAR_DU_ENCODER_LVDS: - rcar_du_lvds_init(rcdu, &pdata->u.lvds, pdata->output); - break; - - default: - break; - } + rcar_du_encoder_init(rcdu, pdata->encoder, pdata->output, + pdata); }
/* Set the possible CRTCs and possible clones. All encoders can be diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h index dba4722..5750e6a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h @@ -16,8 +16,9 @@
#include <linux/types.h>
-#include <drm/drm_crtc.h> - +struct drm_file; +struct drm_device; +struct drm_mode_create_dumb; struct rcar_du_device;
struct rcar_du_format_info { @@ -28,32 +29,8 @@ struct rcar_du_format_info { unsigned int edf; };
-struct rcar_du_encoder { - struct drm_encoder encoder; - unsigned int output; -}; - -#define to_rcar_encoder(e) \ - container_of(e, struct rcar_du_encoder, encoder) - -struct rcar_du_connector { - struct drm_connector connector; - struct rcar_du_encoder *encoder; -}; - -#define to_rcar_connector(c) \ - container_of(c, struct rcar_du_connector, connector) - const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc);
-struct drm_encoder * -rcar_du_connector_best_encoder(struct drm_connector *connector); -void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder); -void rcar_du_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); -void rcar_du_encoder_mode_commit(struct drm_encoder *encoder); - int rcar_du_modeset_init(struct rcar_du_device *rcdu);
int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.c b/drivers/gpu/drm/rcar-du/rcar_du_lvds.c deleted file mode 100644 index 82e5157..0000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvds.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * rcar_du_lvds.c -- R-Car Display Unit LVDS Encoder - * - * Copyright (C) 2013 Renesas Corporation - * - * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <drm/drmP.h> -#include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> - -#include "rcar_du_drv.h" -#include "rcar_du_kms.h" -#include "rcar_du_lvds.h" -#include "rcar_du_lvdscon.h" - -static void rcar_du_lvds_encoder_dpms(struct drm_encoder *encoder, int mode) -{ -} - -static bool rcar_du_lvds_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - const struct drm_display_mode *panel_mode; - struct drm_device *dev = encoder->dev; - struct drm_connector *connector; - bool found = false; - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder == encoder) { - found = true; - break; - } - } - - if (!found) { - dev_dbg(dev->dev, "mode_fixup: no connector found\n"); - return false; - } - - if (list_empty(&connector->modes)) { - dev_dbg(dev->dev, "mode_fixup: empty modes list\n"); - return false; - } - - panel_mode = list_first_entry(&connector->modes, - struct drm_display_mode, head); - - /* We're not allowed to modify the resolution. */ - if (mode->hdisplay != panel_mode->hdisplay || - mode->vdisplay != panel_mode->vdisplay) - return false; - - /* The flat panel mode is fixed, just copy it to the adjusted mode. */ - drm_mode_copy(adjusted_mode, panel_mode); - - return true; -} - -static const struct drm_encoder_helper_funcs encoder_helper_funcs = { - .dpms = rcar_du_lvds_encoder_dpms, - .mode_fixup = rcar_du_lvds_encoder_mode_fixup, - .prepare = rcar_du_encoder_mode_prepare, - .commit = rcar_du_encoder_mode_commit, - .mode_set = rcar_du_encoder_mode_set, -}; - -static const struct drm_encoder_funcs encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -int rcar_du_lvds_init(struct rcar_du_device *rcdu, - const struct rcar_du_encoder_lvds_data *data, - unsigned int output) -{ - struct rcar_du_encoder *renc; - int ret; - - renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); - if (renc == NULL) - return -ENOMEM; - - renc->output = output; - - ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs, - DRM_MODE_ENCODER_LVDS); - if (ret < 0) - return ret; - - drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); - - return rcar_du_lvds_connector_init(rcdu, renc, &data->panel); -} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.h b/drivers/gpu/drm/rcar-du/rcar_du_lvds.h deleted file mode 100644 index b47f832..0000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvds.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * rcar_du_lvds.h -- R-Car Display Unit LVDS Encoder and Connector - * - * Copyright (C) 2013 Renesas Corporation - * - * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __RCAR_DU_LVDS_H__ -#define __RCAR_DU_LVDS_H__ - -struct rcar_du_device; -struct rcar_du_encoder_lvds_data; - -int rcar_du_lvds_init(struct rcar_du_device *rcdu, - const struct rcar_du_encoder_lvds_data *data, - unsigned int output); - -#endif /* __RCAR_DU_LVDS_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index b563e86..628a744 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -16,6 +16,7 @@ #include <drm/drm_crtc_helper.h>
#include "rcar_du_drv.h" +#include "rcar_du_encoder.h" #include "rcar_du_kms.h" #include "rcar_du_lvdscon.h"
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.c b/drivers/gpu/drm/rcar-du/rcar_du_vga.c deleted file mode 100644 index 369ab32..0000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_vga.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * rcar_du_vga.c -- R-Car Display Unit VGA DAC - * - * Copyright (C) 2013 Renesas Corporation - * - * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <drm/drmP.h> -#include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> - -#include "rcar_du_drv.h" -#include "rcar_du_kms.h" -#include "rcar_du_vga.h" -#include "rcar_du_vgacon.h" - -static void rcar_du_vga_encoder_dpms(struct drm_encoder *encoder, int mode) -{ -} - -static bool rcar_du_vga_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static const struct drm_encoder_helper_funcs encoder_helper_funcs = { - .dpms = rcar_du_vga_encoder_dpms, - .mode_fixup = rcar_du_vga_encoder_mode_fixup, - .prepare = rcar_du_encoder_mode_prepare, - .commit = rcar_du_encoder_mode_commit, - .mode_set = rcar_du_encoder_mode_set, -}; - -static const struct drm_encoder_funcs encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -int rcar_du_vga_init(struct rcar_du_device *rcdu, - const struct rcar_du_encoder_vga_data *data, - unsigned int output) -{ - struct rcar_du_encoder *renc; - int ret; - - renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); - if (renc == NULL) - return -ENOMEM; - - renc->output = output; - - ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs, - DRM_MODE_ENCODER_DAC); - if (ret < 0) - return ret; - - drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); - - return rcar_du_vga_connector_init(rcdu, renc); -} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.h b/drivers/gpu/drm/rcar-du/rcar_du_vga.h deleted file mode 100644 index b969b20..0000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_vga.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * rcar_du_vga.h -- R-Car Display Unit VGA DAC - * - * Copyright (C) 2013 Renesas Corporation - * - * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __RCAR_DU_VGA_H__ -#define __RCAR_DU_VGA_H__ - -struct rcar_du_device; -struct rcar_du_encoder_vga_data; - -int rcar_du_vga_init(struct rcar_du_device *rcdu, - const struct rcar_du_encoder_vga_data *data, - unsigned int output); - -#endif /* __RCAR_DU_VGA_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index 2ee3203..36105db 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -16,6 +16,7 @@ #include <drm/drm_crtc_helper.h>
#include "rcar_du_drv.h" +#include "rcar_du_encoder.h" #include "rcar_du_kms.h" #include "rcar_du_vgacon.h"
The struct rcar_du_encoder_data encoder::field describes the encoder type, and the rcar_du_encoder_lvds_data and rcar_du_encoder_vga_data structures describe connector properties. Rename them accordingly.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 2 +- drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 3 ++- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 5 ++--- include/linux/platform_data/rcar-du.h | 19 +++++++++++++------ 4 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 15a5643..0d0375c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -137,7 +137,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, switch (type) { case RCAR_DU_ENCODER_LVDS: return rcar_du_lvds_connector_init(rcdu, renc, - &data->u.lvds.panel); + &data->connector.lvds.panel);
case RCAR_DU_ENCODER_VGA: return rcar_du_vga_connector_init(rcdu, renc); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 4f76e16..08cde12 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -14,10 +14,11 @@ #ifndef __RCAR_DU_ENCODER_H__ #define __RCAR_DU_ENCODER_H__
+#include <linux/platform_data/rcar-du.h> + #include <drm/drm_crtc.h>
struct rcar_du_device; -struct rcar_du_encoder_data;
struct rcar_du_encoder { struct drm_encoder encoder; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 3f8483c..a8eef16 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -191,7 +191,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) const struct rcar_du_encoder_data *pdata = &rcdu->pdata->encoders[i];
- if (pdata->encoder == RCAR_DU_ENCODER_UNUSED) + if (pdata->type == RCAR_DU_ENCODER_UNUSED) continue;
if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) { @@ -201,8 +201,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) continue; }
- rcar_du_encoder_init(rcdu, pdata->encoder, pdata->output, - pdata); + rcar_du_encoder_init(rcdu, pdata->type, pdata->output, pdata); }
/* Set the possible CRTCs and possible clones. All encoders can be diff --git a/include/linux/platform_data/rcar-du.h b/include/linux/platform_data/rcar-du.h index 80587fd..64cd863 100644 --- a/include/linux/platform_data/rcar-du.h +++ b/include/linux/platform_data/rcar-du.h @@ -28,22 +28,29 @@ struct rcar_du_panel_data { struct drm_mode_modeinfo mode; };
-struct rcar_du_encoder_lvds_data { +struct rcar_du_connector_lvds_data { struct rcar_du_panel_data panel; };
-struct rcar_du_encoder_vga_data { +struct rcar_du_connector_vga_data { /* TODO: Add DDC information for EDID retrieval */ };
+/* + * struct rcar_du_encoder_data - Encoder platform data + * @type: the encoder type (RCAR_DU_ENCODER_*) + * @output: the DU output the connector is connected to + * @connector.lvds: platform data for LVDS connectors + * @connector.vga: platform data for VGA connectors + */ struct rcar_du_encoder_data { - enum rcar_du_encoder_type encoder; + enum rcar_du_encoder_type type; unsigned int output;
union { - struct rcar_du_encoder_lvds_data lvds; - struct rcar_du_encoder_vga_data vga; - } u; + struct rcar_du_connector_lvds_data lvds; + struct rcar_du_connector_vga_data vga; + } connector; };
struct rcar_du_platform_data {
Move the plane-related fields of struct rcar_du_device to their own structure.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_drv.h | 11 +---------- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 7d2320f..0305c21 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -15,7 +15,6 @@ #define __RCAR_DU_DRV_H__
#include <linux/kernel.h> -#include <linux/mutex.h> #include <linux/platform_data/rcar-du.h>
#include "rcar_du_crtc.h" @@ -49,15 +48,7 @@ struct rcar_du_device { unsigned int used_crtcs; unsigned int num_crtcs;
- struct { - struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES]; - unsigned int free; - struct mutex lock; - - struct drm_property *alpha; - struct drm_property *colorkey; - struct drm_property *zpos; - } planes; + struct rcar_du_planes planes; };
static inline bool rcar_du_has(struct rcar_du_device *rcdu, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 5397dba..5c8488c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -14,8 +14,11 @@ #ifndef __RCAR_DU_PLANE_H__ #define __RCAR_DU_PLANE_H__
-struct drm_crtc; -struct drm_framebuffer; +#include <linux/mutex.h> + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> + struct rcar_du_device; struct rcar_du_format_info;
@@ -54,6 +57,16 @@ struct rcar_du_plane { unsigned int dst_y; };
+struct rcar_du_planes { + struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES]; + unsigned int free; + struct mutex lock; + + struct drm_property *alpha; + struct drm_property *colorkey; + struct drm_property *zpos; +}; + int rcar_du_plane_init(struct rcar_du_device *rcdu); int rcar_du_plane_register(struct rcar_du_device *rcdu); void rcar_du_plane_setup(struct rcar_du_plane *plane);
The functions initialize or register all planes, rename them accordingly.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 4 ++-- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 4 ++-- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index a8eef16..a1343fb 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -174,7 +174,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) rcdu->ddev->mode_config.max_height = 2047; rcdu->ddev->mode_config.funcs = &rcar_du_mode_config_funcs;
- ret = rcar_du_plane_init(rcdu); + ret = rcar_du_planes_init(rcdu); if (ret < 0) return ret;
@@ -215,7 +215,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) encoder->possible_clones = 1 << 0; }
- ret = rcar_du_plane_register(rcdu); + ret = rcar_du_planes_register(rcdu); if (ret < 0) return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 38ebd20..29f2147 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -435,7 +435,7 @@ static const uint32_t formats[] = { DRM_FORMAT_NV16, };
-int rcar_du_plane_init(struct rcar_du_device *rcdu) +int rcar_du_planes_init(struct rcar_du_device *rcdu) { unsigned int i;
@@ -475,7 +475,7 @@ int rcar_du_plane_init(struct rcar_du_device *rcdu) return 0; }
-int rcar_du_plane_register(struct rcar_du_device *rcdu) +int rcar_du_planes_register(struct rcar_du_device *rcdu) { unsigned int i; int ret; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 5c8488c..bcf6f76 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -67,8 +67,9 @@ struct rcar_du_planes { struct drm_property *zpos; };
-int rcar_du_plane_init(struct rcar_du_device *rcdu); -int rcar_du_plane_register(struct rcar_du_device *rcdu); +int rcar_du_planes_init(struct rcar_du_device *rcdu); +int rcar_du_planes_register(struct rcar_du_device *rcdu); + void rcar_du_plane_setup(struct rcar_du_plane *plane); void rcar_du_plane_update_base(struct rcar_du_plane *plane); void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
The R8A7779 DU is split in per-CRTC resources (scan-out engine, blending unit, timings generator, ...) and device-global resources (start/stop control, planes, ...) shared between the two CRTCs.
The R8A7790 introduced a third CRTC with its own set of global resources This would be modeled as two separate DU device instances if it wasn't for a handful or resources that are shared between the three CRTCs (mostly related to input and output routing). For this reason the R8A7790 DU must be modeled as a single device with three CRTCs, two sets of "semi-global" resources, and a few device-global resources.
Introduce a new rcar_du_group driver-specific object, without any real counterpart in the DU documentation, that models those semi-global resources.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/Makefile | 1 + drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 92 ++++++------------- drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 5 +- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 46 ---------- drivers/gpu/drm/rcar-du/rcar_du_drv.h | 10 +-- drivers/gpu/drm/rcar-du/rcar_du_group.c | 127 ++++++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_group.h | 47 ++++++++++ drivers/gpu/drm/rcar-du/rcar_du_kms.c | 11 ++- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 155 ++++++++++++++++---------------- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 8 +- 10 files changed, 299 insertions(+), 203 deletions(-) create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_group.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_group.h
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile index 57b0fe1..b9b5e66 100644 --- a/drivers/gpu/drm/rcar-du/Makefile +++ b/drivers/gpu/drm/rcar-du/Makefile @@ -1,6 +1,7 @@ rcar-du-drm-y := rcar_du_crtc.o \ rcar_du_drv.o \ rcar_du_encoder.o \ + rcar_du_group.o \ rcar_du_kms.o \ rcar_du_lvdscon.o \ rcar_du_plane.o \ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 03dd701..7784a3b 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -30,21 +30,21 @@
static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + struct rcar_du_device *rcdu = rcrtc->group->dev;
return rcar_du_read(rcdu, rcrtc->mmio_offset + reg); }
static void rcar_du_crtc_write(struct rcar_du_crtc *rcrtc, u32 reg, u32 data) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + struct rcar_du_device *rcdu = rcrtc->group->dev;
rcar_du_write(rcdu, rcrtc->mmio_offset + reg, data); }
static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + struct rcar_du_device *rcdu = rcrtc->group->dev;
rcar_du_write(rcdu, rcrtc->mmio_offset + reg, rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr); @@ -52,7 +52,7 @@ static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr)
static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + struct rcar_du_device *rcdu = rcrtc->group->dev;
rcar_du_write(rcdu, rcrtc->mmio_offset + reg, rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set); @@ -61,7 +61,7 @@ static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set) static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr, u32 set) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + struct rcar_du_device *rcdu = rcrtc->group->dev; u32 value = rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set); @@ -69,14 +69,13 @@ static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg,
static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; int ret;
ret = clk_prepare_enable(rcrtc->clock); if (ret < 0) return ret;
- ret = rcar_du_get(rcdu); + ret = rcar_du_group_get(rcrtc->group); if (ret < 0) clk_disable_unprepare(rcrtc->clock);
@@ -85,17 +84,14 @@ static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; - - rcar_du_put(rcdu); + rcar_du_group_put(rcrtc->group); clk_disable_unprepare(rcrtc->clock); }
static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) { - struct drm_crtc *crtc = &rcrtc->crtc; - struct rcar_du_device *rcdu = crtc->dev->dev_private; - const struct drm_display_mode *mode = &crtc->mode; + const struct drm_display_mode *mode = &rcrtc->crtc.mode; + struct rcar_du_device *rcdu = rcrtc->group->dev; unsigned long clk; u32 value; u32 div; @@ -136,7 +132,7 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
static void rcar_du_crtc_set_routing(struct rcar_du_crtc *rcrtc) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + struct rcar_du_device *rcdu = rcrtc->group->dev; u32 dorcr = rcar_du_read(rcdu, DORCR);
dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK); @@ -153,36 +149,6 @@ static void rcar_du_crtc_set_routing(struct rcar_du_crtc *rcrtc) rcar_du_write(rcdu, DORCR, dorcr); }
-static void __rcar_du_start_stop(struct rcar_du_device *rcdu, bool start) -{ - rcar_du_write(rcdu, DSYSR, - (rcar_du_read(rcdu, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) | - (start ? DSYSR_DEN : DSYSR_DRES)); -} - -static void rcar_du_start_stop(struct rcar_du_device *rcdu, bool start) -{ - /* Many of the configuration bits are only updated when the display - * reset (DRES) bit in DSYSR is set to 1, disabling *both* CRTCs. Some - * of those bits could be pre-configured, but others (especially the - * bits related to plane assignment to display timing controllers) need - * to be modified at runtime. - * - * Restart the display controller if a start is requested. Sorry for the - * flicker. It should be possible to move most of the "DRES-update" bits - * setup to driver initialization time and minimize the number of cases - * when the display controller will have to be restarted. - */ - if (start) { - if (rcdu->used_crtcs++ != 0) - __rcar_du_start_stop(rcdu, false); - __rcar_du_start_stop(rcdu, true); - } else { - if (--rcdu->used_crtcs == 0) - __rcar_du_start_stop(rcdu, false); - } -} - void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); @@ -195,8 +161,8 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output)
void rcar_du_crtc_update_planes(struct drm_crtc *crtc) { - struct rcar_du_device *rcdu = crtc->dev->dev_private; struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct rcar_du_device *rcdu = rcrtc->group->dev; struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES]; unsigned int num_planes = 0; unsigned int prio = 0; @@ -204,8 +170,8 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) u32 dptsr = 0; u32 dspr = 0;
- for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) { - struct rcar_du_plane *plane = &rcdu->planes.planes[i]; + for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) { + struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; unsigned int j;
if (plane->crtc != &rcrtc->crtc || !plane->enabled) @@ -254,10 +220,8 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) */ if (value != dptsr) { rcar_du_write(rcdu, DPTSR, dptsr); - if (rcdu->used_crtcs) { - __rcar_du_start_stop(rcdu, false); - __rcar_du_start_stop(rcdu, true); - } + if (rcrtc->group->used_crtcs) + rcar_du_group_restart(rcrtc->group); } }
@@ -267,7 +231,6 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) { struct drm_crtc *crtc = &rcrtc->crtc; - struct rcar_du_device *rcdu = crtc->dev->dev_private; unsigned int i;
if (rcrtc->started) @@ -284,14 +247,14 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) rcar_du_crtc_set_display_timing(rcrtc); rcar_du_crtc_set_routing(rcrtc);
- mutex_lock(&rcdu->planes.lock); + mutex_lock(&rcrtc->group->planes.lock); rcrtc->plane->enabled = true; rcar_du_crtc_update_planes(crtc); - mutex_unlock(&rcdu->planes.lock); + mutex_unlock(&rcrtc->group->planes.lock);
/* Setup planes. */ - for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) { - struct rcar_du_plane *plane = &rcdu->planes.planes[i]; + for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) { + struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i];
if (plane->crtc != crtc || !plane->enabled) continue; @@ -305,7 +268,7 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) */ rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_MASTER);
- rcar_du_start_stop(rcdu, true); + rcar_du_group_start_stop(rcrtc->group, true);
rcrtc->started = true; } @@ -313,22 +276,21 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) { struct drm_crtc *crtc = &rcrtc->crtc; - struct rcar_du_device *rcdu = crtc->dev->dev_private;
if (!rcrtc->started) return;
- mutex_lock(&rcdu->planes.lock); + mutex_lock(&rcrtc->group->planes.lock); rcrtc->plane->enabled = false; rcar_du_crtc_update_planes(crtc); - mutex_unlock(&rcdu->planes.lock); + mutex_unlock(&rcrtc->group->planes.lock);
/* Select switch sync mode. This stops display operation and configures * the HSYNC and VSYNC signals as inputs. */ rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH);
- rcar_du_start_stop(rcdu, false); + rcar_du_group_start_stop(rcrtc->group, false);
rcrtc->started = false; } @@ -406,8 +368,8 @@ static int rcar_du_crtc_mode_set(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { - struct rcar_du_device *rcdu = crtc->dev->dev_private; struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct rcar_du_device *rcdu = rcrtc->group->dev; const struct rcar_du_format_info *format; int ret;
@@ -583,8 +545,9 @@ static const struct drm_crtc_funcs crtc_funcs = { .page_flip = rcar_du_crtc_page_flip, };
-int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index) +int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) { + struct rcar_du_device *rcdu = rgrp->dev; struct platform_device *pdev = to_platform_device(rcdu->dev); struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index]; struct drm_crtc *crtc = &rcrtc->crtc; @@ -608,10 +571,11 @@ int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index) return PTR_ERR(rcrtc->clock); }
+ rcrtc->group = rgrp; rcrtc->mmio_offset = index ? DISP2_REG_OFFSET : 0; rcrtc->index = index; rcrtc->dpms = DRM_MODE_DPMS_OFF; - rcrtc->plane = &rcdu->planes.planes[index]; + rcrtc->plane = &rgrp->planes.planes[index];
rcrtc->plane->crtc = crtc;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 5b69e98..542a7fe 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -19,7 +19,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h>
-struct rcar_du_device; +struct rcar_du_group; struct rcar_du_plane;
struct rcar_du_crtc { @@ -34,10 +34,11 @@ struct rcar_du_crtc { unsigned int outputs; int dpms;
+ struct rcar_du_group *group; struct rcar_du_plane *plane; };
-int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index); +int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index); void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable); void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, struct drm_file *file); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index bcda2e0..5e4168d 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -29,52 +29,6 @@ #include "rcar_du_regs.h"
/* ----------------------------------------------------------------------------- - * Core device operations - */ - -/* - * rcar_du_get - Acquire a reference to the DU - * - * Acquiring the first reference setups core registers. A reference must be - * held before accessing any hardware registers. - * - * This function must be called with the DRM mode_config lock held. - * - * Return 0 in case of success or a negative error code otherwise. - */ -int rcar_du_get(struct rcar_du_device *rcdu) -{ - if (rcdu->use_count) - goto done; - - /* Enable extended features */ - rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE); - rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G); - rcar_du_write(rcdu, DEFR3, DEFR3_CODE | DEFR3_DEFE3); - rcar_du_write(rcdu, DEFR4, DEFR4_CODE); - rcar_du_write(rcdu, DEFR5, DEFR5_CODE | DEFR5_DEFE5); - - /* Use DS1PR and DS2PR to configure planes priorities and connects the - * superposition 0 to DU0 pins. DU1 pins will be configured dynamically. - */ - rcar_du_write(rcdu, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS); - -done: - rcdu->use_count++; - return 0; -} - -/* - * rcar_du_put - Release a reference to the DU - * - * This function must be called with the DRM mode_config lock held. - */ -void rcar_du_put(struct rcar_du_device *rcdu) -{ - --rcdu->use_count; -} - -/* ----------------------------------------------------------------------------- * DRM operations */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 0305c21..5b57a2f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -18,11 +18,12 @@ #include <linux/platform_data/rcar-du.h>
#include "rcar_du_crtc.h" -#include "rcar_du_plane.h" +#include "rcar_du_group.h"
struct clk; struct device; struct drm_device; +struct rcar_du_device;
#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */
@@ -40,15 +41,13 @@ struct rcar_du_device { const struct rcar_du_device_info *info;
void __iomem *mmio; - unsigned int use_count;
struct drm_device *ddev;
struct rcar_du_crtc crtcs[2]; - unsigned int used_crtcs; unsigned int num_crtcs;
- struct rcar_du_planes planes; + struct rcar_du_group group; };
static inline bool rcar_du_has(struct rcar_du_device *rcdu, @@ -57,9 +56,6 @@ static inline bool rcar_du_has(struct rcar_du_device *rcdu, return rcdu->info->features & feature; }
-int rcar_du_get(struct rcar_du_device *rcdu); -void rcar_du_put(struct rcar_du_device *rcdu); - static inline u32 rcar_du_read(struct rcar_du_device *rcdu, u32 reg) { return ioread32(rcdu->mmio + reg); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c new file mode 100644 index 0000000..625b9f4 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c @@ -0,0 +1,127 @@ +/* + * rcar_du_group.c -- R-Car Display Unit Channels Pair + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * The R8A7779 DU is split in per-CRTC resources (scan-out engine, blending + * unit, timings generator, ...) and device-global resources (start/stop + * control, planes, ...) shared between the two CRTCs. + * + * The R8A7790 introduced a third CRTC with its own set of global resources. + * This would be modeled as two separate DU device instances if it wasn't for + * a handful or resources that are shared between the three CRTCs (mostly + * related to input and output routing). For this reason the R8A7790 DU must be + * modeled as a single device with three CRTCs, two sets of "semi-global" + * resources, and a few device-global resources. + * + * The rcar_du_group object is a driver specific object, without any real + * counterpart in the DU documentation, that models those semi-global resources. + */ + +#include <linux/io.h> + +#include "rcar_du_drv.h" +#include "rcar_du_group.h" +#include "rcar_du_regs.h" + +static u32 rcar_du_group_read(struct rcar_du_group *rgrp, u32 reg) +{ + return rcar_du_read(rgrp->dev, rgrp->mmio_offset + reg); +} + +static void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data) +{ + rcar_du_write(rgrp->dev, rgrp->mmio_offset + reg, data); +} + +static void rcar_du_group_setup(struct rcar_du_group *rgrp) +{ + /* Enable extended features */ + rcar_du_group_write(rgrp, DEFR, DEFR_CODE | DEFR_DEFE); + rcar_du_group_write(rgrp, DEFR2, DEFR2_CODE | DEFR2_DEFE2G); + rcar_du_group_write(rgrp, DEFR3, DEFR3_CODE | DEFR3_DEFE3); + rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE); + rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5); + + /* Use DS1PR and DS2PR to configure planes priorities and connects the + * superposition 0 to DU0 pins. DU1 pins will be configured dynamically. + */ + rcar_du_group_write(rgrp, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS); +} + +/* + * rcar_du_group_get - Acquire a reference to the DU channels group + * + * Acquiring the first reference setups core registers. A reference must be held + * before accessing any hardware registers. + * + * This function must be called with the DRM mode_config lock held. + * + * Return 0 in case of success or a negative error code otherwise. + */ +int rcar_du_group_get(struct rcar_du_group *rgrp) +{ + if (rgrp->use_count) + goto done; + + rcar_du_group_setup(rgrp); + +done: + rgrp->use_count++; + return 0; +} + +/* + * rcar_du_group_put - Release a reference to the DU + * + * This function must be called with the DRM mode_config lock held. + */ +void rcar_du_group_put(struct rcar_du_group *rgrp) +{ + --rgrp->use_count; +} + +static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) +{ + rcar_du_group_write(rgrp, DSYSR, + (rcar_du_group_read(rgrp, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) | + (start ? DSYSR_DEN : DSYSR_DRES)); +} + +void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) +{ + /* Many of the configuration bits are only updated when the display + * reset (DRES) bit in DSYSR is set to 1, disabling *both* CRTCs. Some + * of those bits could be pre-configured, but others (especially the + * bits related to plane assignment to display timing controllers) need + * to be modified at runtime. + * + * Restart the display controller if a start is requested. Sorry for the + * flicker. It should be possible to move most of the "DRES-update" bits + * setup to driver initialization time and minimize the number of cases + * when the display controller will have to be restarted. + */ + if (start) { + if (rgrp->used_crtcs++ != 0) + __rcar_du_group_start_stop(rgrp, false); + __rcar_du_group_start_stop(rgrp, true); + } else { + if (--rgrp->used_crtcs == 0) + __rcar_du_group_start_stop(rgrp, false); + } +} + +void rcar_du_group_restart(struct rcar_du_group *rgrp) +{ + __rcar_du_group_start_stop(rgrp, false); + __rcar_du_group_start_stop(rgrp, true); +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.h b/drivers/gpu/drm/rcar-du/rcar_du_group.h new file mode 100644 index 0000000..748331b --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.h @@ -0,0 +1,47 @@ +/* + * rcar_du_group.c -- R-Car Display Unit Planes and CRTCs Group + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_GROUP_H__ +#define __RCAR_DU_GROUP_H__ + +#include "rcar_du_plane.h" + +struct rcar_du_device; + +/* + * struct rcar_du_group - CRTCs and planes group + * @dev: the DU device + * @mmio_offset: registers offset in the device memory map + * @index: group index + * @use_count: number of users of the group (rcar_du_group_(get|put)) + * @used_crtcs: number of CRTCs currently in use + * @planes: planes handled by the group + */ +struct rcar_du_group { + struct rcar_du_device *dev; + unsigned int mmio_offset; + unsigned int index; + + unsigned int use_count; + unsigned int used_crtcs; + + struct rcar_du_planes planes; +}; + +int rcar_du_group_get(struct rcar_du_group *rgrp); +void rcar_du_group_put(struct rcar_du_group *rgrp); +void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start); +void rcar_du_group_restart(struct rcar_du_group *rgrp); + + +#endif /* __RCAR_DU_GROUP_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index a1343fb..c32e0f9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -174,17 +174,20 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) rcdu->ddev->mode_config.max_height = 2047; rcdu->ddev->mode_config.funcs = &rcar_du_mode_config_funcs;
- ret = rcar_du_planes_init(rcdu); + rcdu->group.dev = rcdu; + rcdu->group.index = 0; + rcdu->group.used_crtcs = 0; + + ret = rcar_du_planes_init(&rcdu->group); if (ret < 0) return ret;
for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) { - ret = rcar_du_crtc_create(rcdu, i); + ret = rcar_du_crtc_create(&rcdu->group, i); if (ret < 0) return ret; }
- rcdu->used_crtcs = 0; rcdu->num_crtcs = i;
for (i = 0; i < rcdu->pdata->num_encoders; ++i) { @@ -215,7 +218,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) encoder->possible_clones = 1 << 0; }
- ret = rcar_du_planes_register(rcdu); + ret = rcar_du_planes_register(&rcdu->group); if (ret < 0) return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 29f2147..1e9cf7c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -36,71 +36,73 @@ static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane) return container_of(plane, struct rcar_du_kms_plane, plane)->hwplane; }
-static u32 rcar_du_plane_read(struct rcar_du_device *rcdu, +static u32 rcar_du_plane_read(struct rcar_du_group *rgrp, unsigned int index, u32 reg) { - return rcar_du_read(rcdu, index * PLANE_OFF + reg); + return rcar_du_read(rgrp->dev, + rgrp->mmio_offset + index * PLANE_OFF + reg); }
-static void rcar_du_plane_write(struct rcar_du_device *rcdu, +static void rcar_du_plane_write(struct rcar_du_group *rgrp, unsigned int index, u32 reg, u32 data) { - rcar_du_write(rcdu, index * PLANE_OFF + reg, data); + rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg, + data); }
int rcar_du_plane_reserve(struct rcar_du_plane *plane, const struct rcar_du_format_info *format) { - struct rcar_du_device *rcdu = plane->dev; + struct rcar_du_group *rgrp = plane->group; unsigned int i; int ret = -EBUSY;
- mutex_lock(&rcdu->planes.lock); + mutex_lock(&rgrp->planes.lock);
- for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) { - if (!(rcdu->planes.free & (1 << i))) + for (i = 0; i < ARRAY_SIZE(rgrp->planes.planes); ++i) { + if (!(rgrp->planes.free & (1 << i))) continue;
if (format->planes == 1 || - rcdu->planes.free & (1 << ((i + 1) % 8))) + rgrp->planes.free & (1 << ((i + 1) % 8))) break; }
- if (i == ARRAY_SIZE(rcdu->planes.planes)) + if (i == ARRAY_SIZE(rgrp->planes.planes)) goto done;
- rcdu->planes.free &= ~(1 << i); + rgrp->planes.free &= ~(1 << i); if (format->planes == 2) - rcdu->planes.free &= ~(1 << ((i + 1) % 8)); + rgrp->planes.free &= ~(1 << ((i + 1) % 8));
plane->hwindex = i;
ret = 0;
done: - mutex_unlock(&rcdu->planes.lock); + mutex_unlock(&rgrp->planes.lock); return ret; }
void rcar_du_plane_release(struct rcar_du_plane *plane) { - struct rcar_du_device *rcdu = plane->dev; + struct rcar_du_group *rgrp = plane->group;
if (plane->hwindex == -1) return;
- mutex_lock(&rcdu->planes.lock); - rcdu->planes.free |= 1 << plane->hwindex; + mutex_lock(&rgrp->planes.lock); + rgrp->planes.free |= 1 << plane->hwindex; if (plane->format->planes == 2) - rcdu->planes.free |= 1 << ((plane->hwindex + 1) % 8); - mutex_unlock(&rcdu->planes.lock); + rgrp->planes.free |= 1 << ((plane->hwindex + 1) % 8); + mutex_unlock(&rgrp->planes.lock);
plane->hwindex = -1; }
void rcar_du_plane_update_base(struct rcar_du_plane *plane) { - struct rcar_du_device *rcdu = plane->dev; + struct rcar_du_group *rgrp = plane->group; unsigned int index = plane->hwindex;
/* The Y position is expressed in raster line units and must be doubled @@ -111,18 +113,18 @@ void rcar_du_plane_update_base(struct rcar_du_plane *plane) * Similarly, for the second plane, NV12 and NV21 formats seem to * require a halved Y position value. */ - rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x); - rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y * + rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x); + rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y * (plane->format->bpp == 32 ? 2 : 1)); - rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[0]); + rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[0]);
if (plane->format->planes == 2) { index = (index + 1) % 8;
- rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x); - rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y * + rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x); + rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y * (plane->format->bpp == 16 ? 2 : 1) / 2); - rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[1]); + rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[1]); } }
@@ -143,7 +145,7 @@ void rcar_du_plane_compute_base(struct rcar_du_plane *plane, static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, unsigned int index) { - struct rcar_du_device *rcdu = plane->dev; + struct rcar_du_group *rgrp = plane->group; u32 colorkey; u32 pnmr;
@@ -157,9 +159,9 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, * enable alpha-blending regardless of the X bit value. */ if (plane->format->fourcc != DRM_FORMAT_XRGB1555) - rcar_du_plane_write(rcdu, index, PnALPHAR, PnALPHAR_ABIT_0); + rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0); else - rcar_du_plane_write(rcdu, index, PnALPHAR, + rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_X | plane->alpha);
pnmr = PnMR_BM_MD | plane->format->pnmr; @@ -175,14 +177,14 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, if (plane->format->fourcc == DRM_FORMAT_YUYV) pnmr |= PnMR_YCDF_YUYV;
- rcar_du_plane_write(rcdu, index, PnMR, pnmr); + rcar_du_plane_write(rgrp, index, PnMR, pnmr);
switch (plane->format->fourcc) { case DRM_FORMAT_RGB565: colorkey = ((plane->colorkey & 0xf80000) >> 8) | ((plane->colorkey & 0x00fc00) >> 5) | ((plane->colorkey & 0x0000f8) >> 3); - rcar_du_plane_write(rcdu, index, PnTC2R, colorkey); + rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); break;
case DRM_FORMAT_ARGB1555: @@ -190,12 +192,12 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, colorkey = ((plane->colorkey & 0xf80000) >> 9) | ((plane->colorkey & 0x00f800) >> 6) | ((plane->colorkey & 0x0000f8) >> 3); - rcar_du_plane_write(rcdu, index, PnTC2R, colorkey); + rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); break;
case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: - rcar_du_plane_write(rcdu, index, PnTC3R, + rcar_du_plane_write(rgrp, index, PnTC3R, PnTC3R_CODE | (plane->colorkey & 0xffffff)); break; } @@ -204,7 +206,7 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, static void __rcar_du_plane_setup(struct rcar_du_plane *plane, unsigned int index) { - struct rcar_du_device *rcdu = plane->dev; + struct rcar_du_group *rgrp = plane->group; u32 ddcr2 = PnDDCR2_CODE; u32 ddcr4; u32 mwr; @@ -214,7 +216,7 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, * The data format is selected by the DDDF field in PnMR and the EDF * field in DDCR4. */ - ddcr4 = rcar_du_plane_read(rcdu, index, PnDDCR4); + ddcr4 = rcar_du_plane_read(rgrp, index, PnDDCR4); ddcr4 &= ~PnDDCR4_EDF_MASK; ddcr4 |= plane->format->edf | PnDDCR4_CODE;
@@ -235,8 +237,8 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, } }
- rcar_du_plane_write(rcdu, index, PnDDCR2, ddcr2); - rcar_du_plane_write(rcdu, index, PnDDCR4, ddcr4); + rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2); + rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
/* Memory pitch (expressed in pixels) */ if (plane->format->planes == 2) @@ -244,19 +246,19 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, else mwr = plane->pitch * 8 / plane->format->bpp;
- rcar_du_plane_write(rcdu, index, PnMWR, mwr); + rcar_du_plane_write(rgrp, index, PnMWR, mwr);
/* Destination position and size */ - rcar_du_plane_write(rcdu, index, PnDSXR, plane->width); - rcar_du_plane_write(rcdu, index, PnDSYR, plane->height); - rcar_du_plane_write(rcdu, index, PnDPXR, plane->dst_x); - rcar_du_plane_write(rcdu, index, PnDPYR, plane->dst_y); + rcar_du_plane_write(rgrp, index, PnDSXR, plane->width); + rcar_du_plane_write(rgrp, index, PnDSYR, plane->height); + rcar_du_plane_write(rgrp, index, PnDPXR, plane->dst_x); + rcar_du_plane_write(rgrp, index, PnDPYR, plane->dst_y);
/* Wrap-around and blinking, disabled */ - rcar_du_plane_write(rcdu, index, PnWASPR, 0); - rcar_du_plane_write(rcdu, index, PnWAMWR, 4095); - rcar_du_plane_write(rcdu, index, PnBTR, 0); - rcar_du_plane_write(rcdu, index, PnMLR, 0); + rcar_du_plane_write(rgrp, index, PnWASPR, 0); + rcar_du_plane_write(rgrp, index, PnWAMWR, 4095); + rcar_du_plane_write(rgrp, index, PnBTR, 0); + rcar_du_plane_write(rgrp, index, PnMLR, 0); }
void rcar_du_plane_setup(struct rcar_du_plane *plane) @@ -276,7 +278,7 @@ rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t src_w, uint32_t src_h) { struct rcar_du_plane *rplane = to_rcar_plane(plane); - struct rcar_du_device *rcdu = plane->dev->dev_private; + struct rcar_du_device *rcdu = rplane->group->dev; const struct rcar_du_format_info *format; unsigned int nplanes; int ret; @@ -319,26 +321,25 @@ rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, rcar_du_plane_compute_base(rplane, fb); rcar_du_plane_setup(rplane);
- mutex_lock(&rcdu->planes.lock); + mutex_lock(&rplane->group->planes.lock); rplane->enabled = true; rcar_du_crtc_update_planes(rplane->crtc); - mutex_unlock(&rcdu->planes.lock); + mutex_unlock(&rplane->group->planes.lock);
return 0; }
static int rcar_du_plane_disable(struct drm_plane *plane) { - struct rcar_du_device *rcdu = plane->dev->dev_private; struct rcar_du_plane *rplane = to_rcar_plane(plane);
if (!rplane->enabled) return 0;
- mutex_lock(&rcdu->planes.lock); + mutex_lock(&rplane->group->planes.lock); rplane->enabled = false; rcar_du_crtc_update_planes(rplane->crtc); - mutex_unlock(&rcdu->planes.lock); + mutex_unlock(&rplane->group->planes.lock);
rcar_du_plane_release(rplane);
@@ -380,9 +381,7 @@ static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane, static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane, unsigned int zpos) { - struct rcar_du_device *rcdu = plane->dev; - - mutex_lock(&rcdu->planes.lock); + mutex_lock(&plane->group->planes.lock); if (plane->zpos == zpos) goto done;
@@ -393,21 +392,21 @@ static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane, rcar_du_crtc_update_planes(plane->crtc);
done: - mutex_unlock(&rcdu->planes.lock); + mutex_unlock(&plane->group->planes.lock); }
static int rcar_du_plane_set_property(struct drm_plane *plane, struct drm_property *property, uint64_t value) { - struct rcar_du_device *rcdu = plane->dev->dev_private; struct rcar_du_plane *rplane = to_rcar_plane(plane); + struct rcar_du_group *rgrp = rplane->group;
- if (property == rcdu->planes.alpha) + if (property == rgrp->planes.alpha) rcar_du_plane_set_alpha(rplane, value); - else if (property == rcdu->planes.colorkey) + else if (property == rgrp->planes.colorkey) rcar_du_plane_set_colorkey(rplane, value); - else if (property == rcdu->planes.zpos) + else if (property == rgrp->planes.zpos) rcar_du_plane_set_zpos(rplane, value); else return -EINVAL; @@ -435,37 +434,39 @@ static const uint32_t formats[] = { DRM_FORMAT_NV16, };
-int rcar_du_planes_init(struct rcar_du_device *rcdu) +int rcar_du_planes_init(struct rcar_du_group *rgrp) { + struct rcar_du_planes *planes = &rgrp->planes; + struct rcar_du_device *rcdu = rgrp->dev; unsigned int i;
- mutex_init(&rcdu->planes.lock); - rcdu->planes.free = 0xff; + mutex_init(&planes->lock); + planes->free = 0xff;
- rcdu->planes.alpha = + planes->alpha = drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255); - if (rcdu->planes.alpha == NULL) + if (planes->alpha == NULL) return -ENOMEM;
/* The color key is expressed as an RGB888 triplet stored in a 32-bit * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0) * or enable source color keying (1). */ - rcdu->planes.colorkey = + planes->colorkey = drm_property_create_range(rcdu->ddev, 0, "colorkey", 0, 0x01ffffff); - if (rcdu->planes.colorkey == NULL) + if (planes->colorkey == NULL) return -ENOMEM;
- rcdu->planes.zpos = + planes->zpos = drm_property_create_range(rcdu->ddev, 0, "zpos", 1, 7); - if (rcdu->planes.zpos == NULL) + if (planes->zpos == NULL) return -ENOMEM;
- for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) { - struct rcar_du_plane *plane = &rcdu->planes.planes[i]; + for (i = 0; i < ARRAY_SIZE(planes->planes); ++i) { + struct rcar_du_plane *plane = &planes->planes[i];
- plane->dev = rcdu; + plane->group = rgrp; plane->hwindex = -1; plane->alpha = 255; plane->colorkey = RCAR_DU_COLORKEY_NONE; @@ -475,8 +476,10 @@ int rcar_du_planes_init(struct rcar_du_device *rcdu) return 0; }
-int rcar_du_planes_register(struct rcar_du_device *rcdu) +int rcar_du_planes_register(struct rcar_du_group *rgrp) { + struct rcar_du_planes *planes = &rgrp->planes; + struct rcar_du_device *rcdu = rgrp->dev; unsigned int i; int ret;
@@ -487,7 +490,7 @@ int rcar_du_planes_register(struct rcar_du_device *rcdu) if (plane == NULL) return -ENOMEM;
- plane->hwplane = &rcdu->planes.planes[i + 2]; + plane->hwplane = &planes->planes[i + 2]; plane->hwplane->zpos = 1;
ret = drm_plane_init(rcdu->ddev, &plane->plane, @@ -498,12 +501,12 @@ int rcar_du_planes_register(struct rcar_du_device *rcdu) return ret;
drm_object_attach_property(&plane->plane.base, - rcdu->planes.alpha, 255); + planes->alpha, 255); drm_object_attach_property(&plane->plane.base, - rcdu->planes.colorkey, + planes->colorkey, RCAR_DU_COLORKEY_NONE); drm_object_attach_property(&plane->plane.base, - rcdu->planes.zpos, 1); + planes->zpos, 1); }
return 0; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index bcf6f76..f94f9ce 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -19,8 +19,8 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h>
-struct rcar_du_device; struct rcar_du_format_info; +struct rcar_du_group;
/* The RCAR DU has 8 hardware planes, shared between KMS planes and CRTCs. As * using KMS planes requires at least one of the CRTCs being enabled, no more @@ -33,7 +33,7 @@ struct rcar_du_format_info; #define RCAR_DU_NUM_SW_PLANES 9
struct rcar_du_plane { - struct rcar_du_device *dev; + struct rcar_du_group *group; struct drm_crtc *crtc;
bool enabled; @@ -67,8 +67,8 @@ struct rcar_du_planes { struct drm_property *zpos; };
-int rcar_du_planes_init(struct rcar_du_device *rcdu); -int rcar_du_planes_register(struct rcar_du_device *rcdu); +int rcar_du_planes_init(struct rcar_du_group *rgrp); +int rcar_du_planes_register(struct rcar_du_group *rgrp);
void rcar_du_plane_setup(struct rcar_du_plane *plane); void rcar_du_plane_update_base(struct rcar_du_plane *plane);
The rcar_du_device structure contains a field that stores the number of CRTCs, use it instead of the CRTCs array size. This prepares the driver to support a variable number of CRTCs.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 2 +- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 5e4168d..fe61962 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -105,7 +105,7 @@ static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file) struct rcar_du_device *rcdu = dev->dev_private; unsigned int i;
- for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) + for (i = 0; i < rcdu->num_crtcs; ++i) rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file); }
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index c32e0f9..845bcb3 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -197,7 +197,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) if (pdata->type == RCAR_DU_ENCODER_UNUSED) continue;
- if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) { + if (pdata->output >= rcdu->num_crtcs) { dev_warn(rcdu->dev, "encoder %u references unexisting output %u, skipping\n", i, pdata->output);
Channels are accessed through a global channel memory offset, there's no need to define register addresses for the second channel.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_regs.h | 9 --------- 1 file changed, 9 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h index 3aba27f..195ed7e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h @@ -20,7 +20,6 @@ */
#define DSYSR 0x00000 /* display 1 */ -#define D2SYSR 0x30000 /* display 2 */ #define DSYSR_ILTS (1 << 29) #define DSYSR_DSEC (1 << 20) #define DSYSR_IUPD (1 << 16) @@ -35,7 +34,6 @@ #define DSYSR_SCM_INT_VIDEO (3 << 4)
#define DSMR 0x00004 -#define D2SMR 0x30004 #define DSMR_VSPM (1 << 28) #define DSMR_ODPM (1 << 27) #define DSMR_DIPM_DISP (0 << 25) @@ -60,7 +58,6 @@ #define DSMR_CSY_MASK (3 << 6)
#define DSSR 0x00008 -#define D2SSR 0x30008 #define DSSR_VC1FB_DSA0 (0 << 30) #define DSSR_VC1FB_DSA1 (1 << 30) #define DSSR_VC1FB_DSA2 (2 << 30) @@ -80,7 +77,6 @@ #define DSSR_ADC(n) (1 << ((n)-1))
#define DSRCR 0x0000c -#define D2SRCR 0x3000c #define DSRCR_TVCL (1 << 15) #define DSRCR_FRCL (1 << 14) #define DSRCR_VBCL (1 << 11) @@ -90,7 +86,6 @@ #define DSRCR_MASK 0x0000cbff
#define DIER 0x00010 -#define D2IER 0x30010 #define DIER_TVE (1 << 15) #define DIER_FRE (1 << 14) #define DIER_VBE (1 << 11) @@ -114,7 +109,6 @@ #define DPPR_BPP32 (DPPR_BPP32_P1 | DPPR_BPP32_P2) /* plane1 & 2 */
#define DEFR 0x00020 -#define D2EFR 0x30020 #define DEFR_CODE (0x7773 << 16) #define DEFR_EXSL (1 << 12) #define DEFR_EXVL (1 << 11) @@ -137,12 +131,10 @@ #define DCPCR_DCE (1 << 0)
#define DEFR2 0x00034 -#define D2EFR2 0x30034 #define DEFR2_CODE (0x7775 << 16) #define DEFR2_DEFE2G (1 << 0)
#define DEFR3 0x00038 -#define D2EFR3 0x30038 #define DEFR3_CODE (0x7776 << 16) #define DEFR3_EVDA (1 << 14) #define DEFR3_EVDM_1 (1 << 12) @@ -153,7 +145,6 @@ #define DEFR3_DEFE3 (1 << 0)
#define DEFR4 0x0003c -#define D2EFR4 0x3003c #define DEFR4_CODE (0x7777 << 16) #define DEFR4_LRUO (1 << 5) #define DEFR4_SPCE (1 << 4)
Output routing is configured in group registers, move the corresponding code from rcar_du_crtc.c to rcar_du_group.c.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 21 +-------------------- drivers/gpu/drm/rcar-du/rcar_du_group.c | 19 +++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_group.h | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 7784a3b..6a2b959 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -130,25 +130,6 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay); }
-static void rcar_du_crtc_set_routing(struct rcar_du_crtc *rcrtc) -{ - struct rcar_du_device *rcdu = rcrtc->group->dev; - u32 dorcr = rcar_du_read(rcdu, DORCR); - - dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK); - - /* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and - * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by - * default. - */ - if (rcrtc->outputs & (1 << 1) && rcrtc->index == 0) - dorcr |= DORCR_PG2D_DS1; - else - dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2; - - rcar_du_write(rcdu, DORCR, dorcr); -} - void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); @@ -245,7 +226,7 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
/* Configure display timings and output routing */ rcar_du_crtc_set_display_timing(rcrtc); - rcar_du_crtc_set_routing(rcrtc); + rcar_du_group_set_routing(rcrtc->group);
mutex_lock(&rcrtc->group->planes.lock); rcrtc->plane->enabled = true; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c index 625b9f4..7e75451 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c @@ -125,3 +125,22 @@ void rcar_du_group_restart(struct rcar_du_group *rgrp) __rcar_du_group_start_stop(rgrp, false); __rcar_du_group_start_stop(rgrp, true); } + +void rcar_du_group_set_routing(struct rcar_du_group *rgrp) +{ + struct rcar_du_crtc *crtc0 = &rgrp->dev->crtcs[rgrp->index * 2]; + u32 dorcr = rcar_du_group_read(rgrp, DORCR); + + dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK); + + /* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and + * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by + * default. + */ + if (crtc0->outputs & (1 << 1)) + dorcr |= DORCR_PG2D_DS1; + else + dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2; + + rcar_du_group_write(rgrp, DORCR, dorcr); +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.h b/drivers/gpu/drm/rcar-du/rcar_du_group.h index 748331b..180c739 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.h @@ -42,6 +42,6 @@ int rcar_du_group_get(struct rcar_du_group *rgrp); void rcar_du_group_put(struct rcar_du_group *rgrp); void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start); void rcar_du_group_restart(struct rcar_du_group *rgrp); - +void rcar_du_group_set_routing(struct rcar_du_group *rgrp);
#endif /* __RCAR_DU_GROUP_H__ */
The DU revision in the R8A7790 SoC uses one IRQ and clock per CRTC. Add a corresponding entry in the module platform ID table.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 5 +++ drivers/gpu/drm/rcar-du/rcar_du_regs.h | 66 ++++++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index fe61962..9c7406c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -215,8 +215,13 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = { .features = 0, };
+static const struct rcar_du_device_info rcar_du_r8a7790_info = { + .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK, +}; + static const struct platform_device_id rcar_du_id_table[] = { { "rcar-du-r8a7779", (kernel_ulong_t)&rcar_du_r8a7779_info }, + { "rcar-du-r8a7790", (kernel_ulong_t)&rcar_du_r8a7790_info }, { } };
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h index 195ed7e..f62a9f3 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h @@ -196,6 +196,68 @@ #define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE2)
/* ----------------------------------------------------------------------------- + * R8A7790-only Control Registers + */ + +#define DD1SSR 0x20008 +#define DD1SSR_TVR (1 << 15) +#define DD1SSR_FRM (1 << 14) +#define DD1SSR_BUF (1 << 12) +#define DD1SSR_VBK (1 << 11) +#define DD1SSR_RINT (1 << 9) +#define DD1SSR_HBK (1 << 8) +#define DD1SSR_ADC(n) (1 << ((n)-1)) + +#define DD1SRCR 0x2000c +#define DD1SRCR_TVR (1 << 15) +#define DD1SRCR_FRM (1 << 14) +#define DD1SRCR_BUF (1 << 12) +#define DD1SRCR_VBK (1 << 11) +#define DD1SRCR_RINT (1 << 9) +#define DD1SRCR_HBK (1 << 8) +#define DD1SRCR_ADC(n) (1 << ((n)-1)) + +#define DD1IER 0x20010 +#define DD1IER_TVR (1 << 15) +#define DD1IER_FRM (1 << 14) +#define DD1IER_BUF (1 << 12) +#define DD1IER_VBK (1 << 11) +#define DD1IER_RINT (1 << 9) +#define DD1IER_HBK (1 << 8) +#define DD1IER_ADC(n) (1 << ((n)-1)) + +#define DEFR8 0x20020 +#define DEFR8_CODE (0x7790 << 16) +#define DEFR8_VSCS (1 << 6) +#define DEFR8_DRGBS_DU(n) ((n) << 4) +#define DEFR8_DRGBS_MASK (3 << 4) +#define DEFR8_DEFE8 (1 << 0) + +#define DOFLR 0x20024 +#define DOFLR_CODE (0x7790 << 16) +#define DOFLR_HSYCFL1 (1 << 13) +#define DOFLR_VSYCFL1 (1 << 12) +#define DOFLR_ODDFL1 (1 << 11) +#define DOFLR_DISPFL1 (1 << 10) +#define DOFLR_CDEFL1 (1 << 9) +#define DOFLR_RGBFL1 (1 << 8) +#define DOFLR_HSYCFL0 (1 << 5) +#define DOFLR_VSYCFL0 (1 << 4) +#define DOFLR_ODDFL0 (1 << 3) +#define DOFLR_DISPFL0 (1 << 2) +#define DOFLR_CDEFL0 (1 << 1) +#define DOFLR_RGBFL0 (1 << 0) + +#define DIDSR 0x20028 +#define DIDSR_CODE (0x7790 << 16) +#define DIDSR_LCDS_DCLKIN(n) (0 << (8 + (n) * 2)) +#define DIDSR_LCDS_LVDS0(n) (2 << (8 + (n) * 2)) +#define DIDSR_LCDS_LVDS1(n) (3 << (8 + (n) * 2)) +#define DIDSR_LCDS_MASK(n) (3 << (8 + (n) * 2)) +#define DIDSR_PCDS_CLK(n, clk) (clk << ((n) * 2)) +#define DIDSR_PCDS_MASK(n) (3 << ((n) * 2)) + +/* ----------------------------------------------------------------------------- * Display Timing Generation Registers */
@@ -364,12 +426,10 @@ * Display Capture Registers */
+#define DCMR 0x0c100 #define DCMWR 0x0c104 -#define DC2MWR 0x0c204 #define DCSAR 0x0c120 -#define DC2SAR 0x0c220 #define DCMLR 0x0c150 -#define DC2MLR 0x0c250
/* ----------------------------------------------------------------------------- * Color Palette Registers
The R8A7790 DU seems to require a 128 bytes pitch alignment, even though the documentation only mentions a 16 pixels alignement as for the R8A7779 DU. Make this configurable through a device flag.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 2 +- drivers/gpu/drm/rcar-du/rcar_du_drv.h | 1 + drivers/gpu/drm/rcar-du/rcar_du_kms.c | 17 ++++++++++++++--- 3 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 9c7406c..fe93670 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -216,7 +216,7 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = { };
static const struct rcar_du_device_info rcar_du_r8a7790_info = { - .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK, + .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_ALIGN_128B, };
static const struct platform_device_id rcar_du_id_table[] = { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 5b57a2f..072e28e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -26,6 +26,7 @@ struct drm_device; struct rcar_du_device;
#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */ +#define RCAR_DU_FEATURE_ALIGN_128B (1 << 1) /* Align pitches to 128 bytes */
/* * struct rcar_du_device_info - DU model-specific information diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 845bcb3..418d902 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -111,11 +111,18 @@ const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc) int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { + struct rcar_du_device *rcdu = dev->dev_private; unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); unsigned int align;
- /* The pitch must be aligned to a 16 pixels boundary. */ - align = 16 * args->bpp / 8; + /* The R8A7779 DU requires a 16 pixels pitch alignment as documented, + * but the R8A7790 DU seems to require a 128 bytes pitch alignment. + */ + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_ALIGN_128B)) + align = 128; + else + align = 16 * args->bpp / 8; + args->pitch = roundup(max(args->pitch, min_pitch), align);
return drm_gem_cma_dumb_create(file, dev, args); @@ -125,6 +132,7 @@ static struct drm_framebuffer * rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) { + struct rcar_du_device *rcdu = dev->dev_private; const struct rcar_du_format_info *format; unsigned int align;
@@ -135,7 +143,10 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, return ERR_PTR(-EINVAL); }
- align = 16 * format->bpp / 8; + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_ALIGN_128B)) + align = 128; + else + align = 16 * format->bpp / 8;
if (mode_cmd->pitches[0] & (align - 1) || mode_cmd->pitches[0] >= 8192) {
The R8A7790 DU has 3 CRTCs, split in two groups. Support them.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 25 ++++++++++--------- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 2 ++ drivers/gpu/drm/rcar-du/rcar_du_drv.h | 6 +++-- drivers/gpu/drm/rcar-du/rcar_du_group.c | 4 +-- drivers/gpu/drm/rcar-du/rcar_du_group.h | 3 +++ drivers/gpu/drm/rcar-du/rcar_du_kms.c | 43 ++++++++++++++++++++++++--------- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 6 +++-- drivers/gpu/drm/rcar-du/rcar_du_regs.h | 4 ++- 8 files changed, 63 insertions(+), 30 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 6a2b959..a340224 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -91,7 +91,6 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) { const struct drm_display_mode *mode = &rcrtc->crtc.mode; - struct rcar_du_device *rcdu = rcrtc->group->dev; unsigned long clk; u32 value; u32 div; @@ -101,9 +100,9 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000); div = clamp(div, 1U, 64U) - 1;
- rcar_du_write(rcdu, rcrtc->index ? ESCR2 : ESCR, - ESCR_DCLKSEL_CLKS | div); - rcar_du_write(rcdu, rcrtc->index ? OTAR2 : OTAR, 0); + rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, + ESCR_DCLKSEL_CLKS | div); + rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
/* Signal polarities */ value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL) @@ -143,7 +142,6 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output) void rcar_du_crtc_update_planes(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - struct rcar_du_device *rcdu = rcrtc->group->dev; struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES]; unsigned int num_planes = 0; unsigned int prio = 0; @@ -189,8 +187,8 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) /* Select display timing and dot clock generator 2 for planes associated * with superposition controller 2. */ - if (rcrtc->index) { - u32 value = rcar_du_read(rcdu, DPTSR); + if (rcrtc->index % 2) { + u32 value = rcar_du_group_read(rcrtc->group, DPTSR);
/* The DPTSR register is updated when the display controller is * stopped. We thus need to restart the DU. Once again, sorry @@ -200,13 +198,14 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) * occur only if we need to break the pre-association. */ if (value != dptsr) { - rcar_du_write(rcdu, DPTSR, dptsr); + rcar_du_group_write(rcrtc->group, DPTSR, dptsr); if (rcrtc->group->used_crtcs) rcar_du_group_restart(rcrtc->group); } }
- rcar_du_write(rcdu, rcrtc->index ? DS2PR : DS1PR, dspr); + rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, + dspr); }
static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) @@ -528,6 +527,10 @@ static const struct drm_crtc_funcs crtc_funcs = {
int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) { + static const unsigned int mmio_offsets[] = { + DU0_REG_OFFSET, DU1_REG_OFFSET, DU2_REG_OFFSET + }; + struct rcar_du_device *rcdu = rgrp->dev; struct platform_device *pdev = to_platform_device(rcdu->dev); struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index]; @@ -553,10 +556,10 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) }
rcrtc->group = rgrp; - rcrtc->mmio_offset = index ? DISP2_REG_OFFSET : 0; + rcrtc->mmio_offset = mmio_offsets[index]; rcrtc->index = index; rcrtc->dpms = DRM_MODE_DPMS_OFF; - rcrtc->plane = &rgrp->planes.planes[index]; + rcrtc->plane = &rgrp->planes.planes[index % 2];
rcrtc->plane->crtc = crtc;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index fe93670..98e35aa 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -213,10 +213,12 @@ static int rcar_du_remove(struct platform_device *pdev)
static const struct rcar_du_device_info rcar_du_r8a7779_info = { .features = 0, + .num_crtcs = 2, };
static const struct rcar_du_device_info rcar_du_r8a7790_info = { .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_ALIGN_128B, + .num_crtcs = 3, };
static const struct platform_device_id rcar_du_id_table[] = { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 072e28e..160e5eb 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -31,9 +31,11 @@ struct rcar_du_device; /* * struct rcar_du_device_info - DU model-specific information * @features: device features (RCAR_DU_FEATURE_*) + * @num_crtcs: total number of CRTCs */ struct rcar_du_device_info { unsigned int features; + unsigned int num_crtcs; };
struct rcar_du_device { @@ -45,10 +47,10 @@ struct rcar_du_device {
struct drm_device *ddev;
- struct rcar_du_crtc crtcs[2]; + struct rcar_du_crtc crtcs[3]; unsigned int num_crtcs;
- struct rcar_du_group group; + struct rcar_du_group groups[2]; };
static inline bool rcar_du_has(struct rcar_du_device *rcdu, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c index 7e75451..0eb106e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c @@ -33,12 +33,12 @@ #include "rcar_du_group.h" #include "rcar_du_regs.h"
-static u32 rcar_du_group_read(struct rcar_du_group *rgrp, u32 reg) +u32 rcar_du_group_read(struct rcar_du_group *rgrp, u32 reg) { return rcar_du_read(rgrp->dev, rgrp->mmio_offset + reg); }
-static void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data) +void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data) { rcar_du_write(rgrp->dev, rgrp->mmio_offset + reg, data); } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.h b/drivers/gpu/drm/rcar-du/rcar_du_group.h index 180c739..4487e83 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.h @@ -38,6 +38,9 @@ struct rcar_du_group { struct rcar_du_planes planes; };
+u32 rcar_du_group_read(struct rcar_du_group *rgrp, u32 reg); +void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data); + int rcar_du_group_get(struct rcar_du_group *rgrp); void rcar_du_group_put(struct rcar_du_group *rgrp); void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 418d902..816963c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -172,8 +172,13 @@ static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
int rcar_du_modeset_init(struct rcar_du_device *rcdu) { + static const unsigned int mmio_offsets[] = { + DU0_REG_OFFSET, DU2_REG_OFFSET + }; + struct drm_device *dev = rcdu->ddev; struct drm_encoder *encoder; + unsigned int num_groups; unsigned int i; int ret;
@@ -185,22 +190,33 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) rcdu->ddev->mode_config.max_height = 2047; rcdu->ddev->mode_config.funcs = &rcar_du_mode_config_funcs;
- rcdu->group.dev = rcdu; - rcdu->group.index = 0; - rcdu->group.used_crtcs = 0; + rcdu->num_crtcs = rcdu->info->num_crtcs; + + /* Initialize the groups. */ + num_groups = DIV_ROUND_UP(rcdu->num_crtcs, 2); + + for (i = 0; i < num_groups; ++i) { + struct rcar_du_group *rgrp = &rcdu->groups[i];
- ret = rcar_du_planes_init(&rcdu->group); - if (ret < 0) - return ret; + rgrp->dev = rcdu; + rgrp->mmio_offset = mmio_offsets[i]; + rgrp->index = i;
- for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) { - ret = rcar_du_crtc_create(&rcdu->group, i); + ret = rcar_du_planes_init(rgrp); if (ret < 0) return ret; }
- rcdu->num_crtcs = i; + /* Create the CRTCs. */ + for (i = 0; i < rcdu->num_crtcs; ++i) { + struct rcar_du_group *rgrp = &rcdu->groups[i / 2]; + + ret = rcar_du_crtc_create(rgrp, i); + if (ret < 0) + return ret; + }
+ /* Initialize the encoders. */ for (i = 0; i < rcdu->pdata->num_encoders; ++i) { const struct rcar_du_encoder_data *pdata = &rcdu->pdata->encoders[i]; @@ -229,9 +245,12 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) encoder->possible_clones = 1 << 0; }
- ret = rcar_du_planes_register(&rcdu->group); - if (ret < 0) - return ret; + /* Now that the CRTCs have been initialized register the planes. */ + for (i = 0; i < num_groups; ++i) { + ret = rcar_du_planes_register(&rcdu->groups[i]); + if (ret < 0) + return ret; + }
drm_kms_helper_poll_init(rcdu->ddev);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 1e9cf7c..5300064 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -480,9 +480,12 @@ int rcar_du_planes_register(struct rcar_du_group *rgrp) { struct rcar_du_planes *planes = &rgrp->planes; struct rcar_du_device *rcdu = rgrp->dev; + unsigned int crtcs; unsigned int i; int ret;
+ crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index)); + for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) { struct rcar_du_kms_plane *plane;
@@ -493,8 +496,7 @@ int rcar_du_planes_register(struct rcar_du_group *rgrp) plane->hwplane = &planes->planes[i + 2]; plane->hwplane->zpos = 1;
- ret = drm_plane_init(rcdu->ddev, &plane->plane, - (1 << rcdu->num_crtcs) - 1, + ret = drm_plane_init(rcdu->ddev, &plane->plane, crtcs, &rcar_du_plane_funcs, formats, ARRAY_SIZE(formats), false); if (ret < 0) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h index f62a9f3..73f7347 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h @@ -13,7 +13,9 @@ #ifndef __RCAR_DU_REGS_H__ #define __RCAR_DU_REGS_H__
-#define DISP2_REG_OFFSET 0x30000 +#define DU0_REG_OFFSET 0x00000 +#define DU1_REG_OFFSET 0x30000 +#define DU2_REG_OFFSET 0x40000
/* ----------------------------------------------------------------------------- * Display Control Registers
The R8A7790 DU has a new extended function control register. Support it.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 3 ++- drivers/gpu/drm/rcar-du/rcar_du_drv.h | 1 + drivers/gpu/drm/rcar-du/rcar_du_group.c | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 98e35aa..dd1950d 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -217,7 +217,8 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = { };
static const struct rcar_du_device_info rcar_du_r8a7790_info = { - .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_ALIGN_128B, + .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_ALIGN_128B + | RCAR_DU_FEATURE_DEFR8, .num_crtcs = 3, };
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 160e5eb..70c335f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -27,6 +27,7 @@ struct rcar_du_device;
#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */ #define RCAR_DU_FEATURE_ALIGN_128B (1 << 1) /* Align pitches to 128 bytes */ +#define RCAR_DU_FEATURE_DEFR8 (1 << 2) /* Has DEFR8 register */
/* * struct rcar_du_device_info - DU model-specific information diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c index 0eb106e..f3ba0ca 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c @@ -51,6 +51,8 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp) rcar_du_group_write(rgrp, DEFR3, DEFR3_CODE | DEFR3_DEFE3); rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE); rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5); + if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_DEFR8)) + rcar_du_group_write(rgrp, DEFR8, DEFR8_CODE | DEFR8_DEFE8);
/* Use DS1PR and DS2PR to configure planes priorities and connects the * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
Split the output routing specification between SoC-internal data, specified in the rcar_du_device_info structure, and board data, passed through platform data.
The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). SoC-internal output routing data specify which output are valid, which CRTCs can be connected to the valid outputs, and the type of in-SoC encoder for the output.
Platform data then specifies external encoders and the output they are connected to.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 6 ++++-- drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 4 +++- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 30 ++++++++++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_drv.h | 16 ++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 26 +++++++++++++++++++++----- drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 5 +++-- drivers/gpu/drm/rcar-du/rcar_du_group.c | 8 ++++---- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 17 +++++++++++------ include/linux/platform_data/rcar-du.h | 17 +++++++++++++++-- 9 files changed, 107 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index a340224..680606e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -129,14 +129,16 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay); }
-void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output) +void rcar_du_crtc_route_output(struct drm_crtc *crtc, + enum rcar_du_output output) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct rcar_du_device *rcdu = rcrtc->group->dev;
/* Store the route from the CRTC output to the DU output. The DU will be * configured when starting the CRTC. */ - rcrtc->outputs |= 1 << output; + rcrtc->outputs |= BIT(output); }
void rcar_du_crtc_update_planes(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 542a7fe..39a983d 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -15,6 +15,7 @@ #define __RCAR_DU_CRTC_H__
#include <linux/mutex.h> +#include <linux/platform_data/rcar-du.h>
#include <drm/drmP.h> #include <drm/drm_crtc.h> @@ -45,7 +46,8 @@ void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc);
-void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output); +void rcar_du_crtc_route_output(struct drm_crtc *crtc, + enum rcar_du_output output); void rcar_du_crtc_update_planes(struct drm_crtc *crtc);
#endif /* __RCAR_DU_CRTC_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index dd1950d..28654e7 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -214,12 +214,42 @@ static int rcar_du_remove(struct platform_device *pdev) static const struct rcar_du_device_info rcar_du_r8a7779_info = { .features = 0, .num_crtcs = 2, + .routes = { + /* R8A7779 has two RGB outputs and one (currently unsupported) + * TCON output. + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(0), + .encoder_type = DRM_MODE_ENCODER_NONE, + }, + [RCAR_DU_OUTPUT_DPAD1] = { + .possible_crtcs = BIT(1) | BIT(0), + .encoder_type = DRM_MODE_ENCODER_NONE, + }, + }, };
static const struct rcar_du_device_info rcar_du_r8a7790_info = { .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_ALIGN_128B | RCAR_DU_FEATURE_DEFR8, .num_crtcs = 3, + .routes = { + /* R8A7790 has one RGB output, two LVDS outputs and one + * (currently unsupported) TCON output. + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(2) | BIT(1) | BIT(0), + .encoder_type = DRM_MODE_ENCODER_NONE, + }, + [RCAR_DU_OUTPUT_LVDS0] = { + .possible_crtcs = BIT(0), + .encoder_type = DRM_MODE_ENCODER_LVDS, + }, + [RCAR_DU_OUTPUT_LVDS1] = { + .possible_crtcs = BIT(2) | BIT(1), + .encoder_type = DRM_MODE_ENCODER_LVDS, + }, + }, };
static const struct platform_device_id rcar_du_id_table[] = { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 70c335f..d5243f4 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -30,13 +30,29 @@ struct rcar_du_device; #define RCAR_DU_FEATURE_DEFR8 (1 << 2) /* Has DEFR8 register */
/* + * struct rcar_du_output_routing - Output routing specification + * @possible_crtcs: bitmask of possible CRTCs for the output + * @encoder_type: DRM type of the internal encoder associated with the output + * + * The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data + * specify the valid SoC outputs, which CRTCs can drive the output, and the type + * of in-SoC encoder for the output. + */ +struct rcar_du_output_routing { + unsigned int possible_crtcs; + unsigned int encoder_type; +}; + +/* * struct rcar_du_device_info - DU model-specific information * @features: device features (RCAR_DU_FEATURE_*) * @num_crtcs: total number of CRTCs + * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*) */ struct rcar_du_device_info { unsigned int features; unsigned int num_crtcs; + struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX]; };
struct rcar_du_device { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 0d0375c..2aac28d 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -115,10 +115,12 @@ static const struct drm_encoder_funcs encoder_funcs = { };
int rcar_du_encoder_init(struct rcar_du_device *rcdu, - enum rcar_du_encoder_type type, unsigned int output, + enum rcar_du_encoder_type type, + enum rcar_du_output output, const struct rcar_du_encoder_data *data) { struct rcar_du_encoder *renc; + unsigned int encoder_type; int ret;
renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); @@ -127,19 +129,33 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
renc->output = output;
+ switch (type) { + case RCAR_DU_ENCODER_VGA: + encoder_type = DRM_MODE_ENCODER_DAC; + break; + case RCAR_DU_ENCODER_LVDS: + encoder_type = DRM_MODE_ENCODER_LVDS; + break; + case RCAR_DU_ENCODER_NONE: + default: + /* No external encoder, use the internal encoder type. */ + encoder_type = rcdu->info->routes[output].encoder_type; + break; + } + ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs, - type); + encoder_type); if (ret < 0) return ret;
drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
- switch (type) { - case RCAR_DU_ENCODER_LVDS: + switch (encoder_type) { + case DRM_MODE_ENCODER_LVDS: return rcar_du_lvds_connector_init(rcdu, renc, &data->connector.lvds.panel);
- case RCAR_DU_ENCODER_VGA: + case DRM_MODE_ENCODER_DAC: return rcar_du_vga_connector_init(rcdu, renc);
default: diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 08cde12..2310416 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -22,7 +22,7 @@ struct rcar_du_device;
struct rcar_du_encoder { struct drm_encoder encoder; - unsigned int output; + enum rcar_du_output output; };
#define to_rcar_encoder(e) \ @@ -40,7 +40,8 @@ struct drm_encoder * rcar_du_connector_best_encoder(struct drm_connector *connector);
int rcar_du_encoder_init(struct rcar_du_device *rcdu, - enum rcar_du_encoder_type type, unsigned int output, + enum rcar_du_encoder_type type, + enum rcar_du_output output, const struct rcar_du_encoder_data *data);
#endif /* __RCAR_DU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c index f3ba0ca..9df6fb6 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c @@ -135,11 +135,11 @@ void rcar_du_group_set_routing(struct rcar_du_group *rgrp)
dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK);
- /* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and - * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by - * default. + /* Set the DPAD1 pins sources. Select CRTC 0 if explicitly requested and + * CRTC 1 in all other cases to avoid cloning CRTC 0 to DPAD0 and DPAD1 + * by default. */ - if (crtc0->outputs & (1 << 1)) + if (crtc0->outputs & BIT(RCAR_DU_OUTPUT_DPAD1)) dorcr |= DORCR_PG2D_DS1; else dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 816963c..2b92e68 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -220,11 +220,14 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) for (i = 0; i < rcdu->pdata->num_encoders; ++i) { const struct rcar_du_encoder_data *pdata = &rcdu->pdata->encoders[i]; + const struct rcar_du_output_routing *route = + &rcdu->info->routes[pdata->output];
if (pdata->type == RCAR_DU_ENCODER_UNUSED) continue;
- if (pdata->output >= rcdu->num_crtcs) { + if (pdata->output >= RCAR_DU_OUTPUT_MAX || + route->possible_crtcs == 0) { dev_warn(rcdu->dev, "encoder %u references unexisting output %u, skipping\n", i, pdata->output); @@ -234,15 +237,17 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) rcar_du_encoder_init(rcdu, pdata->type, pdata->output, pdata); }
- /* Set the possible CRTCs and possible clones. All encoders can be - * driven by the CRTC associated with the output they're connected to, - * as well as by CRTC 0. + /* Set the possible CRTCs and possible clones. There's always at least + * one way for all encoders to clone each other, set all bits in the + * possible clones field. */ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + const struct rcar_du_output_routing *route = + &rcdu->info->routes[renc->output];
- encoder->possible_crtcs = (1 << 0) | (1 << renc->output); - encoder->possible_clones = 1 << 0; + encoder->possible_crtcs = route->possible_crtcs; + encoder->possible_clones = (1 << rcdu->pdata->num_encoders) - 1; }
/* Now that the CRTCs have been initialized register the planes. */ diff --git a/include/linux/platform_data/rcar-du.h b/include/linux/platform_data/rcar-du.h index 64cd863..1a2e990 100644 --- a/include/linux/platform_data/rcar-du.h +++ b/include/linux/platform_data/rcar-du.h @@ -16,8 +16,18 @@
#include <drm/drm_mode.h>
+enum rcar_du_output { + RCAR_DU_OUTPUT_DPAD0, + RCAR_DU_OUTPUT_DPAD1, + RCAR_DU_OUTPUT_LVDS0, + RCAR_DU_OUTPUT_LVDS1, + RCAR_DU_OUTPUT_TCON, + RCAR_DU_OUTPUT_MAX, +}; + enum rcar_du_encoder_type { RCAR_DU_ENCODER_UNUSED = 0, + RCAR_DU_ENCODER_NONE, RCAR_DU_ENCODER_VGA, RCAR_DU_ENCODER_LVDS, }; @@ -39,13 +49,16 @@ struct rcar_du_connector_vga_data { /* * struct rcar_du_encoder_data - Encoder platform data * @type: the encoder type (RCAR_DU_ENCODER_*) - * @output: the DU output the connector is connected to + * @output: the DU output the connector is connected to (RCAR_DU_OUTPUT_*) * @connector.lvds: platform data for LVDS connectors * @connector.vga: platform data for VGA connectors + * + * Encoder platform data describes an on-board encoder, its associated DU SoC + * output, and the connector. */ struct rcar_du_encoder_data { enum rcar_du_encoder_type type; - unsigned int output; + enum rcar_du_output output;
union { struct rcar_du_connector_lvds_data lvds;
The R8A7790 DU variant has a single RGB output called DPAD0 that can be fed with the output of DU0, DU1 or DU2. Making the routing configurable.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 5 ++++ drivers/gpu/drm/rcar-du/rcar_du_drv.h | 2 ++ drivers/gpu/drm/rcar-du/rcar_du_group.c | 45 ++++++++++++++++++++++++++++++--- drivers/gpu/drm/rcar-du/rcar_du_group.h | 2 +- 4 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 680606e..245800d 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -139,6 +139,11 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc, * configured when starting the CRTC. */ rcrtc->outputs |= BIT(output); + + /* Store RGB routing to DPAD0 for R8A7790. */ + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_DEFR8) && + output == RCAR_DU_OUTPUT_DPAD0) + rcdu->dpad0_source = rcrtc->index; }
void rcar_du_crtc_update_planes(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index d5243f4..924f5e0 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -68,6 +68,8 @@ struct rcar_du_device { unsigned int num_crtcs;
struct rcar_du_group groups[2]; + + unsigned int dpad0_source; };
static inline bool rcar_du_has(struct rcar_du_device *rcdu, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c index 9df6fb6..eb53cd9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c @@ -27,6 +27,7 @@ * counterpart in the DU documentation, that models those semi-global resources. */
+#include <linux/clk.h> #include <linux/io.h>
#include "rcar_du_drv.h" @@ -43,6 +44,22 @@ void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data) rcar_du_write(rgrp->dev, rgrp->mmio_offset + reg, data); }
+static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp) +{ + u32 defr8 = DEFR8_CODE | DEFR8_DEFE8; + + if (!rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_DEFR8)) + return; + + /* The DEFR8 register for the first group also controls RGB output + * routing to DPAD0 + */ + if (rgrp->index == 0) + defr8 |= DEFR8_DRGBS_DU(rgrp->dev->dpad0_source); + + rcar_du_group_write(rgrp, DEFR8, defr8); +} + static void rcar_du_group_setup(struct rcar_du_group *rgrp) { /* Enable extended features */ @@ -51,8 +68,8 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp) rcar_du_group_write(rgrp, DEFR3, DEFR3_CODE | DEFR3_DEFE3); rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE); rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5); - if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_DEFR8)) - rcar_du_group_write(rgrp, DEFR8, DEFR8_CODE | DEFR8_DEFE8); + + rcar_du_group_setup_defr8(rgrp);
/* Use DS1PR and DS2PR to configure planes priorities and connects the * superposition 0 to DU0 pins. DU1 pins will be configured dynamically. @@ -128,7 +145,27 @@ void rcar_du_group_restart(struct rcar_du_group *rgrp) __rcar_du_group_start_stop(rgrp, true); }
-void rcar_du_group_set_routing(struct rcar_du_group *rgrp) +static int rcar_du_set_dpad0_routing(struct rcar_du_device *rcdu) +{ + int ret; + + /* RGB output routing to DPAD0 is configured in the DEFR8 register of + * the first group. As this function can be called with the DU0 and DU1 + * CRTCs disabled, we need to enable the first group clock before + * accessing the register. + */ + ret = clk_prepare_enable(rcdu->crtcs[0].clock); + if (ret < 0) + return ret; + + rcar_du_group_setup_defr8(&rcdu->groups[0]); + + clk_disable_unprepare(rcdu->crtcs[0].clock); + + return 0; +} + +int rcar_du_group_set_routing(struct rcar_du_group *rgrp) { struct rcar_du_crtc *crtc0 = &rgrp->dev->crtcs[rgrp->index * 2]; u32 dorcr = rcar_du_group_read(rgrp, DORCR); @@ -145,4 +182,6 @@ void rcar_du_group_set_routing(struct rcar_du_group *rgrp) dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2;
rcar_du_group_write(rgrp, DORCR, dorcr); + + return rcar_du_set_dpad0_routing(rgrp->dev); } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.h b/drivers/gpu/drm/rcar-du/rcar_du_group.h index 4487e83..5025930 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.h @@ -45,6 +45,6 @@ int rcar_du_group_get(struct rcar_du_group *rgrp); void rcar_du_group_put(struct rcar_du_group *rgrp); void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start); void rcar_du_group_restart(struct rcar_du_group *rgrp); -void rcar_du_group_set_routing(struct rcar_du_group *rgrp); +int rcar_du_group_set_routing(struct rcar_du_group *rgrp);
#endif /* __RCAR_DU_GROUP_H__ */
The R8A7790 includes two internal LVDS encoders. Support them in the DU driver.
Signed-off-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/rcar-du/Kconfig | 7 ++ drivers/gpu/drm/rcar-du/Makefile | 4 +- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 2 - drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 2 + drivers/gpu/drm/rcar-du/rcar_du_drv.c | 2 + drivers/gpu/drm/rcar-du/rcar_du_drv.h | 4 + drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 38 ++++++ drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 2 + drivers/gpu/drm/rcar-du/rcar_du_kms.c | 5 + drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c | 194 ++++++++++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h | 46 +++++++ drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | 69 +++++++++++ 12 files changed, 372 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h create mode 100644 drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig index 72887df..c590cd9 100644 --- a/drivers/gpu/drm/rcar-du/Kconfig +++ b/drivers/gpu/drm/rcar-du/Kconfig @@ -7,3 +7,10 @@ config DRM_RCAR_DU help Choose this option if you have an R-Car chipset. If M is selected the module will be called rcar-du-drm. + +config DRM_RCAR_LVDS + bool "R-Car DU LVDS Encoder Support" + depends on DRM_RCAR_DU + help + Enable support the R-Car Display Unit embedded LVDS encoders + (currently only on R8A7790). diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile index b9b5e66..12b8d44 100644 --- a/drivers/gpu/drm/rcar-du/Makefile +++ b/drivers/gpu/drm/rcar-du/Makefile @@ -7,4 +7,6 @@ rcar-du-drm-y := rcar_du_crtc.o \ rcar_du_plane.o \ rcar_du_vgacon.o
-obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o +rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o + +obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 245800d..33df7a5 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -26,8 +26,6 @@ #include "rcar_du_plane.h" #include "rcar_du_regs.h"
-#define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) - static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg) { struct rcar_du_device *rcdu = rcrtc->group->dev; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 39a983d..43e7575 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -39,6 +39,8 @@ struct rcar_du_crtc { struct rcar_du_plane *plane; };
+#define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) + int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index); void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable); void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 28654e7..53cc827 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -227,6 +227,7 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = { .encoder_type = DRM_MODE_ENCODER_NONE, }, }, + .num_lvds = 0, };
static const struct rcar_du_device_info rcar_du_r8a7790_info = { @@ -250,6 +251,7 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = { .encoder_type = DRM_MODE_ENCODER_LVDS, }, }, + .num_lvds = 2, };
static const struct platform_device_id rcar_du_id_table[] = { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 924f5e0..050d71c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -24,6 +24,7 @@ struct clk; struct device; struct drm_device; struct rcar_du_device; +struct rcar_du_lvdsenc;
#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */ #define RCAR_DU_FEATURE_ALIGN_128B (1 << 1) /* Align pitches to 128 bytes */ @@ -48,11 +49,13 @@ struct rcar_du_output_routing { * @features: device features (RCAR_DU_FEATURE_*) * @num_crtcs: total number of CRTCs * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*) + * @num_lvds: number of internal LVDS encoders */ struct rcar_du_device_info { unsigned int features; unsigned int num_crtcs; struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX]; + unsigned int num_lvds; };
struct rcar_du_device { @@ -70,6 +73,7 @@ struct rcar_du_device { struct rcar_du_group groups[2];
unsigned int dpad0_source; + struct rcar_du_lvdsenc *lvds[2]; };
static inline bool rcar_du_has(struct rcar_du_device *rcdu, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 2aac28d..3daa7a1 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -11,6 +11,8 @@ * (at your option) any later version. */
+#include <linux/export.h> + #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> @@ -19,6 +21,7 @@ #include "rcar_du_encoder.h" #include "rcar_du_kms.h" #include "rcar_du_lvdscon.h" +#include "rcar_du_lvdsenc.h" #include "rcar_du_vgacon.h"
/* ----------------------------------------------------------------------------- @@ -39,12 +42,17 @@ rcar_du_connector_best_encoder(struct drm_connector *connector)
static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode) { + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + + if (renc->lvds) + rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, mode); }
static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); const struct drm_display_mode *panel_mode; struct drm_device *dev = encoder->dev; struct drm_connector *connector; @@ -82,15 +90,32 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, /* The flat panel mode is fixed, just copy it to the adjusted mode. */ drm_mode_copy(adjusted_mode, panel_mode);
+ /* The internal LVDS encoder has a clock frequency operating range of + * 30MHz to 150MHz. Clamp the clock accordingly. + */ + if (renc->lvds) + adjusted_mode->clock = clamp(adjusted_mode->clock, + 30000, 150000); + return true; }
static void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder) { + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + + if (renc->lvds) + rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, + DRM_MODE_DPMS_OFF); }
static void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) { + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + + if (renc->lvds) + rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, + DRM_MODE_DPMS_ON); }
static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, @@ -129,6 +154,19 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
renc->output = output;
+ switch (output) { + case RCAR_DU_OUTPUT_LVDS0: + renc->lvds = rcdu->lvds[0]; + break; + + case RCAR_DU_OUTPUT_LVDS1: + renc->lvds = rcdu->lvds[1]; + break; + + default: + break; + } + switch (type) { case RCAR_DU_ENCODER_VGA: encoder_type = DRM_MODE_ENCODER_DAC; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 2310416..0e5a65e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -19,10 +19,12 @@ #include <drm/drm_crtc.h>
struct rcar_du_device; +struct rcar_du_lvdsenc;
struct rcar_du_encoder { struct drm_encoder encoder; enum rcar_du_output output; + struct rcar_du_lvdsenc *lvds; };
#define to_rcar_encoder(e) \ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 2b92e68..cc71b1a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -21,6 +21,7 @@ #include "rcar_du_drv.h" #include "rcar_du_encoder.h" #include "rcar_du_kms.h" +#include "rcar_du_lvdsenc.h" #include "rcar_du_regs.h"
/* ----------------------------------------------------------------------------- @@ -217,6 +218,10 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) }
/* Initialize the encoders. */ + ret = rcar_du_lvdsenc_init(rcdu); + if (ret < 0) + return ret; + for (i = 0; i < rcdu->pdata->num_encoders; ++i) { const struct rcar_du_encoder_data *pdata = &rcdu->pdata->encoders[i]; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c new file mode 100644 index 0000000..4b53108 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c @@ -0,0 +1,194 @@ +/* + * rcar_du_lvdsenc.c -- R-Car Display Unit LVDS Encoder + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "rcar_du_drv.h" +#include "rcar_du_encoder.h" +#include "rcar_du_lvdsenc.h" +#include "rcar_lvds_regs.h" + +struct rcar_du_lvdsenc { + struct rcar_du_device *dev; + + unsigned int index; + void __iomem *mmio; + struct clk *clock; + int dpms; + + enum rcar_lvds_input input; +}; + +static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data) +{ + iowrite32(data, lvds->mmio + reg); +} + +static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, + struct rcar_du_crtc *rcrtc) +{ + const struct drm_display_mode *mode = &rcrtc->crtc.mode; + unsigned int freq = mode->clock; + u32 lvdcr0; + u32 pllcr; + int ret; + + if (lvds->dpms == DRM_MODE_DPMS_ON) + return 0; + + ret = clk_prepare_enable(lvds->clock); + if (ret < 0) + return ret; + + /* PLL clock configuration */ + if (freq <= 38000) + pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; + else if (freq <= 60000) + pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; + else if (freq <= 121000) + pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; + else + pllcr = LVDPLLCR_PLLDLYCNT_150M; + + rcar_lvds_write(lvds, LVDPLLCR, pllcr); + + /* Hardcode the channels and control signals routing for now. + * + * HSYNC -> CTRL0 + * VSYNC -> CTRL1 + * DISP -> CTRL2 + * 0 -> CTRL3 + * + * Channels 1 and 3 are switched on ES1. + */ + rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO | + LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC | + LVDCTRCR_CTR0SEL_HSYNC); + rcar_lvds_write(lvds, LVDCHCR, + LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3) | + LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1)); + + /* Select the input, hardcode mode 0, enable LVDS operation and turn + * bias circuitry on. + */ + lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN; + if (rcrtc->index == 2) + lvdcr0 |= LVDCR0_DUSEL; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + /* Turn all the channels on. */ + rcar_lvds_write(lvds, LVDCR1, LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) | + LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY); + + /* Turn the PLL on, wait for the startup delay, and turn the output on. */ + lvdcr0 |= LVDCR0_PLLEN; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + usleep_range(100, 150); + + lvdcr0 |= LVDCR0_LVRES; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + lvds->dpms = DRM_MODE_DPMS_ON; + return 0; +} + +static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds) +{ + if (lvds->dpms == DRM_MODE_DPMS_OFF) + return; + + rcar_lvds_write(lvds, LVDCR0, 0); + rcar_lvds_write(lvds, LVDCR1, 0); + + clk_disable_unprepare(lvds->clock); + + lvds->dpms = DRM_MODE_DPMS_OFF; +} + +int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, + struct drm_crtc *crtc, int mode) +{ + if (mode == DRM_MODE_DPMS_OFF) { + rcar_du_lvdsenc_stop(lvds); + return 0; + } else if (crtc) { + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + return rcar_du_lvdsenc_start(lvds, rcrtc); + } else + return -EINVAL; +} + +static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds, + struct platform_device *pdev) +{ + struct resource *mem; + char name[7]; + + sprintf(name, "lvds.%u", lvds->index); + + mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (mem == NULL) { + dev_err(&pdev->dev, "failed to get memory resource for %s\n", + name); + return -EINVAL; + } + + lvds->mmio = devm_ioremap_resource(&pdev->dev, mem); + if (lvds->mmio == NULL) { + dev_err(&pdev->dev, "failed to remap memory resource for %s\n", + name); + return -ENOMEM; + } + + lvds->clock = devm_clk_get(&pdev->dev, name); + if (IS_ERR(lvds->clock)) { + dev_err(&pdev->dev, "failed to get clock for %s\n", name); + return PTR_ERR(lvds->clock); + } + + return 0; +} + +int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) +{ + struct platform_device *pdev = to_platform_device(rcdu->dev); + struct rcar_du_lvdsenc *lvds; + unsigned int i; + int ret; + + for (i = 0; i < rcdu->info->num_lvds; ++i) { + lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); + if (lvds == NULL) { + dev_err(&pdev->dev, "failed to allocate private data\n"); + return -ENOMEM; + } + + lvds->dev = rcdu; + lvds->index = i; + lvds->input = i ? RCAR_LVDS_INPUT_DU1 : RCAR_LVDS_INPUT_DU0; + lvds->dpms = DRM_MODE_DPMS_OFF; + + ret = rcar_du_lvdsenc_get_resources(lvds, pdev); + if (ret < 0) + return ret; + + rcdu->lvds[i] = lvds; + } + + return 0; +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h new file mode 100644 index 0000000..7051c6d --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h @@ -0,0 +1,46 @@ +/* + * rcar_du_lvdsenc.h -- R-Car Display Unit LVDS Encoder + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_LVDSENC_H__ +#define __RCAR_DU_LVDSENC_H__ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_data/rcar-du.h> + +struct rcar_drm_crtc; +struct rcar_du_lvdsenc; + +enum rcar_lvds_input { + RCAR_LVDS_INPUT_DU0, + RCAR_LVDS_INPUT_DU1, + RCAR_LVDS_INPUT_DU2, +}; + +#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS) +int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu); +int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, + struct drm_crtc *crtc, int mode); +#else +static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) +{ + return 0; +} +static inline int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, + struct drm_crtc *crtc, int mode) +{ + return 0; +} +#endif + +#endif /* __RCAR_DU_LVDSENC_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h new file mode 100644 index 0000000..77cf928 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h @@ -0,0 +1,69 @@ +/* + * rcar_lvds_regs.h -- R-Car LVDS Interface Registers Definitions + * + * Copyright (C) 2013 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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. + */ + +#ifndef __RCAR_LVDS_REGS_H__ +#define __RCAR_LVDS_REGS_H__ + +#define LVDCR0 0x0000 +#define LVDCR0_DUSEL (1 << 15) +#define LVDCR0_DMD (1 << 12) +#define LVDCR0_LVMD_MASK (0xf << 8) +#define LVDCR0_LVMD_SHIFT 8 +#define LVDCR0_PLLEN (1 << 4) +#define LVDCR0_BEN (1 << 2) +#define LVDCR0_LVEN (1 << 1) +#define LVDCR0_LVRES (1 << 0) + +#define LVDCR1 0x0004 +#define LVDCR1_CKSEL (1 << 15) +#define LVDCR1_CHSTBY(n) (3 << (2 + (n) * 2)) +#define LVDCR1_CLKSTBY (3 << 0) + +#define LVDPLLCR 0x0008 +#define LVDPLLCR_CEEN (1 << 14) +#define LVDPLLCR_FBEN (1 << 13) +#define LVDPLLCR_COSEL (1 << 12) +#define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0) +#define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0) +#define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0) +#define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0) +#define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0) + +#define LVDCTRCR 0x000c +#define LVDCTRCR_CTR3SEL_ZERO (0 << 12) +#define LVDCTRCR_CTR3SEL_ODD (1 << 12) +#define LVDCTRCR_CTR3SEL_CDE (2 << 12) +#define LVDCTRCR_CTR3SEL_MASK (7 << 12) +#define LVDCTRCR_CTR2SEL_DISP (0 << 8) +#define LVDCTRCR_CTR2SEL_ODD (1 << 8) +#define LVDCTRCR_CTR2SEL_CDE (2 << 8) +#define LVDCTRCR_CTR2SEL_HSYNC (3 << 8) +#define LVDCTRCR_CTR2SEL_VSYNC (4 << 8) +#define LVDCTRCR_CTR2SEL_MASK (7 << 8) +#define LVDCTRCR_CTR1SEL_VSYNC (0 << 4) +#define LVDCTRCR_CTR1SEL_DISP (1 << 4) +#define LVDCTRCR_CTR1SEL_ODD (2 << 4) +#define LVDCTRCR_CTR1SEL_CDE (3 << 4) +#define LVDCTRCR_CTR1SEL_HSYNC (4 << 4) +#define LVDCTRCR_CTR1SEL_MASK (7 << 4) +#define LVDCTRCR_CTR0SEL_HSYNC (0 << 0) +#define LVDCTRCR_CTR0SEL_VSYNC (1 << 0) +#define LVDCTRCR_CTR0SEL_DISP (2 << 0) +#define LVDCTRCR_CTR0SEL_ODD (3 << 0) +#define LVDCTRCR_CTR0SEL_CDE (4 << 0) +#define LVDCTRCR_CTR0SEL_MASK (7 << 0) + +#define LVDCHCR 0x0010 +#define LVDCHCR_CHSEL_CH(n, c) ((((c) - (n)) & 3) << ((n) * 4)) +#define LVDCHCR_CHSEL_MASK(n) (3 << ((n) * 4)) + +#endif /* __RCAR_LVDS_REGS_H__ */
dri-devel@lists.freedesktop.org