Commit f66ee304 authored by Laurent Pinchart's avatar Laurent Pinchart

drm/rcar-du: Support per-CRTC clock and IRQ

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: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
parent 481d342e
...@@ -69,6 +69,30 @@ static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg, ...@@ -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); 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) static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
{ {
struct drm_crtc *crtc = &rcrtc->crtc; struct drm_crtc *crtc = &rcrtc->crtc;
...@@ -79,7 +103,7 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) ...@@ -79,7 +103,7 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
u32 div; u32 div;
/* Dot clock */ /* Dot clock */
clk = clk_get_rate(rcdu->clock); clk = clk_get_rate(rcrtc->clock);
div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000); div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000);
div = clamp(div, 1U, 64U) - 1; div = clamp(div, 1U, 64U) - 1;
...@@ -313,20 +337,16 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) ...@@ -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) 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_crtc_stop(rcrtc);
rcar_du_put(rcdu); rcar_du_crtc_put(rcrtc);
} }
void rcar_du_crtc_resume(struct rcar_du_crtc *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) if (rcrtc->dpms != DRM_MODE_DPMS_ON)
return; return;
rcar_du_get(rcdu); rcar_du_crtc_get(rcrtc);
rcar_du_crtc_start(rcrtc); rcar_du_crtc_start(rcrtc);
} }
...@@ -340,18 +360,17 @@ static void rcar_du_crtc_update_base(struct rcar_du_crtc *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) 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); struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
if (rcrtc->dpms == mode) if (rcrtc->dpms == mode)
return; return;
if (mode == DRM_MODE_DPMS_ON) { if (mode == DRM_MODE_DPMS_ON) {
rcar_du_get(rcdu); rcar_du_crtc_get(rcrtc);
rcar_du_crtc_start(rcrtc); rcar_du_crtc_start(rcrtc);
} else { } else {
rcar_du_crtc_stop(rcrtc); rcar_du_crtc_stop(rcrtc);
rcar_du_put(rcdu); rcar_du_crtc_put(rcrtc);
} }
rcrtc->dpms = mode; rcrtc->dpms = mode;
...@@ -367,13 +386,12 @@ static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc, ...@@ -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) 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); struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
/* We need to access the hardware during mode set, acquire a reference /* 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 /* Stop the CRTC and release the plane. Force the DPMS mode to off as a
* result. * result.
...@@ -423,10 +441,10 @@ static int rcar_du_crtc_mode_set(struct drm_crtc *crtc, ...@@ -423,10 +441,10 @@ static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
error: error:
/* There's no rollback/abort operation to clean up in case of error. We /* 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. * here.
*/ */
rcar_du_put(rcdu); rcar_du_crtc_put(rcrtc);
return ret; return ret;
} }
...@@ -514,6 +532,24 @@ static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc) ...@@ -514,6 +532,24 @@ static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
drm_vblank_put(dev, rcrtc->index); 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, static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb, struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event) struct drm_pending_vblank_event *event)
...@@ -551,10 +587,29 @@ static const struct drm_crtc_funcs crtc_funcs = { ...@@ -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) 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 rcar_du_crtc *rcrtc = &rcdu->crtcs[index];
struct drm_crtc *crtc = &rcrtc->crtc; struct drm_crtc *crtc = &rcrtc->crtc;
unsigned int irqflags;
char clk_name[5];
char *name;
int irq;
int ret; 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->mmio_offset = index ? DISP2_REG_OFFSET : 0;
rcrtc->index = index; rcrtc->index = index;
rcrtc->dpms = DRM_MODE_DPMS_OFF; rcrtc->dpms = DRM_MODE_DPMS_OFF;
...@@ -568,6 +623,28 @@ int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index) ...@@ -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); 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; return 0;
} }
...@@ -580,16 +657,3 @@ void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable) ...@@ -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); 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);
}
}
...@@ -25,6 +25,7 @@ struct rcar_du_plane; ...@@ -25,6 +25,7 @@ struct rcar_du_plane;
struct rcar_du_crtc { struct rcar_du_crtc {
struct drm_crtc crtc; struct drm_crtc crtc;
struct clk *clock;
unsigned int mmio_offset; unsigned int mmio_offset;
unsigned int index; unsigned int index;
bool started; bool started;
...@@ -38,7 +39,6 @@ struct rcar_du_crtc { ...@@ -38,7 +39,6 @@ struct rcar_du_crtc {
int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index); 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_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, void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
struct drm_file *file); struct drm_file *file);
void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc);
......
...@@ -35,8 +35,8 @@ ...@@ -35,8 +35,8 @@
/* /*
* rcar_du_get - Acquire a reference to the DU * rcar_du_get - Acquire a reference to the DU
* *
* Acquiring a reference enables the device clock and setup core registers. A * Acquiring the first reference setups core registers. A reference must be
* reference must be held before accessing any hardware registers. * held before accessing any hardware registers.
* *
* This function must be called with the DRM mode_config lock held. * This function must be called with the DRM mode_config lock held.
* *
...@@ -44,16 +44,9 @@ ...@@ -44,16 +44,9 @@
*/ */
int rcar_du_get(struct rcar_du_device *rcdu) int rcar_du_get(struct rcar_du_device *rcdu)
{ {
int ret;
if (rcdu->use_count) if (rcdu->use_count)
goto done; goto done;
/* Enable clocks before accessing the hardware. */
ret = clk_prepare_enable(rcdu->clock);
if (ret < 0)
return ret;
/* Enable extended features */ /* Enable extended features */
rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE); rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE);
rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G); rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G);
...@@ -74,16 +67,11 @@ int rcar_du_get(struct rcar_du_device *rcdu) ...@@ -74,16 +67,11 @@ int rcar_du_get(struct rcar_du_device *rcdu)
/* /*
* rcar_du_put - Release a reference to the DU * 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. * This function must be called with the DRM mode_config lock held.
*/ */
void rcar_du_put(struct rcar_du_device *rcdu) void rcar_du_put(struct rcar_du_device *rcdu)
{ {
if (--rcdu->use_count) --rcdu->use_count;
return;
clk_disable_unprepare(rcdu->clock);
} }
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
...@@ -95,8 +83,8 @@ static int rcar_du_unload(struct drm_device *dev) ...@@ -95,8 +83,8 @@ static int rcar_du_unload(struct drm_device *dev)
drm_kms_helper_poll_fini(dev); drm_kms_helper_poll_fini(dev);
drm_mode_config_cleanup(dev); drm_mode_config_cleanup(dev);
drm_vblank_cleanup(dev); drm_vblank_cleanup(dev);
drm_irq_uninstall(dev);
dev->irq_enabled = 0;
dev->dev_private = NULL; dev->dev_private = NULL;
return 0; return 0;
...@@ -127,18 +115,12 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) ...@@ -127,18 +115,12 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
rcdu->ddev = dev; rcdu->ddev = dev;
dev->dev_private = rcdu; dev->dev_private = rcdu;
/* I/O resources and clocks */ /* I/O resources */
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem); rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(rcdu->mmio)) if (IS_ERR(rcdu->mmio))
return PTR_ERR(rcdu->mmio); return PTR_ERR(rcdu->mmio);
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 */ /* DRM/KMS objects */
ret = rcar_du_modeset_init(rcdu); ret = rcar_du_modeset_init(rcdu);
if (ret < 0) { if (ret < 0) {
...@@ -146,18 +128,14 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) ...@@ -146,18 +128,14 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
goto done; goto done;
} }
/* IRQ and vblank handling */ /* vblank handling */
ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1); ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to initialize vblank\n"); dev_err(&pdev->dev, "failed to initialize vblank\n");
goto done; goto done;
} }
ret = drm_irq_install(dev); dev->irq_enabled = 1;
if (ret < 0) {
dev_err(&pdev->dev, "failed to install IRQ handler\n");
goto done;
}
platform_set_drvdata(pdev, rcdu); platform_set_drvdata(pdev, rcdu);
...@@ -177,18 +155,6 @@ static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file) ...@@ -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); 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) static int rcar_du_enable_vblank(struct drm_device *dev, int crtc)
{ {
struct rcar_du_device *rcdu = dev->dev_private; struct rcar_du_device *rcdu = dev->dev_private;
...@@ -221,12 +187,10 @@ static const struct file_operations rcar_du_fops = { ...@@ -221,12 +187,10 @@ static const struct file_operations rcar_du_fops = {
}; };
static struct drm_driver rcar_du_driver = { static struct drm_driver rcar_du_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME,
| DRIVER_PRIME,
.load = rcar_du_load, .load = rcar_du_load,
.unload = rcar_du_unload, .unload = rcar_du_unload,
.preclose = rcar_du_preclose, .preclose = rcar_du_preclose,
.irq_handler = rcar_du_irq,
.get_vblank_counter = drm_vblank_count, .get_vblank_counter = drm_vblank_count,
.enable_vblank = rcar_du_enable_vblank, .enable_vblank = rcar_du_enable_vblank,
.disable_vblank = rcar_du_disable_vblank, .disable_vblank = rcar_du_disable_vblank,
......
...@@ -25,6 +25,8 @@ struct clk; ...@@ -25,6 +25,8 @@ struct clk;
struct device; struct device;
struct drm_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 * struct rcar_du_device_info - DU model-specific information
* @features: device features (RCAR_DU_FEATURE_*) * @features: device features (RCAR_DU_FEATURE_*)
...@@ -39,7 +41,6 @@ struct rcar_du_device { ...@@ -39,7 +41,6 @@ struct rcar_du_device {
const struct rcar_du_device_info *info; const struct rcar_du_device_info *info;
void __iomem *mmio; void __iomem *mmio;
struct clk *clock;
unsigned int use_count; unsigned int use_count;
struct drm_device *ddev; struct drm_device *ddev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment