Merge branch 'drm/next/du' of git://linuxtv.org/pinchartl/fbdev into drm-rcar-stable

Create topic branch for rcar for shmobile tree to pull as well, arm-soc should
probably merge after drm merges if possible.

Signed-off-by: Dave Airlie <airlied@redhat.com>
* 'drm/next/du' of git://linuxtv.org/pinchartl/fbdev: (23 commits)
  drm/rcar-du: Add FBDEV emulation support
  drm/rcar-du: Add internal LVDS encoder support
  drm/rcar-du: Configure RGB output routing to DPAD0
  drm/rcar-du: Rework output routing support
  drm/rcar-du: Add support for DEFR8 register
  drm/rcar-du: Add support for multiple groups
  drm/rcar-du: Fix buffer pitch alignment for R8A7790 DU
  drm/rcar-du: Add support for the R8A7790 DU
  drm/rcar-du: Move output routing configuration to group
  drm/rcar-du: Remove register definitions for the second channel
  drm/rcar-du: Use dynamic number of CRTCs instead of CRTCs array size
  drm/rcar-du: Introduce CRTCs groups
  drm/rcar-du: Rename rcar_du_plane_(init|register) to rcar_du_planes_*
  drm/rcar-du: Create rcar_du_planes structure
  drm/rcar-du: Rename platform data fields to match what they describe
  drm/rcar-du: Merge LVDS and VGA encoder code
  drm/rcar-du: Split VGA encoder and connector
  drm/rcar-du: Split LVDS encoder and connector
  drm/rcar-du: Clarify comment regarding plane Y source coordinate
  drm/rcar-du: Support per-CRTC clock and IRQ
  ...
This commit is contained in:
Dave Airlie 2013-08-10 07:16:50 +10:00
commit b43bd92568
23 changed files with 1420 additions and 616 deletions

View File

@ -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).

View File

@ -1,8 +1,12 @@
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_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
rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o
obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o

View File

@ -23,30 +23,26 @@
#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)
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);
@ -54,7 +50,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);
@ -63,29 +59,48 @@ 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);
}
static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
{
int ret;
ret = clk_prepare_enable(rcrtc->clock);
if (ret < 0)
return ret;
ret = rcar_du_group_get(rcrtc->group);
if (ret < 0)
clk_disable_unprepare(rcrtc->clock);
return ret;
}
static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
{
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;
unsigned long clk;
u32 value;
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;
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)
@ -112,68 +127,25 @@ 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->crtc.dev->dev_private;
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);
}
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)
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);
/* 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)
{
struct rcar_du_device *rcdu = crtc->dev->dev_private;
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES];
unsigned int num_planes = 0;
@ -182,8 +154,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)
@ -220,8 +192,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
@ -231,21 +203,19 @@ 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);
if (rcdu->used_crtcs) {
__rcar_du_start_stop(rcdu, false);
__rcar_du_start_stop(rcdu, true);
}
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)
{
struct drm_crtc *crtc = &rcrtc->crtc;
struct rcar_du_device *rcdu = crtc->dev->dev_private;
unsigned int i;
if (rcrtc->started)
@ -260,16 +230,16 @@ 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(&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;
@ -283,7 +253,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;
}
@ -291,42 +261,37 @@ 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;
}
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 +305,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 +331,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.
@ -390,8 +353,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;
@ -423,10 +386,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 +477,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)
@ -549,16 +530,41 @@ 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)
{
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];
struct drm_crtc *crtc = &rcrtc->crtc;
unsigned int irqflags;
char clk_name[5];
char *name;
int irq;
int ret;
rcrtc->mmio_offset = index ? DISP2_REG_OFFSET : 0;
/* 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->group = rgrp;
rcrtc->mmio_offset = mmio_offsets[index];
rcrtc->index = index;
rcrtc->dpms = DRM_MODE_DPMS_OFF;
rcrtc->plane = &rcdu->planes.planes[index];
rcrtc->plane = &rgrp->planes.planes[index % 2];
rcrtc->plane->crtc = crtc;
@ -568,6 +574,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 +608,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);
}
}

View File

@ -15,16 +15,18 @@
#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>
struct rcar_du_device;
struct rcar_du_group;
struct rcar_du_plane;
struct rcar_du_crtc {
struct drm_crtc crtc;
struct clk *clock;
unsigned int mmio_offset;
unsigned int index;
bool started;
@ -33,18 +35,21 @@ 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);
#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_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);
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__ */

View File

@ -21,6 +21,7 @@
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include "rcar_du_crtc.h"
@ -28,75 +29,22 @@
#include "rcar_du_kms.h"
#include "rcar_du_regs.h"
/* -----------------------------------------------------------------------------
* Core device operations
*/
/*
* 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.
*
* 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)
{
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);
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
*
* 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);
}
/* -----------------------------------------------------------------------------
* DRM operations
*/
static int rcar_du_unload(struct drm_device *dev)
{
struct rcar_du_device *rcdu = dev->dev_private;
if (rcdu->fbdev)
drm_fbdev_cma_fini(rcdu->fbdev);
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;
@ -107,7 +55,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;
@ -124,35 +71,15 @@ 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;
/* I/O resources and clocks */
/* I/O resources */
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");
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;
}
rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(rcdu->mmio))
return PTR_ERR(rcdu->mmio);
/* DRM/KMS objects */
ret = rcar_du_modeset_init(rcdu);
@ -161,18 +88,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);
@ -188,20 +111,15 @@ 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);
}
static irqreturn_t rcar_du_irq(int irq, void *arg)
static void rcar_du_lastclose(struct drm_device *dev)
{
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;
drm_fbdev_cma_restore_mode(rcdu->fbdev);
}
static int rcar_du_enable_vblank(struct drm_device *dev, int crtc)
@ -236,12 +154,11 @@ 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,
.lastclose = rcar_du_lastclose,
.get_vblank_counter = drm_vblank_count,
.enable_vblank = rcar_du_enable_vblank,
.disable_vblank = rcar_du_disable_vblank,
@ -313,6 +230,57 @@ static int rcar_du_remove(struct platform_device *pdev)
return 0;
}
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,
},
},
.num_lvds = 0,
};
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,
},
},
.num_lvds = 2,
};
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 },
{ }
};
MODULE_DEVICE_TABLE(platform, rcar_du_id_table);
static struct platform_driver rcar_du_platform_driver = {
.probe = rcar_du_probe,
.remove = rcar_du_remove,
@ -321,6 +289,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);

View File

@ -15,43 +15,74 @@
#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"
#include "rcar_du_plane.h"
#include "rcar_du_group.h"
struct clk;
struct device;
struct drm_device;
struct drm_fbdev_cma;
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 */
#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_*)
* @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 {
struct device *dev;
const struct rcar_du_platform_data *pdata;
const struct rcar_du_device_info *info;
void __iomem *mmio;
struct clk *clock;
unsigned int use_count;
struct drm_device *ddev;
struct drm_fbdev_cma *fbdev;
struct rcar_du_crtc crtcs[2];
unsigned int used_crtcs;
struct rcar_du_crtc crtcs[3];
unsigned int num_crtcs;
struct {
struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES];
unsigned int free;
struct mutex lock;
struct rcar_du_group groups[2];
struct drm_property *alpha;
struct drm_property *colorkey;
struct drm_property *zpos;
} planes;
unsigned int dpad0_source;
struct rcar_du_lvdsenc *lvds[2];
};
int rcar_du_get(struct rcar_du_device *rcdu);
void rcar_du_put(struct rcar_du_device *rcdu);
static inline bool rcar_du_has(struct rcar_du_device *rcdu,
unsigned int feature)
{
return rcdu->info->features & feature;
}
static inline u32 rcar_du_read(struct rcar_du_device *rcdu, u32 reg)
{

View File

@ -0,0 +1,202 @@
/*
* 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 <linux/export.h>
#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_lvdsenc.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)
{
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;
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);
/* 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,
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,
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);
if (renc == NULL)
return -ENOMEM;
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;
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,
encoder_type);
if (ret < 0)
return ret;
drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
switch (encoder_type) {
case DRM_MODE_ENCODER_LVDS:
return rcar_du_lvds_connector_init(rcdu, renc,
&data->connector.lvds.panel);
case DRM_MODE_ENCODER_DAC:
return rcar_du_vga_connector_init(rcdu, renc);
default:
return -EINVAL;
}
}

View File

@ -0,0 +1,49 @@
/*
* 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 <linux/platform_data/rcar-du.h>
#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) \
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,
enum rcar_du_output output,
const struct rcar_du_encoder_data *data);
#endif /* __RCAR_DU_ENCODER_H__ */

View File

@ -0,0 +1,187 @@
/*
* 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/clk.h>
#include <linux/io.h>
#include "rcar_du_drv.h"
#include "rcar_du_group.h"
#include "rcar_du_regs.h"
u32 rcar_du_group_read(struct rcar_du_group *rgrp, u32 reg)
{
return rcar_du_read(rgrp->dev, rgrp->mmio_offset + reg);
}
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 */
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);
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.
*/
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);
}
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);
dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK);
/* 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 & BIT(RCAR_DU_OUTPUT_DPAD1))
dorcr |= DORCR_PG2D_DS1;
else
dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2;
rcar_du_group_write(rgrp, DORCR, dorcr);
return rcar_du_set_dpad0_routing(rgrp->dev);
}

View File

@ -0,0 +1,50 @@
/*
* 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;
};
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);
void rcar_du_group_restart(struct rcar_du_group *rgrp);
int rcar_du_group_set_routing(struct rcar_du_group *rgrp);
#endif /* __RCAR_DU_GROUP_H__ */

View File

@ -19,10 +19,10 @@
#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_lvdsenc.h"
#include "rcar_du_regs.h"
#include "rcar_du_vga.h"
/* -----------------------------------------------------------------------------
* Format helpers
@ -105,35 +105,6 @@ const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc)
return NULL;
}
/* -----------------------------------------------------------------------------
* 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
*/
@ -141,11 +112,18 @@ void rcar_du_encoder_mode_commit(struct drm_encoder *encoder)
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);
@ -155,6 +133,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;
@ -165,7 +144,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) {
@ -185,81 +167,124 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
return drm_fb_cma_create(dev, file_priv, mode_cmd);
}
static void rcar_du_output_poll_changed(struct drm_device *dev)
{
struct rcar_du_device *rcdu = dev->dev_private;
drm_fbdev_cma_hotplug_event(rcdu->fbdev);
}
static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
.fb_create = rcar_du_fb_create,
.output_poll_changed = rcar_du_output_poll_changed,
};
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;
struct drm_fbdev_cma *fbdev;
unsigned int num_groups;
unsigned int i;
int ret;
drm_mode_config_init(rcdu->ddev);
drm_mode_config_init(dev);
rcdu->ddev->mode_config.min_width = 0;
rcdu->ddev->mode_config.min_height = 0;
rcdu->ddev->mode_config.max_width = 4095;
rcdu->ddev->mode_config.max_height = 2047;
rcdu->ddev->mode_config.funcs = &rcar_du_mode_config_funcs;
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
dev->mode_config.max_width = 4095;
dev->mode_config.max_height = 2047;
dev->mode_config.funcs = &rcar_du_mode_config_funcs;
ret = rcar_du_plane_init(rcdu);
if (ret < 0)
return ret;
rcdu->num_crtcs = rcdu->info->num_crtcs;
for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) {
ret = rcar_du_crtc_create(rcdu, i);
/* 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];
rgrp->dev = rcdu;
rgrp->mmio_offset = mmio_offsets[i];
rgrp->index = i;
ret = rcar_du_planes_init(rgrp);
if (ret < 0)
return ret;
}
rcdu->used_crtcs = 0;
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. */
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];
const struct rcar_du_output_routing *route =
&rcdu->info->routes[pdata->output];
if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) {
if (pdata->type == RCAR_DU_ENCODER_UNUSED)
continue;
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);
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->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;
}
ret = rcar_du_plane_register(rcdu);
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);
drm_kms_helper_poll_init(dev);
drm_helper_disable_unused_functions(rcdu->ddev);
drm_helper_disable_unused_functions(dev);
fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc,
dev->mode_config.num_connector);
if (IS_ERR(fbdev))
return PTR_ERR(fbdev);
#ifndef CONFIG_FRAMEBUFFER_CONSOLE
drm_fbdev_cma_restore_mode(fbdev);
#endif
rcdu->fbdev = fbdev;
return 0;
}

View File

@ -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,

View File

@ -1,5 +1,5 @@
/*
* rcar_du_lvds.c -- R-Car Display Unit LVDS Encoder and Connector
* rcar_du_lvdscon.c -- R-Car Display Unit LVDS Connector
*
* Copyright (C) 2013 Renesas Corporation
*
@ -16,8 +16,9 @@
#include <drm/drm_crtc_helper.h>
#include "rcar_du_drv.h"
#include "rcar_du_encoder.h"
#include "rcar_du_kms.h"
#include "rcar_du_lvds.h"
#include "rcar_du_lvdscon.h"
struct rcar_du_lvds_connector {
struct rcar_du_connector connector;
@ -28,13 +29,10 @@ struct rcar_du_lvds_connector {
#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 rcar_du_lvds_connector *lvdscon =
to_rcar_lvds_connector(connector);
struct drm_display_mode *mode;
mode = drm_mode_create(connector->dev);
@ -90,9 +88,9 @@ static const struct drm_connector_funcs connector_funcs = {
.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)
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;
@ -131,86 +129,3 @@ static int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
return 0;
}
/* -----------------------------------------------------------------------------
* Encoder
*/
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);
}

View File

@ -1,5 +1,5 @@
/*
* rcar_du_lvds.h -- R-Car Display Unit LVDS Encoder and Connector
* rcar_du_lvdscon.h -- R-Car Display Unit LVDS Connector
*
* Copyright (C) 2013 Renesas Corporation
*
@ -11,14 +11,15 @@
* (at your option) any later version.
*/
#ifndef __RCAR_DU_LVDS_H__
#define __RCAR_DU_LVDS_H__
#ifndef __RCAR_DU_LVDSCON_H__
#define __RCAR_DU_LVDSCON_H__
struct rcar_du_device;
struct rcar_du_encoder_lvds_data;
struct rcar_du_encoder;
struct rcar_du_panel_data;
int rcar_du_lvds_init(struct rcar_du_device *rcdu,
const struct rcar_du_encoder_lvds_data *data,
unsigned int output);
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_LVDS_H__ */
#endif /* __RCAR_DU_LVDSCON_H__ */

