mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
20
drivers/gpu/drm/rcar-du/Kconfig
Normal file
20
drivers/gpu/drm/rcar-du/Kconfig
Normal file
|
@ -0,0 +1,20 @@
|
|||
config DRM_RCAR_DU
|
||||
tristate "DRM Support for R-Car Display Unit"
|
||||
depends on DRM && ARM
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select DRM_GEM_CMA_HELPER
|
||||
select DRM_KMS_FB_HELPER
|
||||
select VIDEOMODE_HELPERS
|
||||
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
|
||||
depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST
|
||||
help
|
||||
Enable support the R-Car Display Unit embedded LVDS encoders
|
||||
(currently only on R8A7790).
|
12
drivers/gpu/drm/rcar-du/Makefile
Normal file
12
drivers/gpu/drm/rcar-du/Makefile
Normal file
|
@ -0,0 +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_lvdscon.o \
|
||||
rcar_du_plane.o \
|
||||
rcar_du_vgacon.o
|
||||
|
||||
rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o
|
||||
|
||||
obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
|
610
drivers/gpu/drm/rcar-du/rcar_du_crtc.c
Normal file
610
drivers/gpu/drm/rcar-du/rcar_du_crtc.c
Normal file
|
@ -0,0 +1,610 @@
|
|||
/*
|
||||
* rcar_du_crtc.c -- R-Car Display Unit CRTCs
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 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/mutex.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.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"
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_kms.h"
|
||||
#include "rcar_du_plane.h"
|
||||
#include "rcar_du_regs.h"
|
||||
|
||||
static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
|
||||
{
|
||||
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->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->group->dev;
|
||||
|
||||
rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
|
||||
rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr);
|
||||
}
|
||||
|
||||
static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg,
|
||||
u32 clr, u32 set)
|
||||
{
|
||||
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)
|
||||
{
|
||||
const struct drm_display_mode *mode = &rcrtc->crtc.mode;
|
||||
unsigned long clk;
|
||||
u32 value;
|
||||
u32 div;
|
||||
|
||||
/* Dot clock */
|
||||
clk = clk_get_rate(rcrtc->clock);
|
||||
div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000);
|
||||
div = clamp(div, 1U, 64U) - 1;
|
||||
|
||||
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)
|
||||
| ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
|
||||
| DSMR_DIPM_DE;
|
||||
rcar_du_crtc_write(rcrtc, DSMR, value);
|
||||
|
||||
/* Display timings */
|
||||
rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19);
|
||||
rcar_du_crtc_write(rcrtc, HDER, mode->htotal - mode->hsync_start +
|
||||
mode->hdisplay - 19);
|
||||
rcar_du_crtc_write(rcrtc, HSWR, mode->hsync_end -
|
||||
mode->hsync_start - 1);
|
||||
rcar_du_crtc_write(rcrtc, HCR, mode->htotal - 1);
|
||||
|
||||
rcar_du_crtc_write(rcrtc, VDSR, mode->vtotal - mode->vsync_end - 2);
|
||||
rcar_du_crtc_write(rcrtc, VDER, mode->vtotal - mode->vsync_end +
|
||||
mode->vdisplay - 2);
|
||||
rcar_du_crtc_write(rcrtc, VSPR, mode->vtotal - mode->vsync_end +
|
||||
mode->vsync_start - 1);
|
||||
rcar_du_crtc_write(rcrtc, VCR, mode->vtotal - 1);
|
||||
|
||||
rcar_du_crtc_write(rcrtc, DESR, mode->htotal - mode->hsync_start);
|
||||
rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay);
|
||||
}
|
||||
|
||||
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 |= 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_crtc *rcrtc = to_rcar_crtc(crtc);
|
||||
struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES];
|
||||
unsigned int num_planes = 0;
|
||||
unsigned int prio = 0;
|
||||
unsigned int i;
|
||||
u32 dptsr = 0;
|
||||
u32 dspr = 0;
|
||||
|
||||
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)
|
||||
continue;
|
||||
|
||||
/* Insert the plane in the sorted planes array. */
|
||||
for (j = num_planes++; j > 0; --j) {
|
||||
if (planes[j-1]->zpos <= plane->zpos)
|
||||
break;
|
||||
planes[j] = planes[j-1];
|
||||
}
|
||||
|
||||
planes[j] = plane;
|
||||
prio += plane->format->planes * 4;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_planes; ++i) {
|
||||
struct rcar_du_plane *plane = planes[i];
|
||||
unsigned int index = plane->hwindex;
|
||||
|
||||
prio -= 4;
|
||||
dspr |= (index + 1) << prio;
|
||||
dptsr |= DPTSR_PnDK(index) | DPTSR_PnTS(index);
|
||||
|
||||
if (plane->format->planes == 2) {
|
||||
index = (index + 1) % 8;
|
||||
|
||||
prio -= 4;
|
||||
dspr |= (index + 1) << prio;
|
||||
dptsr |= DPTSR_PnDK(index) | DPTSR_PnTS(index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Select display timing and dot clock generator 2 for planes associated
|
||||
* with superposition controller 2.
|
||||
*/
|
||||
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
|
||||
* for the flicker. One way to mitigate the issue would be to
|
||||
* pre-associate planes with CRTCs (either with a fixed 4/4
|
||||
* split, or through a module parameter). Flicker would then
|
||||
* occur only if we need to break the pre-association.
|
||||
*/
|
||||
if (value != dptsr) {
|
||||
rcar_du_group_write(rcrtc->group, DPTSR, dptsr);
|
||||
if (rcrtc->group->used_crtcs)
|
||||
rcar_du_group_restart(rcrtc->group);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned int i;
|
||||
|
||||
if (rcrtc->started)
|
||||
return;
|
||||
|
||||
if (WARN_ON(rcrtc->plane->format == NULL))
|
||||
return;
|
||||
|
||||
/* Set display off and background to black */
|
||||
rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0));
|
||||
rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0));
|
||||
|
||||
/* Configure display timings and output routing */
|
||||
rcar_du_crtc_set_display_timing(rcrtc);
|
||||
rcar_du_group_set_routing(rcrtc->group);
|
||||
|
||||
mutex_lock(&rcrtc->group->planes.lock);
|
||||
rcrtc->plane->enabled = true;
|
||||
rcar_du_crtc_update_planes(crtc);
|
||||
mutex_unlock(&rcrtc->group->planes.lock);
|
||||
|
||||
/* Setup planes. */
|
||||
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;
|
||||
|
||||
rcar_du_plane_setup(plane);
|
||||
}
|
||||
|
||||
/* Select master sync mode. This enables display operation in master
|
||||
* sync mode (with the HSYNC and VSYNC signals configured as outputs and
|
||||
* actively driven).
|
||||
*/
|
||||
rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_MASTER);
|
||||
|
||||
rcar_du_group_start_stop(rcrtc->group, true);
|
||||
|
||||
rcrtc->started = true;
|
||||
}
|
||||
|
||||
static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
|
||||
{
|
||||
struct drm_crtc *crtc = &rcrtc->crtc;
|
||||
|
||||
if (!rcrtc->started)
|
||||
return;
|
||||
|
||||
mutex_lock(&rcrtc->group->planes.lock);
|
||||
rcrtc->plane->enabled = false;
|
||||
rcar_du_crtc_update_planes(crtc);
|
||||
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_group_start_stop(rcrtc->group, false);
|
||||
|
||||
rcrtc->started = false;
|
||||
}
|
||||
|
||||
void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc)
|
||||
{
|
||||
rcar_du_crtc_stop(rcrtc);
|
||||
rcar_du_crtc_put(rcrtc);
|
||||
}
|
||||
|
||||
void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc)
|
||||
{
|
||||
if (rcrtc->dpms != DRM_MODE_DPMS_ON)
|
||||
return;
|
||||
|
||||
rcar_du_crtc_get(rcrtc);
|
||||
rcar_du_crtc_start(rcrtc);
|
||||
}
|
||||
|
||||
static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc)
|
||||
{
|
||||
struct drm_crtc *crtc = &rcrtc->crtc;
|
||||
|
||||
rcar_du_plane_compute_base(rcrtc->plane, crtc->primary->fb);
|
||||
rcar_du_plane_update_base(rcrtc->plane);
|
||||
}
|
||||
|
||||
static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode)
|
||||
{
|
||||
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
|
||||
|
||||
if (rcrtc->dpms == mode)
|
||||
return;
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON) {
|
||||
rcar_du_crtc_get(rcrtc);
|
||||
rcar_du_crtc_start(rcrtc);
|
||||
} else {
|
||||
rcar_du_crtc_stop(rcrtc);
|
||||
rcar_du_crtc_put(rcrtc);
|
||||
}
|
||||
|
||||
rcrtc->dpms = mode;
|
||||
}
|
||||
|
||||
static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
/* TODO Fixup modes */
|
||||
return true;
|
||||
}
|
||||
|
||||
static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc)
|
||||
{
|
||||
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
|
||||
|
||||
/* We need to access the hardware during mode set, acquire a reference
|
||||
* to the CRTC.
|
||||
*/
|
||||
rcar_du_crtc_get(rcrtc);
|
||||
|
||||
/* Stop the CRTC and release the plane. Force the DPMS mode to off as a
|
||||
* result.
|
||||
*/
|
||||
rcar_du_crtc_stop(rcrtc);
|
||||
rcar_du_plane_release(rcrtc->plane);
|
||||
|
||||
rcrtc->dpms = DRM_MODE_DPMS_OFF;
|
||||
}
|
||||
|
||||
static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode,
|
||||
int x, int y,
|
||||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
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;
|
||||
|
||||
format = rcar_du_format_info(crtc->primary->fb->pixel_format);
|
||||
if (format == NULL) {
|
||||
dev_dbg(rcdu->dev, "mode_set: unsupported format %08x\n",
|
||||
crtc->primary->fb->pixel_format);
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = rcar_du_plane_reserve(rcrtc->plane, format);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
rcrtc->plane->format = format;
|
||||
|
||||
rcrtc->plane->src_x = x;
|
||||
rcrtc->plane->src_y = y;
|
||||
rcrtc->plane->width = mode->hdisplay;
|
||||
rcrtc->plane->height = mode->vdisplay;
|
||||
|
||||
rcar_du_plane_compute_base(rcrtc->plane, crtc->primary->fb);
|
||||
|
||||
rcrtc->outputs = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
/* There's no rollback/abort operation to clean up in case of error. We
|
||||
* thus need to release the reference to the CRTC acquired in prepare()
|
||||
* here.
|
||||
*/
|
||||
rcar_du_crtc_put(rcrtc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc)
|
||||
{
|
||||
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
|
||||
|
||||
/* We're done, restart the CRTC and set the DPMS mode to on. The
|
||||
* reference to the DU acquired at prepare() time will thus be released
|
||||
* by the DPMS handler (possibly called by the disable() handler).
|
||||
*/
|
||||
rcar_du_crtc_start(rcrtc);
|
||||
rcrtc->dpms = DRM_MODE_DPMS_ON;
|
||||
}
|
||||
|
||||
static int rcar_du_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
||||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
|
||||
|
||||
rcrtc->plane->src_x = x;
|
||||
rcrtc->plane->src_y = y;
|
||||
|
||||
rcar_du_crtc_update_base(rcrtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_du_crtc_disable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
|
||||
|
||||
rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
|
||||
rcar_du_plane_release(rcrtc->plane);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
|
||||
.dpms = rcar_du_crtc_dpms,
|
||||
.mode_fixup = rcar_du_crtc_mode_fixup,
|
||||
.prepare = rcar_du_crtc_mode_prepare,
|
||||
.commit = rcar_du_crtc_mode_commit,
|
||||
.mode_set = rcar_du_crtc_mode_set,
|
||||
.mode_set_base = rcar_du_crtc_mode_set_base,
|
||||
.disable = rcar_du_crtc_disable,
|
||||
};
|
||||
|
||||
void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct drm_device *dev = rcrtc->crtc.dev;
|
||||
unsigned long flags;
|
||||
|
||||
/* Destroy the pending vertical blanking event associated with the
|
||||
* pending page flip, if any, and disable vertical blanking interrupts.
|
||||
*/
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
event = rcrtc->event;
|
||||
if (event && event->base.file_priv == file) {
|
||||
rcrtc->event = NULL;
|
||||
event->base.destroy(&event->base);
|
||||
drm_vblank_put(dev, rcrtc->index);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
|
||||
{
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct drm_device *dev = rcrtc->crtc.dev;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
event = rcrtc->event;
|
||||
rcrtc->event = NULL;
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
if (event == NULL)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
drm_send_vblank_event(dev, rcrtc->index, event);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
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,
|
||||
uint32_t page_flip_flags)
|
||||
{
|
||||
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
|
||||
struct drm_device *dev = rcrtc->crtc.dev;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
if (rcrtc->event != NULL) {
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
crtc->primary->fb = fb;
|
||||
rcar_du_crtc_update_base(rcrtc);
|
||||
|
||||
if (event) {
|
||||
event->pipe = rcrtc->index;
|
||||
drm_vblank_get(dev, rcrtc->index);
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
rcrtc->event = event;
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs crtc_funcs = {
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.set_config = drm_crtc_helper_set_config,
|
||||
.page_flip = rcar_du_crtc_page_flip,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
/* 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 = &rgrp->planes.planes[index % 2];
|
||||
|
||||
rcrtc->plane->crtc = crtc;
|
||||
|
||||
ret = drm_crtc_init(rcdu->ddev, crtc, &crtc_funcs);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
rcar_du_crtc_write(rcrtc, DSRCR, DSRCR_VBCL);
|
||||
rcar_du_crtc_set(rcrtc, DIER, DIER_VBE);
|
||||
} else {
|
||||
rcar_du_crtc_clr(rcrtc, DIER, DIER_VBE);
|
||||
}
|
||||
}
|
55
drivers/gpu/drm/rcar-du/rcar_du_crtc.h
Normal file
55
drivers/gpu/drm/rcar-du/rcar_du_crtc.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* rcar_du_crtc.h -- R-Car Display Unit CRTCs
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __RCAR_DU_CRTC_H__
|
||||
#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_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;
|
||||
|
||||
struct drm_pending_vblank_event *event;
|
||||
unsigned int outputs;
|
||||
int dpms;
|
||||
|
||||
struct rcar_du_group *group;
|
||||
struct rcar_du_plane *plane;
|
||||
};
|
||||
|
||||
#define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc)
|
||||
|
||||
int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index);
|
||||
void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable);
|
||||
void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
|
||||
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,
|
||||
enum rcar_du_output output);
|
||||
void rcar_du_crtc_update_planes(struct drm_crtc *crtc);
|
||||
|
||||
#endif /* __RCAR_DU_CRTC_H__ */
|
345
drivers/gpu/drm/rcar-du/rcar_du_drv.c
Normal file
345
drivers/gpu/drm/rcar-du/rcar_du_drv.c
Normal file
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* rcar_du_drv.c -- R-Car Display Unit DRM driver
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 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/io.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#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"
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_kms.h"
|
||||
#include "rcar_du_regs.h"
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Device Information
|
||||
*/
|
||||
|
||||
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,
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_DPAD1] = {
|
||||
.possible_crtcs = BIT(1) | BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 1,
|
||||
},
|
||||
},
|
||||
.num_lvds = 0,
|
||||
};
|
||||
|
||||
static const struct rcar_du_device_info rcar_du_r8a7790_info = {
|
||||
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8,
|
||||
.quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES,
|
||||
.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,
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_LVDS0] = {
|
||||
.possible_crtcs = BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_LVDS,
|
||||
.port = 1,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_LVDS1] = {
|
||||
.possible_crtcs = BIT(2) | BIT(1),
|
||||
.encoder_type = DRM_MODE_ENCODER_LVDS,
|
||||
.port = 2,
|
||||
},
|
||||
},
|
||||
.num_lvds = 2,
|
||||
};
|
||||
|
||||
static const struct rcar_du_device_info rcar_du_r8a7791_info = {
|
||||
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8,
|
||||
.num_crtcs = 2,
|
||||
.routes = {
|
||||
/* R8A7791 has one RGB output, one LVDS output and one
|
||||
* (currently unsupported) TCON output.
|
||||
*/
|
||||
[RCAR_DU_OUTPUT_DPAD0] = {
|
||||
.possible_crtcs = BIT(1),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_LVDS0] = {
|
||||
.possible_crtcs = BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_LVDS,
|
||||
.port = 1,
|
||||
},
|
||||
},
|
||||
.num_lvds = 1,
|
||||
};
|
||||
|
||||
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 },
|
||||
{ "rcar-du-r8a7791", (kernel_ulong_t)&rcar_du_r8a7791_info },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(platform, rcar_du_id_table);
|
||||
|
||||
static const struct of_device_id rcar_du_of_table[] = {
|
||||
{ .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
|
||||
{ .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
|
||||
{ .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, rcar_du_of_table);
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* 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);
|
||||
|
||||
dev->irq_enabled = 0;
|
||||
dev->dev_private = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_du_load(struct drm_device *dev, unsigned long flags)
|
||||
{
|
||||
struct platform_device *pdev = dev->platformdev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct rcar_du_device *rcdu;
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
|
||||
if (pdata == NULL && np == NULL) {
|
||||
dev_err(dev->dev, "no platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
|
||||
if (rcdu == NULL) {
|
||||
dev_err(dev->dev, "failed to allocate private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rcdu->dev = &pdev->dev;
|
||||
rcdu->pdata = pdata;
|
||||
rcdu->info = np ? of_match_device(rcar_du_of_table, rcdu->dev)->data
|
||||
: (void *)platform_get_device_id(pdev)->driver_data;
|
||||
rcdu->ddev = dev;
|
||||
dev->dev_private = rcdu;
|
||||
|
||||
/* I/O resources */
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(rcdu->mmio))
|
||||
return PTR_ERR(rcdu->mmio);
|
||||
|
||||
/* DRM/KMS objects */
|
||||
ret = rcar_du_modeset_init(rcdu);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
dev->irq_enabled = 1;
|
||||
|
||||
platform_set_drvdata(pdev, rcdu);
|
||||
|
||||
done:
|
||||
if (ret)
|
||||
rcar_du_unload(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 < rcdu->num_crtcs; ++i)
|
||||
rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file);
|
||||
}
|
||||
|
||||
static void rcar_du_lastclose(struct drm_device *dev)
|
||||
{
|
||||
struct rcar_du_device *rcdu = dev->dev_private;
|
||||
|
||||
drm_fbdev_cma_restore_mode(rcdu->fbdev);
|
||||
}
|
||||
|
||||
static int rcar_du_enable_vblank(struct drm_device *dev, int crtc)
|
||||
{
|
||||
struct rcar_du_device *rcdu = dev->dev_private;
|
||||
|
||||
rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_du_disable_vblank(struct drm_device *dev, int crtc)
|
||||
{
|
||||
struct rcar_du_device *rcdu = dev->dev_private;
|
||||
|
||||
rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], false);
|
||||
}
|
||||
|
||||
static const struct file_operations rcar_du_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
#endif
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
|
||||
static struct drm_driver rcar_du_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME,
|
||||
.load = rcar_du_load,
|
||||
.unload = rcar_du_unload,
|
||||
.preclose = rcar_du_preclose,
|
||||
.lastclose = rcar_du_lastclose,
|
||||
.set_busid = drm_platform_set_busid,
|
||||
.get_vblank_counter = drm_vblank_count,
|
||||
.enable_vblank = rcar_du_enable_vblank,
|
||||
.disable_vblank = rcar_du_disable_vblank,
|
||||
.gem_free_object = drm_gem_cma_free_object,
|
||||
.gem_vm_ops = &drm_gem_cma_vm_ops,
|
||||
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_import = drm_gem_prime_import,
|
||||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
|
||||
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
|
||||
.gem_prime_vmap = drm_gem_cma_prime_vmap,
|
||||
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
|
||||
.gem_prime_mmap = drm_gem_cma_prime_mmap,
|
||||
.dumb_create = rcar_du_dumb_create,
|
||||
.dumb_map_offset = drm_gem_cma_dumb_map_offset,
|
||||
.dumb_destroy = drm_gem_dumb_destroy,
|
||||
.fops = &rcar_du_fops,
|
||||
.name = "rcar-du",
|
||||
.desc = "Renesas R-Car Display Unit",
|
||||
.date = "20130110",
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Power management
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int rcar_du_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct rcar_du_device *rcdu = dev_get_drvdata(dev);
|
||||
|
||||
drm_kms_helper_poll_disable(rcdu->ddev);
|
||||
/* TODO Suspend the CRTC */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_du_pm_resume(struct device *dev)
|
||||
{
|
||||
struct rcar_du_device *rcdu = dev_get_drvdata(dev);
|
||||
|
||||
/* TODO Resume the CRTC */
|
||||
|
||||
drm_kms_helper_poll_enable(rcdu->ddev);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops rcar_du_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume)
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Platform driver
|
||||
*/
|
||||
|
||||
static int rcar_du_probe(struct platform_device *pdev)
|
||||
{
|
||||
return drm_platform_init(&rcar_du_driver, pdev);
|
||||
}
|
||||
|
||||
static int rcar_du_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
|
||||
|
||||
drm_put_dev(rcdu->ddev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rcar_du_platform_driver = {
|
||||
.probe = rcar_du_probe,
|
||||
.remove = rcar_du_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "rcar-du",
|
||||
.pm = &rcar_du_pm_ops,
|
||||
.of_match_table = rcar_du_of_table,
|
||||
},
|
||||
.id_table = rcar_du_id_table,
|
||||
};
|
||||
|
||||
module_platform_driver(rcar_du_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
|
||||
MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
|
||||
MODULE_LICENSE("GPL");
|
109
drivers/gpu/drm/rcar-du/rcar_du_drv.h
Normal file
109
drivers/gpu/drm/rcar-du/rcar_du_drv.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* rcar_du_drv.h -- R-Car Display Unit DRM driver
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __RCAR_DU_DRV_H__
|
||||
#define __RCAR_DU_DRV_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_data/rcar-du.h>
|
||||
|
||||
#include "rcar_du_crtc.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_DEFR8 (1 << 1) /* Has DEFR8 register */
|
||||
|
||||
#define RCAR_DU_QUIRK_ALIGN_128B (1 << 0) /* Align pitches to 128 bytes */
|
||||
#define RCAR_DU_QUIRK_LVDS_LANES (1 << 1) /* LVDS lanes 1 and 3 inverted */
|
||||
|
||||
/*
|
||||
* 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
|
||||
* @port: device tree port number corresponding to this output route
|
||||
*
|
||||
* 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;
|
||||
unsigned int port;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct rcar_du_device_info - DU model-specific information
|
||||
* @features: device features (RCAR_DU_FEATURE_*)
|
||||
* @quirks: device quirks (RCAR_DU_QUIRK_*)
|
||||
* @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 quirks;
|
||||
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 drm_device *ddev;
|
||||
struct drm_fbdev_cma *fbdev;
|
||||
|
||||
struct rcar_du_crtc crtcs[3];
|
||||
unsigned int num_crtcs;
|
||||
|
||||
struct rcar_du_group groups[2];
|
||||
|
||||
unsigned int dpad0_source;
|
||||
struct rcar_du_lvdsenc *lvds[2];
|
||||
};
|
||||
|
||||
static inline bool rcar_du_has(struct rcar_du_device *rcdu,
|
||||
unsigned int feature)
|
||||
{
|
||||
return rcdu->info->features & feature;
|
||||
}
|
||||
|
||||
static inline bool rcar_du_needs(struct rcar_du_device *rcdu,
|
||||
unsigned int quirk)
|
||||
{
|
||||
return rcdu->info->quirks & quirk;
|
||||
}
|
||||
|
||||
static inline u32 rcar_du_read(struct rcar_du_device *rcdu, u32 reg)
|
||||
{
|
||||
return ioread32(rcdu->mmio + reg);
|
||||
}
|
||||
|
||||
static inline void rcar_du_write(struct rcar_du_device *rcdu, u32 reg, u32 data)
|
||||
{
|
||||
iowrite32(data, rcdu->mmio + reg);
|
||||
}
|
||||
|
||||
#endif /* __RCAR_DU_DRV_H__ */
|
205
drivers/gpu/drm/rcar-du/rcar_du_encoder.c
Normal file
205
drivers/gpu/drm/rcar-du/rcar_du_encoder.c
Normal file
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* rcar_du_encoder.c -- R-Car Display Unit Encoder
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 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 device_node *np)
|
||||
{
|
||||
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: {
|
||||
const struct rcar_du_panel_data *pdata =
|
||||
data ? &data->connector.lvds.panel : NULL;
|
||||
return rcar_du_lvds_connector_init(rcdu, renc, pdata, np);
|
||||
}
|
||||
|
||||
case DRM_MODE_ENCODER_DAC:
|
||||
return rcar_du_vga_connector_init(rcdu, renc);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
50
drivers/gpu/drm/rcar-du/rcar_du_encoder.h
Normal file
50
drivers/gpu/drm/rcar-du/rcar_du_encoder.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* rcar_du_encoder.h -- R-Car Display Unit Encoder
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 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,
|
||||
struct device_node *np);
|
||||
|
||||
#endif /* __RCAR_DU_ENCODER_H__ */
|
187
drivers/gpu/drm/rcar-du/rcar_du_group.c
Normal file
187
drivers/gpu/drm/rcar-du/rcar_du_group.c
Normal file
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* rcar_du_group.c -- R-Car Display Unit Channels Pair
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 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);
|
||||
}
|
50
drivers/gpu/drm/rcar-du/rcar_du_group.h
Normal file
50
drivers/gpu/drm/rcar-du/rcar_du_group.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* rcar_du_group.c -- R-Car Display Unit Planes and CRTCs Group
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 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__ */
|
491
drivers/gpu/drm/rcar-du/rcar_du_kms.c
Normal file
491
drivers/gpu/drm/rcar-du/rcar_du_kms.c
Normal file
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* rcar_du_kms.c -- R-Car Display Unit Mode Setting
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
#include "rcar_du_crtc.h"
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_encoder.h"
|
||||
#include "rcar_du_kms.h"
|
||||
#include "rcar_du_lvdsenc.h"
|
||||
#include "rcar_du_regs.h"
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Format helpers
|
||||
*/
|
||||
|
||||
static const struct rcar_du_format_info rcar_du_format_infos[] = {
|
||||
{
|
||||
.fourcc = DRM_FORMAT_RGB565,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
.pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
|
||||
.edf = PnDDCR4_EDF_NONE,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_ARGB1555,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
|
||||
.edf = PnDDCR4_EDF_NONE,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_XRGB1555,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
|
||||
.edf = PnDDCR4_EDF_NONE,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_XRGB8888,
|
||||
.bpp = 32,
|
||||
.planes = 1,
|
||||
.pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
|
||||
.edf = PnDDCR4_EDF_RGB888,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_ARGB8888,
|
||||
.bpp = 32,
|
||||
.planes = 1,
|
||||
.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP,
|
||||
.edf = PnDDCR4_EDF_ARGB8888,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_UYVY,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
|
||||
.edf = PnDDCR4_EDF_NONE,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_YUYV,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
|
||||
.edf = PnDDCR4_EDF_NONE,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_NV12,
|
||||
.bpp = 12,
|
||||
.planes = 2,
|
||||
.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
|
||||
.edf = PnDDCR4_EDF_NONE,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_NV21,
|
||||
.bpp = 12,
|
||||
.planes = 2,
|
||||
.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
|
||||
.edf = PnDDCR4_EDF_NONE,
|
||||
}, {
|
||||
/* In YUV 4:2:2, only NV16 is supported (NV61 isn't) */
|
||||
.fourcc = DRM_FORMAT_NV16,
|
||||
.bpp = 16,
|
||||
.planes = 2,
|
||||
.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
|
||||
.edf = PnDDCR4_EDF_NONE,
|
||||
},
|
||||
};
|
||||
|
||||
const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rcar_du_format_infos); ++i) {
|
||||
if (rcar_du_format_infos[i].fourcc == fourcc)
|
||||
return &rcar_du_format_infos[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Frame buffer
|
||||
*/
|
||||
|
||||
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 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_needs(rcdu, RCAR_DU_QUIRK_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);
|
||||
}
|
||||
|
||||
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 max_pitch;
|
||||
unsigned int align;
|
||||
unsigned int bpp;
|
||||
|
||||
format = rcar_du_format_info(mode_cmd->pixel_format);
|
||||
if (format == NULL) {
|
||||
dev_dbg(dev->dev, "unsupported pixel format %08x\n",
|
||||
mode_cmd->pixel_format);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* The pitch and alignment constraints are expressed in pixels on the
|
||||
* hardware side and in bytes in the DRM API.
|
||||
*/
|
||||
bpp = format->planes == 2 ? 1 : format->bpp / 8;
|
||||
max_pitch = 4096 * bpp;
|
||||
|
||||
if (rcar_du_needs(rcdu, RCAR_DU_QUIRK_ALIGN_128B))
|
||||
align = 128;
|
||||
else
|
||||
align = 16 * bpp;
|
||||
|
||||
if (mode_cmd->pitches[0] & (align - 1) ||
|
||||
mode_cmd->pitches[0] >= max_pitch) {
|
||||
dev_dbg(dev->dev, "invalid pitch value %u\n",
|
||||
mode_cmd->pitches[0]);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (format->planes == 2) {
|
||||
if (mode_cmd->pitches[1] != mode_cmd->pitches[0]) {
|
||||
dev_dbg(dev->dev,
|
||||
"luma and chroma pitches do not match\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
static int rcar_du_encoders_init_pdata(struct rcar_du_device *rcdu)
|
||||
{
|
||||
unsigned int num_encoders = 0;
|
||||
unsigned int i;
|
||||
int 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->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;
|
||||
}
|
||||
|
||||
ret = rcar_du_encoder_init(rcdu, pdata->type, pdata->output,
|
||||
pdata, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
num_encoders++;
|
||||
}
|
||||
|
||||
return num_encoders;
|
||||
}
|
||||
|
||||
static int rcar_du_encoders_init_dt_one(struct rcar_du_device *rcdu,
|
||||
enum rcar_du_output output,
|
||||
struct of_endpoint *ep)
|
||||
{
|
||||
static const struct {
|
||||
const char *compatible;
|
||||
enum rcar_du_encoder_type type;
|
||||
} encoders[] = {
|
||||
{ "adi,adv7123", RCAR_DU_ENCODER_VGA },
|
||||
{ "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS },
|
||||
};
|
||||
|
||||
enum rcar_du_encoder_type enc_type = RCAR_DU_ENCODER_NONE;
|
||||
struct device_node *connector = NULL;
|
||||
struct device_node *encoder = NULL;
|
||||
struct device_node *prev = NULL;
|
||||
struct device_node *entity_ep_node;
|
||||
struct device_node *entity;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Locate the connected entity and infer its type from the number of
|
||||
* endpoints.
|
||||
*/
|
||||
entity = of_graph_get_remote_port_parent(ep->local_node);
|
||||
if (!entity) {
|
||||
dev_dbg(rcdu->dev, "unconnected endpoint %s, skipping\n",
|
||||
ep->local_node->full_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
entity_ep_node = of_parse_phandle(ep->local_node, "remote-endpoint", 0);
|
||||
|
||||
while (1) {
|
||||
struct device_node *ep_node;
|
||||
|
||||
ep_node = of_graph_get_next_endpoint(entity, prev);
|
||||
of_node_put(prev);
|
||||
prev = ep_node;
|
||||
|
||||
if (!ep_node)
|
||||
break;
|
||||
|
||||
if (ep_node == entity_ep_node)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* We've found one endpoint other than the input, this must
|
||||
* be an encoder. Locate the connector.
|
||||
*/
|
||||
encoder = entity;
|
||||
connector = of_graph_get_remote_port_parent(ep_node);
|
||||
of_node_put(ep_node);
|
||||
|
||||
if (!connector) {
|
||||
dev_warn(rcdu->dev,
|
||||
"no connector for encoder %s, skipping\n",
|
||||
encoder->full_name);
|
||||
of_node_put(entity_ep_node);
|
||||
of_node_put(encoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
of_node_put(entity_ep_node);
|
||||
|
||||
if (encoder) {
|
||||
/*
|
||||
* If an encoder has been found, get its type based on its
|
||||
* compatible string.
|
||||
*/
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(encoders); ++i) {
|
||||
if (of_device_is_compatible(encoder,
|
||||
encoders[i].compatible)) {
|
||||
enc_type = encoders[i].type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(encoders)) {
|
||||
dev_warn(rcdu->dev,
|
||||
"unknown encoder type for %s, skipping\n",
|
||||
encoder->full_name);
|
||||
of_node_put(encoder);
|
||||
of_node_put(connector);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* If no encoder has been found the entity must be the
|
||||
* connector.
|
||||
*/
|
||||
connector = entity;
|
||||
}
|
||||
|
||||
ret = rcar_du_encoder_init(rcdu, enc_type, output, NULL, connector);
|
||||
of_node_put(encoder);
|
||||
of_node_put(connector);
|
||||
|
||||
return ret < 0 ? ret : 1;
|
||||
}
|
||||
|
||||
static int rcar_du_encoders_init_dt(struct rcar_du_device *rcdu)
|
||||
{
|
||||
struct device_node *np = rcdu->dev->of_node;
|
||||
struct device_node *prev = NULL;
|
||||
unsigned int num_encoders = 0;
|
||||
|
||||
/*
|
||||
* Iterate over the endpoints and create one encoder for each output
|
||||
* pipeline.
|
||||
*/
|
||||
while (1) {
|
||||
struct device_node *ep_node;
|
||||
enum rcar_du_output output;
|
||||
struct of_endpoint ep;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
ep_node = of_graph_get_next_endpoint(np, prev);
|
||||
of_node_put(prev);
|
||||
prev = ep_node;
|
||||
|
||||
if (ep_node == NULL)
|
||||
break;
|
||||
|
||||
ret = of_graph_parse_endpoint(ep_node, &ep);
|
||||
if (ret < 0) {
|
||||
of_node_put(ep_node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Find the output route corresponding to the port number. */
|
||||
for (i = 0; i < RCAR_DU_OUTPUT_MAX; ++i) {
|
||||
if (rcdu->info->routes[i].possible_crtcs &&
|
||||
rcdu->info->routes[i].port == ep.port) {
|
||||
output = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == RCAR_DU_OUTPUT_MAX) {
|
||||
dev_warn(rcdu->dev,
|
||||
"port %u references unexisting output, skipping\n",
|
||||
ep.port);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Process the output pipeline. */
|
||||
ret = rcar_du_encoders_init_dt_one(rcdu, output, &ep);
|
||||
if (ret < 0) {
|
||||
of_node_put(ep_node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
num_encoders += ret;
|
||||
}
|
||||
|
||||
return num_encoders;
|
||||
}
|
||||
|
||||
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_encoders;
|
||||
unsigned int num_groups;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
drm_mode_config_init(dev);
|
||||
|
||||
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;
|
||||
|
||||
rcdu->num_crtcs = rcdu->info->num_crtcs;
|
||||
|
||||
/* Initialize the groups. */
|
||||
num_groups = DIV_ROUND_UP(rcdu->num_crtcs, 2);
|
||||
|
||||
for (i = 0; i < num_groups; ++i) {
|
||||
struct rcar_du_group *rgrp = &rcdu->groups[i];
|
||||
|
||||
rgrp->dev = rcdu;
|
||||
rgrp->mmio_offset = mmio_offsets[i];
|
||||
rgrp->index = i;
|
||||
|
||||
ret = rcar_du_planes_init(rgrp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
if (rcdu->pdata)
|
||||
ret = rcar_du_encoders_init_pdata(rcdu);
|
||||
else
|
||||
ret = rcar_du_encoders_init_dt(rcdu);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
num_encoders = ret;
|
||||
|
||||
/* 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 = route->possible_crtcs;
|
||||
encoder->possible_clones = (1 << num_encoders) - 1;
|
||||
}
|
||||
|
||||
/* 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(dev);
|
||||
|
||||
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;
|
||||
}
|
39
drivers/gpu/drm/rcar-du/rcar_du_kms.h
Normal file
39
drivers/gpu/drm/rcar-du/rcar_du_kms.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* rcar_du_kms.h -- R-Car Display Unit Mode Setting
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __RCAR_DU_KMS_H__
|
||||
#define __RCAR_DU_KMS_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct drm_file;
|
||||
struct drm_device;
|
||||
struct drm_mode_create_dumb;
|
||||
struct rcar_du_device;
|
||||
|
||||
struct rcar_du_format_info {
|
||||
u32 fourcc;
|
||||
unsigned int bpp;
|
||||
unsigned int planes;
|
||||
unsigned int pnmr;
|
||||
unsigned int edf;
|
||||
};
|
||||
|
||||
const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc);
|
||||
|
||||
int rcar_du_modeset_init(struct rcar_du_device *rcdu);
|
||||
|
||||
int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args);
|
||||
|
||||
#endif /* __RCAR_DU_KMS_H__ */
|
133
drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
Normal file
133
drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* rcar_du_lvdscon.c -- R-Car Display Unit LVDS Connector
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#include <video/display_timing.h>
|
||||
#include <video/of_display_timing.h>
|
||||
#include <video/videomode.h>
|
||||
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_encoder.h"
|
||||
#include "rcar_du_kms.h"
|
||||
#include "rcar_du_lvdscon.h"
|
||||
|
||||
struct rcar_du_lvds_connector {
|
||||
struct rcar_du_connector connector;
|
||||
|
||||
struct rcar_du_panel_data panel;
|
||||
};
|
||||
|
||||
#define to_rcar_lvds_connector(c) \
|
||||
container_of(c, struct rcar_du_lvds_connector, connector.connector)
|
||||
|
||||
static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct rcar_du_lvds_connector *lvdscon =
|
||||
to_rcar_lvds_connector(connector);
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_create(connector->dev);
|
||||
if (mode == NULL)
|
||||
return 0;
|
||||
|
||||
mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
|
||||
|
||||
drm_display_mode_from_videomode(&lvdscon->panel.mode, mode);
|
||||
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs connector_helper_funcs = {
|
||||
.get_modes = rcar_du_lvds_connector_get_modes,
|
||||
.best_encoder = rcar_du_connector_best_encoder,
|
||||
};
|
||||
|
||||
static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.detect = rcar_du_lvds_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = rcar_du_lvds_connector_destroy,
|
||||
};
|
||||
|
||||
int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc,
|
||||
const struct rcar_du_panel_data *panel,
|
||||
/* TODO const */ struct device_node *np)
|
||||
{
|
||||
struct rcar_du_lvds_connector *lvdscon;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
|
||||
lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL);
|
||||
if (lvdscon == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (panel) {
|
||||
lvdscon->panel = *panel;
|
||||
} else {
|
||||
struct display_timing timing;
|
||||
|
||||
ret = of_get_display_timing(np, "panel-timing", &timing);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
videomode_from_timing(&timing, &lvdscon->panel.mode);
|
||||
|
||||
of_property_read_u32(np, "width-mm", &lvdscon->panel.width_mm);
|
||||
of_property_read_u32(np, "height-mm", &lvdscon->panel.height_mm);
|
||||
}
|
||||
|
||||
connector = &lvdscon->connector.connector;
|
||||
connector->display_info.width_mm = lvdscon->panel.width_mm;
|
||||
connector->display_info.height_mm = lvdscon->panel.height_mm;
|
||||
|
||||
ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drm_connector_helper_add(connector, &connector_helper_funcs);
|
||||
ret = drm_connector_register(connector);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
|
||||
drm_object_property_set_value(&connector->base,
|
||||
rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
|
||||
|
||||
ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
connector->encoder = &renc->encoder;
|
||||
lvdscon->connector.encoder = renc;
|
||||
|
||||
return 0;
|
||||
}
|
26
drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
Normal file
26
drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* rcar_du_lvdscon.h -- R-Car Display Unit LVDS Connector
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __RCAR_DU_LVDSCON_H__
|
||||
#define __RCAR_DU_LVDSCON_H__
|
||||
|
||||
struct rcar_du_device;
|
||||
struct rcar_du_encoder;
|
||||
struct rcar_du_panel_data;
|
||||
|
||||
int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc,
|
||||
const struct rcar_du_panel_data *panel,
|
||||
struct device_node *np);
|
||||
|
||||
#endif /* __RCAR_DU_LVDSCON_H__ */
|
192
drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
Normal file
192
drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
Normal file
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* rcar_du_lvdsenc.c -- R-Car Display Unit LVDS Encoder
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 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 lvdhcr;
|
||||
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
|
||||
*/
|
||||
rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
|
||||
LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
|
||||
LVDCTRCR_CTR0SEL_HSYNC);
|
||||
|
||||
if (rcar_du_needs(lvds->dev, RCAR_DU_QUIRK_LVDS_LANES))
|
||||
lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
|
||||
| LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
|
||||
else
|
||||
lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
|
||||
| LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);
|
||||
|
||||
rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
|
||||
|
||||
/* 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);
|
||||
lvds->mmio = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(lvds->mmio))
|
||||
return PTR_ERR(lvds->mmio);
|
||||
|
||||
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;
|
||||
}
|
46
drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
Normal file
46
drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* rcar_du_lvdsenc.h -- R-Car Display Unit LVDS Encoder
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 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__ */
|
516
drivers/gpu/drm/rcar-du/rcar_du_plane.c
Normal file
516
drivers/gpu/drm/rcar-du/rcar_du_plane.c
Normal file
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
* rcar_du_plane.c -- R-Car Display Unit Planes
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_kms.h"
|
||||
#include "rcar_du_plane.h"
|
||||
#include "rcar_du_regs.h"
|
||||
|
||||
#define RCAR_DU_COLORKEY_NONE (0 << 24)
|
||||
#define RCAR_DU_COLORKEY_SOURCE (1 << 24)
|
||||
#define RCAR_DU_COLORKEY_MASK (1 << 24)
|
||||
|
||||
struct rcar_du_kms_plane {
|
||||
struct drm_plane plane;
|
||||
struct rcar_du_plane *hwplane;
|
||||
};
|
||||
|
||||
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_group *rgrp,
|
||||
unsigned int index, u32 reg)
|
||||
{
|
||||
return rcar_du_read(rgrp->dev,
|
||||
rgrp->mmio_offset + index * PLANE_OFF + reg);
|
||||
}
|
||||
|
||||
static void rcar_du_plane_write(struct rcar_du_group *rgrp,
|
||||
unsigned int index, u32 reg, u32 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_group *rgrp = plane->group;
|
||||
unsigned int i;
|
||||
int ret = -EBUSY;
|
||||
|
||||
mutex_lock(&rgrp->planes.lock);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rgrp->planes.planes); ++i) {
|
||||
if (!(rgrp->planes.free & (1 << i)))
|
||||
continue;
|
||||
|
||||
if (format->planes == 1 ||
|
||||
rgrp->planes.free & (1 << ((i + 1) % 8)))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(rgrp->planes.planes))
|
||||
goto done;
|
||||
|
||||
rgrp->planes.free &= ~(1 << i);
|
||||
if (format->planes == 2)
|
||||
rgrp->planes.free &= ~(1 << ((i + 1) % 8));
|
||||
|
||||
plane->hwindex = i;
|
||||
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
mutex_unlock(&rgrp->planes.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rcar_du_plane_release(struct rcar_du_plane *plane)
|
||||
{
|
||||
struct rcar_du_group *rgrp = plane->group;
|
||||
|
||||
if (plane->hwindex == -1)
|
||||
return;
|
||||
|
||||
mutex_lock(&rgrp->planes.lock);
|
||||
rgrp->planes.free |= 1 << plane->hwindex;
|
||||
if (plane->format->planes == 2)
|
||||
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_group *rgrp = plane->group;
|
||||
unsigned int index = plane->hwindex;
|
||||
u32 mwr;
|
||||
|
||||
/* Memory pitch (expressed in pixels) */
|
||||
if (plane->format->planes == 2)
|
||||
mwr = plane->pitch;
|
||||
else
|
||||
mwr = plane->pitch * 8 / plane->format->bpp;
|
||||
|
||||
rcar_du_plane_write(rgrp, index, PnMWR, mwr);
|
||||
|
||||
/* 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(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(rgrp, index, PnDSA0R, plane->dma[0]);
|
||||
|
||||
if (plane->format->planes == 2) {
|
||||
index = (index + 1) % 8;
|
||||
|
||||
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(rgrp, index, PnDSA0R, plane->dma[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
|
||||
struct drm_framebuffer *fb)
|
||||
{
|
||||
struct drm_gem_cma_object *gem;
|
||||
|
||||
plane->pitch = fb->pitches[0];
|
||||
|
||||
gem = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
plane->dma[0] = gem->paddr + fb->offsets[0];
|
||||
|
||||
if (plane->format->planes == 2) {
|
||||
gem = drm_fb_cma_get_gem_obj(fb, 1);
|
||||
plane->dma[1] = gem->paddr + fb->offsets[1];
|
||||
}
|
||||
}
|
||||
|
||||
static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
|
||||
unsigned int index)
|
||||
{
|
||||
struct rcar_du_group *rgrp = plane->group;
|
||||
u32 colorkey;
|
||||
u32 pnmr;
|
||||
|
||||
/* The PnALPHAR register controls alpha-blending in 16bpp formats
|
||||
* (ARGB1555 and XRGB1555).
|
||||
*
|
||||
* For ARGB, set the alpha value to 0, and enable alpha-blending when
|
||||
* the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
|
||||
*
|
||||
* For XRGB, set the alpha value to the plane-wide alpha value and
|
||||
* enable alpha-blending regardless of the X bit value.
|
||||
*/
|
||||
if (plane->format->fourcc != DRM_FORMAT_XRGB1555)
|
||||
rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
|
||||
else
|
||||
rcar_du_plane_write(rgrp, index, PnALPHAR,
|
||||
PnALPHAR_ABIT_X | plane->alpha);
|
||||
|
||||
pnmr = PnMR_BM_MD | plane->format->pnmr;
|
||||
|
||||
/* Disable color keying when requested. YUV formats have the
|
||||
* PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
|
||||
* automatically.
|
||||
*/
|
||||
if ((plane->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
|
||||
pnmr |= PnMR_SPIM_TP_OFF;
|
||||
|
||||
/* For packed YUV formats we need to select the U/V order. */
|
||||
if (plane->format->fourcc == DRM_FORMAT_YUYV)
|
||||
pnmr |= PnMR_YCDF_YUYV;
|
||||
|
||||
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(rgrp, index, PnTC2R, colorkey);
|
||||
break;
|
||||
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
case DRM_FORMAT_XRGB1555:
|
||||
colorkey = ((plane->colorkey & 0xf80000) >> 9)
|
||||
| ((plane->colorkey & 0x00f800) >> 6)
|
||||
| ((plane->colorkey & 0x0000f8) >> 3);
|
||||
rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
|
||||
break;
|
||||
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
rcar_du_plane_write(rgrp, index, PnTC3R,
|
||||
PnTC3R_CODE | (plane->colorkey & 0xffffff));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
|
||||
unsigned int index)
|
||||
{
|
||||
struct rcar_du_group *rgrp = plane->group;
|
||||
u32 ddcr2 = PnDDCR2_CODE;
|
||||
u32 ddcr4;
|
||||
|
||||
/* Data format
|
||||
*
|
||||
* The data format is selected by the DDDF field in PnMR and the EDF
|
||||
* field in DDCR4.
|
||||
*/
|
||||
ddcr4 = rcar_du_plane_read(rgrp, index, PnDDCR4);
|
||||
ddcr4 &= ~PnDDCR4_EDF_MASK;
|
||||
ddcr4 |= plane->format->edf | PnDDCR4_CODE;
|
||||
|
||||
rcar_du_plane_setup_mode(plane, index);
|
||||
|
||||
if (plane->format->planes == 2) {
|
||||
if (plane->hwindex != index) {
|
||||
if (plane->format->fourcc == DRM_FORMAT_NV12 ||
|
||||
plane->format->fourcc == DRM_FORMAT_NV21)
|
||||
ddcr2 |= PnDDCR2_Y420;
|
||||
|
||||
if (plane->format->fourcc == DRM_FORMAT_NV21)
|
||||
ddcr2 |= PnDDCR2_NV21;
|
||||
|
||||
ddcr2 |= PnDDCR2_DIVU;
|
||||
} else {
|
||||
ddcr2 |= PnDDCR2_DIVY;
|
||||
}
|
||||
}
|
||||
|
||||
rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2);
|
||||
rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
|
||||
|
||||
/* Destination position and size */
|
||||
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(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)
|
||||
{
|
||||
__rcar_du_plane_setup(plane, plane->hwindex);
|
||||
if (plane->format->planes == 2)
|
||||
__rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8);
|
||||
|
||||
rcar_du_plane_update_base(plane);
|
||||
}
|
||||
|
||||
static int
|
||||
rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
|
||||
unsigned int crtc_w, unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h)
|
||||
{
|
||||
struct rcar_du_plane *rplane = to_rcar_plane(plane);
|
||||
struct rcar_du_device *rcdu = rplane->group->dev;
|
||||
const struct rcar_du_format_info *format;
|
||||
unsigned int nplanes;
|
||||
int ret;
|
||||
|
||||
format = rcar_du_format_info(fb->pixel_format);
|
||||
if (format == NULL) {
|
||||
dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
|
||||
fb->pixel_format);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) {
|
||||
dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nplanes = rplane->format ? rplane->format->planes : 0;
|
||||
|
||||
/* Reallocate hardware planes if the number of required planes has
|
||||
* changed.
|
||||
*/
|
||||
if (format->planes != nplanes) {
|
||||
rcar_du_plane_release(rplane);
|
||||
ret = rcar_du_plane_reserve(rplane, format);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
rplane->crtc = crtc;
|
||||
rplane->format = format;
|
||||
|
||||
rplane->src_x = src_x >> 16;
|
||||
rplane->src_y = src_y >> 16;
|
||||
rplane->dst_x = crtc_x;
|
||||
rplane->dst_y = crtc_y;
|
||||
rplane->width = crtc_w;
|
||||
rplane->height = crtc_h;
|
||||
|
||||
rcar_du_plane_compute_base(rplane, fb);
|
||||
rcar_du_plane_setup(rplane);
|
||||
|
||||
mutex_lock(&rplane->group->planes.lock);
|
||||
rplane->enabled = true;
|
||||
rcar_du_crtc_update_planes(rplane->crtc);
|
||||
mutex_unlock(&rplane->group->planes.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_du_plane_disable(struct drm_plane *plane)
|
||||
{
|
||||
struct rcar_du_plane *rplane = to_rcar_plane(plane);
|
||||
|
||||
if (!rplane->enabled)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&rplane->group->planes.lock);
|
||||
rplane->enabled = false;
|
||||
rcar_du_crtc_update_planes(rplane->crtc);
|
||||
mutex_unlock(&rplane->group->planes.lock);
|
||||
|
||||
rcar_du_plane_release(rplane);
|
||||
|
||||
rplane->crtc = NULL;
|
||||
rplane->format = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Both the .set_property and the .update_plane operations are called with the
|
||||
* mode_config lock held. There is this no need to explicitly protect access to
|
||||
* the alpha and colorkey fields and the mode register.
|
||||
*/
|
||||
static void rcar_du_plane_set_alpha(struct rcar_du_plane *plane, u32 alpha)
|
||||
{
|
||||
if (plane->alpha == alpha)
|
||||
return;
|
||||
|
||||
plane->alpha = alpha;
|
||||
if (!plane->enabled || plane->format->fourcc != DRM_FORMAT_XRGB1555)
|
||||
return;
|
||||
|
||||
rcar_du_plane_setup_mode(plane, plane->hwindex);
|
||||
}
|
||||
|
||||
static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane,
|
||||
u32 colorkey)
|
||||
{
|
||||
if (plane->colorkey == colorkey)
|
||||
return;
|
||||
|
||||
plane->colorkey = colorkey;
|
||||
if (!plane->enabled)
|
||||
return;
|
||||
|
||||
rcar_du_plane_setup_mode(plane, plane->hwindex);
|
||||
}
|
||||
|
||||
static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane,
|
||||
unsigned int zpos)
|
||||
{
|
||||
mutex_lock(&plane->group->planes.lock);
|
||||
if (plane->zpos == zpos)
|
||||
goto done;
|
||||
|
||||
plane->zpos = zpos;
|
||||
if (!plane->enabled)
|
||||
goto done;
|
||||
|
||||
rcar_du_crtc_update_planes(plane->crtc);
|
||||
|
||||
done:
|
||||
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_plane *rplane = to_rcar_plane(plane);
|
||||
struct rcar_du_group *rgrp = rplane->group;
|
||||
|
||||
if (property == rgrp->planes.alpha)
|
||||
rcar_du_plane_set_alpha(rplane, value);
|
||||
else if (property == rgrp->planes.colorkey)
|
||||
rcar_du_plane_set_colorkey(rplane, value);
|
||||
else if (property == rgrp->planes.zpos)
|
||||
rcar_du_plane_set_zpos(rplane, value);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_plane_funcs rcar_du_plane_funcs = {
|
||||
.update_plane = rcar_du_plane_update,
|
||||
.disable_plane = rcar_du_plane_disable,
|
||||
.set_property = rcar_du_plane_set_property,
|
||||
.destroy = drm_plane_cleanup,
|
||||
};
|
||||
|
||||
static const uint32_t formats[] = {
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_ARGB1555,
|
||||
DRM_FORMAT_XRGB1555,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_UYVY,
|
||||
DRM_FORMAT_YUYV,
|
||||
DRM_FORMAT_NV12,
|
||||
DRM_FORMAT_NV21,
|
||||
DRM_FORMAT_NV16,
|
||||
};
|
||||
|
||||
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(&planes->lock);
|
||||
planes->free = 0xff;
|
||||
|
||||
planes->alpha =
|
||||
drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
|
||||
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).
|
||||
*/
|
||||
planes->colorkey =
|
||||
drm_property_create_range(rcdu->ddev, 0, "colorkey",
|
||||
0, 0x01ffffff);
|
||||
if (planes->colorkey == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
planes->zpos =
|
||||
drm_property_create_range(rcdu->ddev, 0, "zpos", 1, 7);
|
||||
if (planes->zpos == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(planes->planes); ++i) {
|
||||
struct rcar_du_plane *plane = &planes->planes[i];
|
||||
|
||||
plane->group = rgrp;
|
||||
plane->hwindex = -1;
|
||||
plane->alpha = 255;
|
||||
plane->colorkey = RCAR_DU_COLORKEY_NONE;
|
||||
plane->zpos = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
plane = devm_kzalloc(rcdu->dev, sizeof(*plane), GFP_KERNEL);
|
||||
if (plane == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
plane->hwplane = &planes->planes[i + 2];
|
||||
plane->hwplane->zpos = 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,
|
||||
planes->alpha, 255);
|
||||
drm_object_attach_property(&plane->plane.base,
|
||||
planes->colorkey,
|
||||
RCAR_DU_COLORKEY_NONE);
|
||||
drm_object_attach_property(&plane->plane.base,
|
||||
planes->zpos, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
81
drivers/gpu/drm/rcar-du/rcar_du_plane.h
Normal file
81
drivers/gpu/drm/rcar-du/rcar_du_plane.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* rcar_du_plane.h -- R-Car Display Unit Planes
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __RCAR_DU_PLANE_H__
|
||||
#define __RCAR_DU_PLANE_H__
|
||||
|
||||
#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
|
||||
* than 7 KMS planes can be available. We thus create 7 KMS planes and
|
||||
* 9 software planes (one for each KMS planes and one for each CRTC).
|
||||
*/
|
||||
|
||||
#define RCAR_DU_NUM_KMS_PLANES 7
|
||||
#define RCAR_DU_NUM_HW_PLANES 8
|
||||
#define RCAR_DU_NUM_SW_PLANES 9
|
||||
|
||||
struct rcar_du_plane {
|
||||
struct rcar_du_group *group;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
bool enabled;
|
||||
|
||||
int hwindex; /* 0-based, -1 means unused */
|
||||
unsigned int alpha;
|
||||
unsigned int colorkey;
|
||||
unsigned int zpos;
|
||||
|
||||
const struct rcar_du_format_info *format;
|
||||
|
||||
unsigned long dma[2];
|
||||
unsigned int pitch;
|
||||
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
|
||||
unsigned int src_x;
|
||||
unsigned int src_y;
|
||||
unsigned int dst_x;
|
||||
unsigned int dst_y;
|
||||
};
|
||||
|
||||
struct rcar_du_planes {
|
||||
struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES];
|
||||
unsigned int free;
|
||||
struct mutex lock;
|
||||
|
||||
struct drm_property *alpha;
|
||||
struct drm_property *colorkey;
|
||||
struct drm_property *zpos;
|
||||
};
|
||||
|
||||
int rcar_du_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,
|
||||
struct drm_framebuffer *fb);
|
||||
int rcar_du_plane_reserve(struct rcar_du_plane *plane,
|
||||
const struct rcar_du_format_info *format);
|
||||
void rcar_du_plane_release(struct rcar_du_plane *plane);
|
||||
|
||||
#endif /* __RCAR_DU_PLANE_H__ */
|
513
drivers/gpu/drm/rcar-du/rcar_du_regs.h
Normal file
513
drivers/gpu/drm/rcar-du/rcar_du_regs.h
Normal file
|
@ -0,0 +1,513 @@
|
|||
/*
|
||||
* rcar_du_regs.h -- R-Car Display Unit 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_DU_REGS_H__
|
||||
#define __RCAR_DU_REGS_H__
|
||||
|
||||
#define DU0_REG_OFFSET 0x00000
|
||||
#define DU1_REG_OFFSET 0x30000
|
||||
#define DU2_REG_OFFSET 0x40000
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Display Control Registers
|
||||
*/
|
||||
|
||||
#define DSYSR 0x00000 /* display 1 */
|
||||
#define DSYSR_ILTS (1 << 29)
|
||||
#define DSYSR_DSEC (1 << 20)
|
||||
#define DSYSR_IUPD (1 << 16)
|
||||
#define DSYSR_DRES (1 << 9)
|
||||
#define DSYSR_DEN (1 << 8)
|
||||
#define DSYSR_TVM_MASTER (0 << 6)
|
||||
#define DSYSR_TVM_SWITCH (1 << 6)
|
||||
#define DSYSR_TVM_TVSYNC (2 << 6)
|
||||
#define DSYSR_TVM_MASK (3 << 6)
|
||||
#define DSYSR_SCM_INT_NONE (0 << 4)
|
||||
#define DSYSR_SCM_INT_SYNC (2 << 4)
|
||||
#define DSYSR_SCM_INT_VIDEO (3 << 4)
|
||||
|
||||
#define DSMR 0x00004
|
||||
#define DSMR_VSPM (1 << 28)
|
||||
#define DSMR_ODPM (1 << 27)
|
||||
#define DSMR_DIPM_DISP (0 << 25)
|
||||
#define DSMR_DIPM_CSYNC (1 << 25)
|
||||
#define DSMR_DIPM_DE (3 << 25)
|
||||
#define DSMR_DIPM_MASK (3 << 25)
|
||||
#define DSMR_CSPM (1 << 24)
|
||||
#define DSMR_DIL (1 << 19)
|
||||
#define DSMR_VSL (1 << 18)
|
||||
#define DSMR_HSL (1 << 17)
|
||||
#define DSMR_DDIS (1 << 16)
|
||||
#define DSMR_CDEL (1 << 15)
|
||||
#define DSMR_CDEM_CDE (0 << 13)
|
||||
#define DSMR_CDEM_LOW (2 << 13)
|
||||
#define DSMR_CDEM_HIGH (3 << 13)
|
||||
#define DSMR_CDEM_MASK (3 << 13)
|
||||
#define DSMR_CDED (1 << 12)
|
||||
#define DSMR_ODEV (1 << 8)
|
||||
#define DSMR_CSY_VH_OR (0 << 6)
|
||||
#define DSMR_CSY_333 (2 << 6)
|
||||
#define DSMR_CSY_222 (3 << 6)
|
||||
#define DSMR_CSY_MASK (3 << 6)
|
||||
|
||||
#define DSSR 0x00008
|
||||
#define DSSR_VC1FB_DSA0 (0 << 30)
|
||||
#define DSSR_VC1FB_DSA1 (1 << 30)
|
||||
#define DSSR_VC1FB_DSA2 (2 << 30)
|
||||
#define DSSR_VC1FB_INIT (3 << 30)
|
||||
#define DSSR_VC1FB_MASK (3 << 30)
|
||||
#define DSSR_VC0FB_DSA0 (0 << 28)
|
||||
#define DSSR_VC0FB_DSA1 (1 << 28)
|
||||
#define DSSR_VC0FB_DSA2 (2 << 28)
|
||||
#define DSSR_VC0FB_INIT (3 << 28)
|
||||
#define DSSR_VC0FB_MASK (3 << 28)
|
||||
#define DSSR_DFB(n) (1 << ((n)+15))
|
||||
#define DSSR_TVR (1 << 15)
|
||||
#define DSSR_FRM (1 << 14)
|
||||
#define DSSR_VBK (1 << 11)
|
||||
#define DSSR_RINT (1 << 9)
|
||||
#define DSSR_HBK (1 << 8)
|
||||
#define DSSR_ADC(n) (1 << ((n)-1))
|
||||
|
||||
#define DSRCR 0x0000c
|
||||
#define DSRCR_TVCL (1 << 15)
|
||||
#define DSRCR_FRCL (1 << 14)
|
||||
#define DSRCR_VBCL (1 << 11)
|
||||
#define DSRCR_RICL (1 << 9)
|
||||
#define DSRCR_HBCL (1 << 8)
|
||||
#define DSRCR_ADCL(n) (1 << ((n)-1))
|
||||
#define DSRCR_MASK 0x0000cbff
|
||||
|
||||
#define DIER 0x00010
|
||||
#define DIER_TVE (1 << 15)
|
||||
#define DIER_FRE (1 << 14)
|
||||
#define DIER_VBE (1 << 11)
|
||||
#define DIER_RIE (1 << 9)
|
||||
#define DIER_HBE (1 << 8)
|
||||
#define DIER_ADCE(n) (1 << ((n)-1))
|
||||
|
||||
#define CPCR 0x00014
|
||||
#define CPCR_CP4CE (1 << 19)
|
||||
#define CPCR_CP3CE (1 << 18)
|
||||
#define CPCR_CP2CE (1 << 17)
|
||||
#define CPCR_CP1CE (1 << 16)
|
||||
|
||||
#define DPPR 0x00018
|
||||
#define DPPR_DPE(n) (1 << ((n)*4-1))
|
||||
#define DPPR_DPS(n, p) (((p)-1) << DPPR_DPS_SHIFT(n))
|
||||
#define DPPR_DPS_SHIFT(n) (((n)-1)*4)
|
||||
#define DPPR_BPP16 (DPPR_DPE(8) | DPPR_DPS(8, 1)) /* plane1 */
|
||||
#define DPPR_BPP32_P1 (DPPR_DPE(7) | DPPR_DPS(7, 1))
|
||||
#define DPPR_BPP32_P2 (DPPR_DPE(8) | DPPR_DPS(8, 2))
|
||||
#define DPPR_BPP32 (DPPR_BPP32_P1 | DPPR_BPP32_P2) /* plane1 & 2 */
|
||||
|
||||
#define DEFR 0x00020
|
||||
#define DEFR_CODE (0x7773 << 16)
|
||||
#define DEFR_EXSL (1 << 12)
|
||||
#define DEFR_EXVL (1 << 11)
|
||||
#define DEFR_EXUP (1 << 5)
|
||||
#define DEFR_VCUP (1 << 4)
|
||||
#define DEFR_DEFE (1 << 0)
|
||||
|
||||
#define DAPCR 0x00024
|
||||
#define DAPCR_CODE (0x7773 << 16)
|
||||
#define DAPCR_AP2E (1 << 4)
|
||||
#define DAPCR_AP1E (1 << 0)
|
||||
|
||||
#define DCPCR 0x00028
|
||||
#define DCPCR_CODE (0x7773 << 16)
|
||||
#define DCPCR_CA2B (1 << 13)
|
||||
#define DCPCR_CD2F (1 << 12)
|
||||
#define DCPCR_DC2E (1 << 8)
|
||||
#define DCPCR_CAB (1 << 5)
|
||||
#define DCPCR_CDF (1 << 4)
|
||||
#define DCPCR_DCE (1 << 0)
|
||||
|
||||
#define DEFR2 0x00034
|
||||
#define DEFR2_CODE (0x7775 << 16)
|
||||
#define DEFR2_DEFE2G (1 << 0)
|
||||
|
||||
#define DEFR3 0x00038
|
||||
#define DEFR3_CODE (0x7776 << 16)
|
||||
#define DEFR3_EVDA (1 << 14)
|
||||
#define DEFR3_EVDM_1 (1 << 12)
|
||||
#define DEFR3_EVDM_2 (2 << 12)
|
||||
#define DEFR3_EVDM_3 (3 << 12)
|
||||
#define DEFR3_VMSM2_EMA (1 << 6)
|
||||
#define DEFR3_VMSM1_ENA (1 << 4)
|
||||
#define DEFR3_DEFE3 (1 << 0)
|
||||
|
||||
#define DEFR4 0x0003c
|
||||
#define DEFR4_CODE (0x7777 << 16)
|
||||
#define DEFR4_LRUO (1 << 5)
|
||||
#define DEFR4_SPCE (1 << 4)
|
||||
|
||||
#define DVCSR 0x000d0
|
||||
#define DVCSR_VCnFB2_DSA0(n) (0 << ((n)*2+16))
|
||||
#define DVCSR_VCnFB2_DSA1(n) (1 << ((n)*2+16))
|
||||
#define DVCSR_VCnFB2_DSA2(n) (2 << ((n)*2+16))
|
||||
#define DVCSR_VCnFB2_INIT(n) (3 << ((n)*2+16))
|
||||
#define DVCSR_VCnFB2_MASK(n) (3 << ((n)*2+16))
|
||||
#define DVCSR_VCnFB_DSA0(n) (0 << ((n)*2))
|
||||
#define DVCSR_VCnFB_DSA1(n) (1 << ((n)*2))
|
||||
#define DVCSR_VCnFB_DSA2(n) (2 << ((n)*2))
|
||||
#define DVCSR_VCnFB_INIT(n) (3 << ((n)*2))
|
||||
#define DVCSR_VCnFB_MASK(n) (3 << ((n)*2))
|
||||
|
||||
#define DEFR5 0x000e0
|
||||
#define DEFR5_CODE (0x66 << 24)
|
||||
#define DEFR5_YCRGB2_DIS (0 << 14)
|
||||
#define DEFR5_YCRGB2_PRI1 (1 << 14)
|
||||
#define DEFR5_YCRGB2_PRI2 (2 << 14)
|
||||
#define DEFR5_YCRGB2_PRI3 (3 << 14)
|
||||
#define DEFR5_YCRGB2_MASK (3 << 14)
|
||||
#define DEFR5_YCRGB1_DIS (0 << 12)
|
||||
#define DEFR5_YCRGB1_PRI1 (1 << 12)
|
||||
#define DEFR5_YCRGB1_PRI2 (2 << 12)
|
||||
#define DEFR5_YCRGB1_PRI3 (3 << 12)
|
||||
#define DEFR5_YCRGB1_MASK (3 << 12)
|
||||
#define DEFR5_DEFE5 (1 << 0)
|
||||
|
||||
#define DDLTR 0x000e4
|
||||
#define DDLTR_CODE (0x7766 << 16)
|
||||
#define DDLTR_DLAR2 (1 << 6)
|
||||
#define DDLTR_DLAY2 (1 << 5)
|
||||
#define DDLTR_DLAY1 (1 << 1)
|
||||
|
||||
#define DEFR6 0x000e8
|
||||
#define DEFR6_CODE (0x7778 << 16)
|
||||
#define DEFR6_ODPM22_D2SMR (0 << 10)
|
||||
#define DEFR6_ODPM22_DISP (2 << 10)
|
||||
#define DEFR6_ODPM22_CDE (3 << 10)
|
||||
#define DEFR6_ODPM22_MASK (3 << 10)
|
||||
#define DEFR6_ODPM12_DSMR (0 << 8)
|
||||
#define DEFR6_ODPM12_DISP (2 << 8)
|
||||
#define DEFR6_ODPM12_CDE (3 << 8)
|
||||
#define DEFR6_ODPM12_MASK (3 << 8)
|
||||
#define DEFR6_TCNE2 (1 << 6)
|
||||
#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
|
||||
*/
|
||||
|
||||
#define HDSR 0x00040
|
||||
#define HDER 0x00044
|
||||
#define VDSR 0x00048
|
||||
#define VDER 0x0004c
|
||||
#define HCR 0x00050
|
||||
#define HSWR 0x00054
|
||||
#define VCR 0x00058
|
||||
#define VSPR 0x0005c
|
||||
#define EQWR 0x00060
|
||||
#define SPWR 0x00064
|
||||
#define CLAMPSR 0x00070
|
||||
#define CLAMPWR 0x00074
|
||||
#define DESR 0x00078
|
||||
#define DEWR 0x0007c
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Display Attribute Registers
|
||||
*/
|
||||
|
||||
#define CP1TR 0x00080
|
||||
#define CP2TR 0x00084
|
||||
#define CP3TR 0x00088
|
||||
#define CP4TR 0x0008c
|
||||
|
||||
#define DOOR 0x00090
|
||||
#define DOOR_RGB(r, g, b) (((r) << 18) | ((g) << 10) | ((b) << 2))
|
||||
#define CDER 0x00094
|
||||
#define CDER_RGB(r, g, b) (((r) << 18) | ((g) << 10) | ((b) << 2))
|
||||
#define BPOR 0x00098
|
||||
#define BPOR_RGB(r, g, b) (((r) << 18) | ((g) << 10) | ((b) << 2))
|
||||
|
||||
#define RINTOFSR 0x0009c
|
||||
|
||||
#define DSHPR 0x000c8
|
||||
#define DSHPR_CODE (0x7776 << 16)
|
||||
#define DSHPR_PRIH (0xa << 4)
|
||||
#define DSHPR_PRIL_BPP16 (0x8 << 0)
|
||||
#define DSHPR_PRIL_BPP32 (0x9 << 0)
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Display Plane Registers
|
||||
*/
|
||||
|
||||
#define PLANE_OFF 0x00100
|
||||
|
||||
#define PnMR 0x00100 /* plane 1 */
|
||||
#define PnMR_VISL_VIN0 (0 << 26) /* use Video Input 0 */
|
||||
#define PnMR_VISL_VIN1 (1 << 26) /* use Video Input 1 */
|
||||
#define PnMR_VISL_VIN2 (2 << 26) /* use Video Input 2 */
|
||||
#define PnMR_VISL_VIN3 (3 << 26) /* use Video Input 3 */
|
||||
#define PnMR_YCDF_YUYV (1 << 20) /* YUYV format */
|
||||
#define PnMR_TC_R (0 << 17) /* Tranparent color is PnTC1R */
|
||||
#define PnMR_TC_CP (1 << 17) /* Tranparent color is color palette */
|
||||
#define PnMR_WAE (1 << 16) /* Wrap around Enable */
|
||||
#define PnMR_SPIM_TP (0 << 12) /* Transparent Color */
|
||||
#define PnMR_SPIM_ALP (1 << 12) /* Alpha Blending */
|
||||
#define PnMR_SPIM_EOR (2 << 12) /* EOR */
|
||||
#define PnMR_SPIM_TP_OFF (1 << 14) /* No Transparent Color */
|
||||
#define PnMR_CPSL_CP1 (0 << 8) /* Color Palette selected 1 */
|
||||
#define PnMR_CPSL_CP2 (1 << 8) /* Color Palette selected 2 */
|
||||
#define PnMR_CPSL_CP3 (2 << 8) /* Color Palette selected 3 */
|
||||
#define PnMR_CPSL_CP4 (3 << 8) /* Color Palette selected 4 */
|
||||
#define PnMR_DC (1 << 7) /* Display Area Change */
|
||||
#define PnMR_BM_MD (0 << 4) /* Manual Display Change Mode */
|
||||
#define PnMR_BM_AR (1 << 4) /* Auto Rendering Mode */
|
||||
#define PnMR_BM_AD (2 << 4) /* Auto Display Change Mode */
|
||||
#define PnMR_BM_VC (3 << 4) /* Video Capture Mode */
|
||||
#define PnMR_DDDF_8BPP (0 << 0) /* 8bit */
|
||||
#define PnMR_DDDF_16BPP (1 << 0) /* 16bit or 32bit */
|
||||
#define PnMR_DDDF_ARGB (2 << 0) /* ARGB */
|
||||
#define PnMR_DDDF_YC (3 << 0) /* YC */
|
||||
#define PnMR_DDDF_MASK (3 << 0)
|
||||
|
||||
#define PnMWR 0x00104
|
||||
|
||||
#define PnALPHAR 0x00108
|
||||
#define PnALPHAR_ABIT_1 (0 << 12)
|
||||
#define PnALPHAR_ABIT_0 (1 << 12)
|
||||
#define PnALPHAR_ABIT_X (2 << 12)
|
||||
|
||||
#define PnDSXR 0x00110
|
||||
#define PnDSYR 0x00114
|
||||
#define PnDPXR 0x00118
|
||||
#define PnDPYR 0x0011c
|
||||
|
||||
#define PnDSA0R 0x00120
|
||||
#define PnDSA1R 0x00124
|
||||
#define PnDSA2R 0x00128
|
||||
#define PnDSA_MASK 0xfffffff0
|
||||
|
||||
#define PnSPXR 0x00130
|
||||
#define PnSPYR 0x00134
|
||||
#define PnWASPR 0x00138
|
||||
#define PnWAMWR 0x0013c
|
||||
|
||||
#define PnBTR 0x00140
|
||||
|
||||
#define PnTC1R 0x00144
|
||||
#define PnTC2R 0x00148
|
||||
#define PnTC3R 0x0014c
|
||||
#define PnTC3R_CODE (0x66 << 24)
|
||||
|
||||
#define PnMLR 0x00150
|
||||
|
||||
#define PnSWAPR 0x00180
|
||||
#define PnSWAPR_DIGN (1 << 4)
|
||||
#define PnSWAPR_SPQW (1 << 3)
|
||||
#define PnSWAPR_SPLW (1 << 2)
|
||||
#define PnSWAPR_SPWD (1 << 1)
|
||||
#define PnSWAPR_SPBY (1 << 0)
|
||||
|
||||
#define PnDDCR 0x00184
|
||||
#define PnDDCR_CODE (0x7775 << 16)
|
||||
#define PnDDCR_LRGB1 (1 << 11)
|
||||
#define PnDDCR_LRGB0 (1 << 10)
|
||||
|
||||
#define PnDDCR2 0x00188
|
||||
#define PnDDCR2_CODE (0x7776 << 16)
|
||||
#define PnDDCR2_NV21 (1 << 5)
|
||||
#define PnDDCR2_Y420 (1 << 4)
|
||||
#define PnDDCR2_DIVU (1 << 1)
|
||||
#define PnDDCR2_DIVY (1 << 0)
|
||||
|
||||
#define PnDDCR4 0x00190
|
||||
#define PnDDCR4_CODE (0x7766 << 16)
|
||||
#define PnDDCR4_SDFS_RGB (0 << 4)
|
||||
#define PnDDCR4_SDFS_YC (5 << 4)
|
||||
#define PnDDCR4_SDFS_MASK (7 << 4)
|
||||
#define PnDDCR4_EDF_NONE (0 << 0)
|
||||
#define PnDDCR4_EDF_ARGB8888 (1 << 0)
|
||||
#define PnDDCR4_EDF_RGB888 (2 << 0)
|
||||
#define PnDDCR4_EDF_RGB666 (3 << 0)
|
||||
#define PnDDCR4_EDF_MASK (7 << 0)
|
||||
|
||||
#define APnMR 0x0a100
|
||||
#define APnMR_WAE (1 << 16) /* Wrap around Enable */
|
||||
#define APnMR_DC (1 << 7) /* Display Area Change */
|
||||
#define APnMR_BM_MD (0 << 4) /* Manual Display Change Mode */
|
||||
#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 DCSAR 0x0c120
|
||||
#define DCMLR 0x0c150
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Color Palette Registers
|
||||
*/
|
||||
|
||||
#define CP1_000R 0x01000
|
||||
#define CP1_255R 0x013fc
|
||||
#define CP2_000R 0x02000
|
||||
#define CP2_255R 0x023fc
|
||||
#define CP3_000R 0x03000
|
||||
#define CP3_255R 0x033fc
|
||||
#define CP4_000R 0x04000
|
||||
#define CP4_255R 0x043fc
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* External Synchronization Control Registers
|
||||
*/
|
||||
|
||||
#define ESCR 0x10000
|
||||
#define ESCR2 0x31000
|
||||
#define ESCR_DCLKOINV (1 << 25)
|
||||
#define ESCR_DCLKSEL_DCLKIN (0 << 20)
|
||||
#define ESCR_DCLKSEL_CLKS (1 << 20)
|
||||
#define ESCR_DCLKSEL_MASK (1 << 20)
|
||||
#define ESCR_DCLKDIS (1 << 16)
|
||||
#define ESCR_SYNCSEL_OFF (0 << 8)
|
||||
#define ESCR_SYNCSEL_EXVSYNC (2 << 8)
|
||||
#define ESCR_SYNCSEL_EXHSYNC (3 << 8)
|
||||
#define ESCR_FRQSEL_MASK (0x3f << 0)
|
||||
|
||||
#define OTAR 0x10004
|
||||
#define OTAR2 0x31004
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Dual Display Output Control Registers
|
||||
*/
|
||||
|
||||
#define DORCR 0x11000
|
||||
#define DORCR_PG2T (1 << 30)
|
||||
#define DORCR_DK2S (1 << 28)
|
||||
#define DORCR_PG2D_DS1 (0 << 24)
|
||||
#define DORCR_PG2D_DS2 (1 << 24)
|
||||
#define DORCR_PG2D_FIX0 (2 << 24)
|
||||
#define DORCR_PG2D_DOOR (3 << 24)
|
||||
#define DORCR_PG2D_MASK (3 << 24)
|
||||
#define DORCR_DR1D (1 << 21)
|
||||
#define DORCR_PG1D_DS1 (0 << 16)
|
||||
#define DORCR_PG1D_DS2 (1 << 16)
|
||||
#define DORCR_PG1D_FIX0 (2 << 16)
|
||||
#define DORCR_PG1D_DOOR (3 << 16)
|
||||
#define DORCR_PG1D_MASK (3 << 16)
|
||||
#define DORCR_RGPV (1 << 4)
|
||||
#define DORCR_DPRS (1 << 0)
|
||||
|
||||
#define DPTSR 0x11004
|
||||
#define DPTSR_PnDK(n) (1 << ((n) + 16))
|
||||
#define DPTSR_PnTS(n) (1 << (n))
|
||||
|
||||
#define DAPTSR 0x11008
|
||||
#define DAPTSR_APnDK(n) (1 << ((n) + 16))
|
||||
#define DAPTSR_APnTS(n) (1 << (n))
|
||||
|
||||
#define DS1PR 0x11020
|
||||
#define DS2PR 0x11024
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* YC-RGB Conversion Coefficient Registers
|
||||
*/
|
||||
|
||||
#define YNCR 0x11080
|
||||
#define YNOR 0x11084
|
||||
#define CRNOR 0x11088
|
||||
#define CBNOR 0x1108c
|
||||
#define RCRCR 0x11090
|
||||
#define GCRCR 0x11094
|
||||
#define GCBCR 0x11098
|
||||
#define BCBCR 0x1109c
|
||||
|
||||
#endif /* __RCAR_DU_REGS_H__ */
|
89
drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
Normal file
89
drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* rcar_du_vgacon.c -- R-Car Display Unit VGA Connector
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_encoder.h"
|
||||
#include "rcar_du_kms.h"
|
||||
#include "rcar_du_vgacon.h"
|
||||
|
||||
static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs connector_helper_funcs = {
|
||||
.get_modes = rcar_du_vga_connector_get_modes,
|
||||
.best_encoder = rcar_du_connector_best_encoder,
|
||||
};
|
||||
|
||||
static void rcar_du_vga_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.detect = rcar_du_vga_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = rcar_du_vga_connector_destroy,
|
||||
};
|
||||
|
||||
int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc)
|
||||
{
|
||||
struct rcar_du_connector *rcon;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
|
||||
rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
|
||||
if (rcon == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
connector = &rcon->connector;
|
||||
connector->display_info.width_mm = 0;
|
||||
connector->display_info.height_mm = 0;
|
||||
|
||||
ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
|
||||
DRM_MODE_CONNECTOR_VGA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drm_connector_helper_add(connector, &connector_helper_funcs);
|
||||
ret = drm_connector_register(connector);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
|
||||
drm_object_property_set_value(&connector->base,
|
||||
rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
|
||||
|
||||
ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
connector->encoder = &renc->encoder;
|
||||
rcon->encoder = renc;
|
||||
|
||||
return 0;
|
||||
}
|
23
drivers/gpu/drm/rcar-du/rcar_du_vgacon.h
Normal file
23
drivers/gpu/drm/rcar-du/rcar_du_vgacon.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* rcar_du_vgacon.h -- R-Car Display Unit VGA Connector
|
||||
*
|
||||
* Copyright (C) 2013-2014 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __RCAR_DU_VGACON_H__
|
||||
#define __RCAR_DU_VGACON_H__
|
||||
|
||||
struct rcar_du_device;
|
||||
struct rcar_du_encoder;
|
||||
|
||||
int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc);
|
||||
|
||||
#endif /* __RCAR_DU_VGACON_H__ */
|
69
drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
Normal file
69
drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
Normal 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__ */
|
Loading…
Add table
Add a link
Reference in a new issue