View File

@ -0,0 +1,196 @@
/*
* 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;
}

View File

@ -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__ */

View File

@ -36,90 +36,95 @@ 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;
/* 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);
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]);
}
}
@ -140,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;
@ -154,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;
@ -172,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:
@ -187,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;
}
@ -201,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;
@ -211,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;
@ -232,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)
@ -241,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)
@ -273,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;
@ -316,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);
@ -377,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;
@ -390,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;
@ -432,37 +434,39 @@ 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_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;
@ -472,11 +476,16 @@ 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_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;
@ -484,23 +493,22 @@ int rcar_du_plane_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,
(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)
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;

View File

@ -14,10 +14,13 @@
#ifndef __RCAR_DU_PLANE_H__
#define __RCAR_DU_PLANE_H__
struct drm_crtc;
struct drm_framebuffer;
struct rcar_du_device;
#include <linux/mutex.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
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
@ -30,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;
@ -54,8 +57,19 @@ struct rcar_du_plane {
unsigned int dst_y;
};
int rcar_du_plane_init(struct rcar_du_device *rcdu);
int rcar_du_plane_register(struct rcar_du_device *rcdu);
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_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);
void rcar_du_plane_compute_base(struct rcar_du_plane *plane,

View File

@ -13,14 +13,15 @@
#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
*/
#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 +36,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 +60,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 +79,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 +88,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 +111,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 +133,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 +147,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)
@ -204,6 +197,68 @@
#define DEFR6_MLOS1 (1 << 2)
#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
*/
@ -349,21 +404,34 @@
#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
*/
#define DCMR 0x0c100
#define DCMWR 0x0c104
#define DC2MWR 0x0c204
#define DCSAR 0x0c120
#define DC2SAR 0x0c220
#define DCMLR 0x0c150
#define DC2MLR 0x0c250
/* -----------------------------------------------------------------------------
* Color Palette Registers

View File

@ -1,5 +1,5 @@
/*
* rcar_du_vga.c -- R-Car Display Unit VGA DAC and Connector
* rcar_du_vgacon.c -- R-Car Display Unit VGA Connector
*
* Copyright (C) 2013 Renesas Corporation
*
@ -16,12 +16,9 @@
#include <drm/drm_crtc_helper.h>
#include "rcar_du_drv.h"
#include "rcar_du_encoder.h"
#include "rcar_du_kms.h"
#include "rcar_du_vga.h"
/* -----------------------------------------------------------------------------
* Connector
*/
#include "rcar_du_vgacon.h"
static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
{
@ -49,7 +46,7 @@ static void rcar_du_vga_connector_destroy(struct drm_connector *connector)
static enum drm_connector_status
rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
{
return connector_status_unknown;
return connector_status_connected;
}
static const struct drm_connector_funcs connector_funcs = {
@ -59,8 +56,8 @@ static const struct drm_connector_funcs connector_funcs = {
.destroy = rcar_du_vga_connector_destroy,
};
static int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
struct rcar_du_encoder *renc)
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;
@ -97,53 +94,3 @@ static int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
return 0;
}
/* -----------------------------------------------------------------------------
* Encoder
*/
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);
}

View File

@ -1,5 +1,5 @@
/*
* rcar_du_vga.h -- R-Car Display Unit VGA DAC and Connector
* rcar_du_vgacon.h -- R-Car Display Unit VGA Connector
*
* Copyright (C) 2013 Renesas Corporation
*
@ -11,14 +11,13 @@
* (at your option) any later version.
*/
#ifndef __RCAR_DU_VGA_H__
#define __RCAR_DU_VGA_H__
#ifndef __RCAR_DU_VGACON_H__
#define __RCAR_DU_VGACON_H__
struct rcar_du_device;
struct rcar_du_encoder_vga_data;
struct rcar_du_encoder;
int rcar_du_vga_init(struct rcar_du_device *rcdu,
const struct rcar_du_encoder_vga_data *data,
unsigned int output);
int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
struct rcar_du_encoder *renc);
#endif /* __RCAR_DU_VGA_H__ */
#endif /* __RCAR_DU_VGACON_H__ */

View File

@ -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__ */

View File

@ -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,
};
@ -28,22 +38,32 @@ 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 (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 encoder;
unsigned int output;
enum rcar_du_encoder_type type;
enum rcar_du_output 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 {