Commit 2ba446f8 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'shmob-drm-atomic-dt-tag2' of...

Merge tag 'shmob-drm-atomic-dt-tag2' of git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers into drm-next

drm: renesas: shmobile: Atomic conversion + DT support

Currently, there are two drivers for the LCD controller on Renesas
SuperH-based and ARM-based SH-Mobile and R-Mobile SoCs:
  1. sh_mobile_lcdcfb, using the fbdev framework,
  2. shmob_drm, using the DRM framework.
However, only the former driver is used, as all platform support
integrates the former.  None of these drivers support DT-based systems.

Convert the SH-Mobile DRM driver to atomic modesetting, and add DT
support, complemented by the customary set of fixes and improvements.
Acked-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Link: https://lore.kernel.org/r/cover.1694767208.git.geert+renesas@glider.be/Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Geert Uytterhoeven <geert@linux-m68k.org>
Link: https://patchwork.freedesktop.org/patch/msgid/CAMuHMdUF61V5qNyKbrTGxZfEJvCVuLO7q2R5MqZYkzRC_cNr0w@mail.gmail.com
parents 63180809 1399ebac
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/renesas,shmobile-lcdc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas SH-Mobile LCD Controller (LCDC)
maintainers:
- Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
- Geert Uytterhoeven <geert+renesas@glider.be>
properties:
compatible:
enum:
- renesas,r8a7740-lcdc # R-Mobile A1
- renesas,sh73a0-lcdc # SH-Mobile AG5
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
minItems: 1
maxItems: 5
description:
Only the functional clock is mandatory.
Some of the optional clocks are model-dependent (e.g. "video" (a.k.a.
"vou" or "dv_clk") is available on R-Mobile A1 only).
clock-names:
minItems: 1
items:
- const: fck
- enum: [ media, lclk, hdmi, video ]
- enum: [ media, lclk, hdmi, video ]
- enum: [ media, lclk, hdmi, video ]
- enum: [ media, lclk, hdmi, video ]
power-domains:
maxItems: 1
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: LCD port (R-Mobile A1 and SH-Mobile AG5)
unevaluatedProperties: false
port@1:
$ref: /schemas/graph.yaml#/properties/port
description: HDMI port (R-Mobile A1 LCDC1 and SH-Mobile AG5)
unevaluatedProperties: false
port@2:
$ref: /schemas/graph.yaml#/properties/port
description: MIPI-DSI port (SH-Mobile AG5)
unevaluatedProperties: false
required:
- port@0
unevaluatedProperties: false
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- power-domains
- ports
additionalProperties: false
allOf:
- if:
properties:
compatible:
contains:
const: renesas,r8a7740-lcdc
then:
properties:
ports:
properties:
port@2: false
- if:
properties:
compatible:
contains:
const: renesas,sh73a0-lcdc
then:
properties:
ports:
required:
- port@1
- port@2
examples:
- |
#include <dt-bindings/clock/r8a7740-clock.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
lcd-controller@fe940000 {
compatible = "renesas,r8a7740-lcdc";
reg = <0xfe940000 0x4000>;
interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp1_clks R8A7740_CLK_LCDC0>,
<&cpg_clocks R8A7740_CLK_M3>, <&lcdlclk0_clk>,
<&vou_clk>;
clock-names = "fck", "media", "lclk", "video";
power-domains = <&pd_a4lc>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lcdc0_rgb: endpoint {
};
};
};
};
......@@ -949,6 +949,78 @@ The following tables list existing packed RGB formats.
- b\ :sub:`2`
- b\ :sub:`1`
- b\ :sub:`0`
* .. _MEDIA-BUS-FMT-RGB666-2X9-BE:
- MEDIA_BUS_FMT_RGB666_2X9_BE
- 0x1025
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- r\ :sub:`5`
- r\ :sub:`4`
- r\ :sub:`3`
- r\ :sub:`2`
- r\ :sub:`1`
- r\ :sub:`0`
- g\ :sub:`5`
- g\ :sub:`4`
- g\ :sub:`3`
* -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- g\ :sub:`2`
- g\ :sub:`1`
- g\ :sub:`0`
- b\ :sub:`5`
- b\ :sub:`4`
- b\ :sub:`3`
- b\ :sub:`2`
- b\ :sub:`1`
- b\ :sub:`0`
* .. _MEDIA-BUS-FMT-BGR666-1X18:
- MEDIA_BUS_FMT_BGR666_1X18
......
......@@ -7053,7 +7053,7 @@ F: drivers/gpu/host1x/
F: include/linux/host1x.h
F: include/uapi/drm/tegra_drm.h
DRM DRIVERS FOR RENESAS
DRM DRIVERS FOR RENESAS R-CAR
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
L: dri-devel@lists.freedesktop.org
......@@ -7064,7 +7064,16 @@ F: Documentation/devicetree/bindings/display/bridge/renesas,dsi-csi2-tx.yaml
F: Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.yaml
F: Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml
F: Documentation/devicetree/bindings/display/renesas,du.yaml
F: drivers/gpu/drm/renesas/
F: drivers/gpu/drm/renesas/rcar-du/
DRM DRIVERS FOR RENESAS SHMOBILE
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
M: Geert Uytterhoeven <geert+renesas@glider.be>
L: dri-devel@lists.freedesktop.org
L: linux-renesas-soc@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/display/renesas,shmobile-lcdc.yaml
F: drivers/gpu/drm/renesas/shmobile/
F: include/linux/platform_data/shmob_drm.h
DRM DRIVERS FOR ROCKCHIP
......
# SPDX-License-Identifier: GPL-2.0
config DRM_SHMOBILE
tristate "DRM Support for SH Mobile"
depends on DRM
depends on DRM && PM
depends on ARCH_RENESAS || ARCH_SHMOBILE || COMPILE_TEST
select BACKLIGHT_CLASS_DEVICE
select DRM_KMS_HELPER
select DRM_GEM_DMA_HELPER
select VIDEOMODE_HELPERS
help
Choose this option if you have an SH Mobile chipset.
If M is selected the module will be called shmob-drm.
......
# SPDX-License-Identifier: GPL-2.0
shmob-drm-y := shmob_drm_backlight.o \
shmob_drm_crtc.o \
shmob-drm-y := shmob_drm_crtc.o \
shmob_drm_drv.o \
shmob_drm_kms.o \
shmob_drm_plane.o
......
// SPDX-License-Identifier: GPL-2.0+
/*
* shmob_drm_backlight.c -- SH Mobile DRM Backlight
*
* Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
#include <linux/backlight.h>
#include "shmob_drm_backlight.h"
#include "shmob_drm_crtc.h"
#include "shmob_drm_drv.h"
static int shmob_drm_backlight_update(struct backlight_device *bdev)
{
struct shmob_drm_connector *scon = bl_get_data(bdev);
struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
int brightness = backlight_get_brightness(bdev);
return bdata->set_brightness(brightness);
}
static int shmob_drm_backlight_get_brightness(struct backlight_device *bdev)
{
struct shmob_drm_connector *scon = bl_get_data(bdev);
struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
return bdata->get_brightness();
}
static const struct backlight_ops shmob_drm_backlight_ops = {
.options = BL_CORE_SUSPENDRESUME,
.update_status = shmob_drm_backlight_update,
.get_brightness = shmob_drm_backlight_get_brightness,
};
void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode)
{
if (scon->backlight == NULL)
return;
scon->backlight->props.power = mode == DRM_MODE_DPMS_ON
? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
backlight_update_status(scon->backlight);
}
int shmob_drm_backlight_init(struct shmob_drm_connector *scon)
{
struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
struct drm_connector *connector = &scon->connector;
struct drm_device *dev = connector->dev;
struct backlight_device *backlight;
if (!bdata->max_brightness)
return 0;
backlight = backlight_device_register(bdata->name, dev->dev, scon,
&shmob_drm_backlight_ops, NULL);
if (IS_ERR(backlight)) {
dev_err(dev->dev, "unable to register backlight device: %ld\n",
PTR_ERR(backlight));
return PTR_ERR(backlight);
}
backlight->props.max_brightness = bdata->max_brightness;
backlight->props.brightness = bdata->max_brightness;
backlight->props.power = FB_BLANK_POWERDOWN;
backlight_update_status(backlight);
scon->backlight = backlight;
return 0;
}
void shmob_drm_backlight_exit(struct shmob_drm_connector *scon)
{
backlight_device_unregister(scon->backlight);
}
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* shmob_drm_backlight.h -- SH Mobile DRM Backlight
*
* Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
#ifndef __SHMOB_DRM_BACKLIGHT_H__
#define __SHMOB_DRM_BACKLIGHT_H__
struct shmob_drm_connector;
void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode);
int shmob_drm_backlight_init(struct shmob_drm_connector *scon);
void shmob_drm_backlight_exit(struct shmob_drm_connector *scon);
#endif /* __SHMOB_DRM_BACKLIGHT_H__ */
......@@ -14,39 +14,30 @@
#include <drm/drm_connector.h>
#include <drm/drm_encoder.h>
struct backlight_device;
#include <linux/wait.h>
#include <video/videomode.h>
struct drm_pending_vblank_event;
struct shmob_drm_device;
struct shmob_drm_format_info;
struct shmob_drm_crtc {
struct drm_crtc crtc;
struct drm_crtc base;
struct drm_pending_vblank_event *event;
int dpms;
const struct shmob_drm_format_info *format;
unsigned long dma[2];
unsigned int line_size;
bool started;
};
struct shmob_drm_encoder {
struct drm_encoder encoder;
int dpms;
wait_queue_head_t flip_wait;
};
/* Legacy connector */
struct shmob_drm_connector {
struct drm_connector connector;
struct drm_connector base;
struct drm_encoder *encoder;
struct backlight_device *backlight;
const struct videomode *mode;
};
int shmob_drm_crtc_create(struct shmob_drm_device *sdev);
void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc);
void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc);
void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc);
int shmob_drm_encoder_create(struct shmob_drm_device *sdev);
int shmob_drm_connector_create(struct shmob_drm_device *sdev,
......
......@@ -11,13 +11,17 @@
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_modeset_helper.h>
#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
......@@ -31,57 +35,23 @@
* Hardware initialization
*/
static int shmob_drm_init_interface(struct shmob_drm_device *sdev)
{
static const u32 ldmt1r[] = {
[SHMOB_DRM_IFACE_RGB8] = LDMT1R_MIFTYP_RGB8,
[SHMOB_DRM_IFACE_RGB9] = LDMT1R_MIFTYP_RGB9,
[SHMOB_DRM_IFACE_RGB12A] = LDMT1R_MIFTYP_RGB12A,
[SHMOB_DRM_IFACE_RGB12B] = LDMT1R_MIFTYP_RGB12B,
[SHMOB_DRM_IFACE_RGB16] = LDMT1R_MIFTYP_RGB16,
[SHMOB_DRM_IFACE_RGB18] = LDMT1R_MIFTYP_RGB18,
[SHMOB_DRM_IFACE_RGB24] = LDMT1R_MIFTYP_RGB24,
[SHMOB_DRM_IFACE_YUV422] = LDMT1R_MIFTYP_YCBCR,
[SHMOB_DRM_IFACE_SYS8A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8A,
[SHMOB_DRM_IFACE_SYS8B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8B,
[SHMOB_DRM_IFACE_SYS8C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8C,
[SHMOB_DRM_IFACE_SYS8D] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8D,
[SHMOB_DRM_IFACE_SYS9] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS9,
[SHMOB_DRM_IFACE_SYS12] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS12,
[SHMOB_DRM_IFACE_SYS16A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16A,
[SHMOB_DRM_IFACE_SYS16B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16B,
[SHMOB_DRM_IFACE_SYS16C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16C,
[SHMOB_DRM_IFACE_SYS18] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS18,
[SHMOB_DRM_IFACE_SYS24] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS24,
};
if (sdev->pdata->iface.interface >= ARRAY_SIZE(ldmt1r)) {
dev_err(sdev->dev, "invalid interface type %u\n",
sdev->pdata->iface.interface);
return -EINVAL;
}
sdev->ldmt1r = ldmt1r[sdev->pdata->iface.interface];
return 0;
}
static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev,
enum shmob_drm_clk_source clksrc)
enum shmob_drm_clk_source clksrc)
{
struct clk *clk;
char *clkname;
switch (clksrc) {
case SHMOB_DRM_CLK_BUS:
clkname = "bus_clk";
clkname = "fck";
sdev->lddckr = LDDCKR_ICKSEL_BUS;
break;
case SHMOB_DRM_CLK_PERIPHERAL:
clkname = "peripheral_clk";
clkname = "media";
sdev->lddckr = LDDCKR_ICKSEL_MIPI;
break;
case SHMOB_DRM_CLK_EXTERNAL:
clkname = NULL;
clkname = "lclk";
sdev->lddckr = LDDCKR_ICKSEL_HDMI;
break;
default:
......@@ -105,7 +75,7 @@ static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev,
static irqreturn_t shmob_drm_irq(int irq, void *arg)
{
struct drm_device *dev = arg;
struct shmob_drm_device *sdev = dev->dev_private;
struct shmob_drm_device *sdev = to_shmob_device(dev);
unsigned long flags;
u32 status;
......@@ -119,7 +89,7 @@ static irqreturn_t shmob_drm_irq(int irq, void *arg)
spin_unlock_irqrestore(&sdev->irq_lock, flags);
if (status & LDINTR_VES) {
drm_handle_vblank(dev, 0);
drm_crtc_handle_vblank(&sdev->crtc.base);
shmob_drm_crtc_finish_page_flip(&sdev->crtc);
}
......@@ -129,7 +99,7 @@ static irqreturn_t shmob_drm_irq(int irq, void *arg)
DEFINE_DRM_GEM_DMA_FOPS(shmob_drm_fops);
static const struct drm_driver shmob_drm_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET,
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
DRM_GEM_DMA_DRIVER_OPS,
.fops = &shmob_drm_fops,
.name = "shmob-drm",
......@@ -147,26 +117,45 @@ static int shmob_drm_pm_suspend(struct device *dev)
{
struct shmob_drm_device *sdev = dev_get_drvdata(dev);
drm_kms_helper_poll_disable(sdev->ddev);
shmob_drm_crtc_suspend(&sdev->crtc);
return drm_mode_config_helper_suspend(&sdev->ddev);
}
static int shmob_drm_pm_resume(struct device *dev)
{
struct shmob_drm_device *sdev = dev_get_drvdata(dev);
return drm_mode_config_helper_resume(&sdev->ddev);
}
static int shmob_drm_pm_runtime_suspend(struct device *dev)
{
struct shmob_drm_device *sdev = dev_get_drvdata(dev);
if (sdev->clock)
clk_disable_unprepare(sdev->clock);
return 0;
}
static int shmob_drm_pm_resume(struct device *dev)
static int shmob_drm_pm_runtime_resume(struct device *dev)
{
struct shmob_drm_device *sdev = dev_get_drvdata(dev);
int ret;
drm_modeset_lock_all(sdev->ddev);
shmob_drm_crtc_resume(&sdev->crtc);
drm_modeset_unlock_all(sdev->ddev);
if (sdev->clock) {
ret = clk_prepare_enable(sdev->clock);
if (ret < 0)
return ret;
}
drm_kms_helper_poll_enable(sdev->ddev);
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(shmob_drm_pm_ops,
shmob_drm_pm_suspend, shmob_drm_pm_resume);
static const struct dev_pm_ops shmob_drm_pm_ops = {
SYSTEM_SLEEP_PM_OPS(shmob_drm_pm_suspend, shmob_drm_pm_resume)
RUNTIME_PM_OPS(shmob_drm_pm_runtime_suspend,
shmob_drm_pm_runtime_resume, NULL)
};
/* -----------------------------------------------------------------------------
* Platform driver
......@@ -175,37 +164,45 @@ static DEFINE_SIMPLE_DEV_PM_OPS(shmob_drm_pm_ops,
static void shmob_drm_remove(struct platform_device *pdev)
{
struct shmob_drm_device *sdev = platform_get_drvdata(pdev);
struct drm_device *ddev = sdev->ddev;
struct drm_device *ddev = &sdev->ddev;
drm_dev_unregister(ddev);
drm_atomic_helper_shutdown(ddev);
drm_kms_helper_poll_fini(ddev);
free_irq(sdev->irq, ddev);
drm_dev_put(ddev);
}
static int shmob_drm_probe(struct platform_device *pdev)
{
struct shmob_drm_platform_data *pdata = pdev->dev.platform_data;
const struct shmob_drm_config *config;
struct shmob_drm_device *sdev;
struct drm_device *ddev;
unsigned int i;
int ret;
if (pdata == NULL) {
config = of_device_get_match_data(&pdev->dev);
if (!config && !pdata) {
dev_err(&pdev->dev, "no platform data\n");
return -EINVAL;
}
/*
* Allocate and initialize the driver private data, I/O resources and
* clocks.
* Allocate and initialize the DRM device, driver private data, I/O
* resources and clocks.
*/
sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
if (sdev == NULL)
return -ENOMEM;
sdev = devm_drm_dev_alloc(&pdev->dev, &shmob_drm_driver,
struct shmob_drm_device, ddev);
if (IS_ERR(sdev))
return PTR_ERR(sdev);
ddev = &sdev->ddev;
sdev->dev = &pdev->dev;
sdev->pdata = pdata;
if (config) {
sdev->config = *config;
} else {
sdev->pdata = pdata;
sdev->config.clk_source = pdata->clk_source;
sdev->config.clk_div = pdata->iface.clk_div;
}
spin_lock_init(&sdev->irq_lock);
platform_set_drvdata(pdev, sdev);
......@@ -214,49 +211,32 @@ static int shmob_drm_probe(struct platform_device *pdev)
if (IS_ERR(sdev->mmio))
return PTR_ERR(sdev->mmio);
ret = shmob_drm_setup_clocks(sdev, pdata->clk_source);
ret = shmob_drm_setup_clocks(sdev, sdev->config.clk_source);
if (ret < 0)
return ret;
ret = shmob_drm_init_interface(sdev);
if (ret < 0)
ret = devm_pm_runtime_enable(&pdev->dev);
if (ret)
return ret;
/* Allocate and initialize the DRM device. */
ddev = drm_dev_alloc(&shmob_drm_driver, &pdev->dev);
if (IS_ERR(ddev))
return PTR_ERR(ddev);
sdev->ddev = ddev;
ddev->dev_private = sdev;
ret = shmob_drm_modeset_init(sdev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to initialize mode setting\n");
goto err_free_drm_dev;
}
for (i = 0; i < 4; ++i) {
ret = shmob_drm_plane_create(sdev, i);
if (ret < 0) {
dev_err(&pdev->dev, "failed to create plane %u\n", i);
goto err_modeset_cleanup;
}
}
ret = drm_vblank_init(ddev, 1);
if (ret < 0) {
dev_err(&pdev->dev, "failed to initialize vblank\n");
goto err_modeset_cleanup;
return ret;
}
ret = shmob_drm_modeset_init(sdev);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"failed to initialize mode setting\n");
ret = platform_get_irq(pdev, 0);
if (ret < 0)
goto err_modeset_cleanup;
sdev->irq = ret;
ret = request_irq(sdev->irq, shmob_drm_irq, 0, ddev->driver->name,
ddev);
ret = devm_request_irq(&pdev->dev, sdev->irq, shmob_drm_irq, 0,
ddev->driver->name, ddev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to install IRQ handler\n");
goto err_modeset_cleanup;
......@@ -268,28 +248,35 @@ static int shmob_drm_probe(struct platform_device *pdev)
*/
ret = drm_dev_register(ddev, 0);
if (ret < 0)
goto err_irq_uninstall;
goto err_modeset_cleanup;
drm_fbdev_generic_setup(ddev, 16);
return 0;
err_irq_uninstall:
free_irq(sdev->irq, ddev);
err_modeset_cleanup:
drm_kms_helper_poll_fini(ddev);
err_free_drm_dev:
drm_dev_put(ddev);
return ret;
}
static const struct shmob_drm_config shmob_arm_config = {
.clk_source = SHMOB_DRM_CLK_BUS,
.clk_div = 5,
};
static const struct of_device_id shmob_drm_of_table[] __maybe_unused = {
{ .compatible = "renesas,r8a7740-lcdc", .data = &shmob_arm_config, },
{ .compatible = "renesas,sh73a0-lcdc", .data = &shmob_arm_config, },
{ /* sentinel */ }
};
static struct platform_driver shmob_drm_platform_driver = {
.probe = shmob_drm_probe,
.remove_new = shmob_drm_remove,
.driver = {
.name = "shmob-drm",
.pm = pm_sleep_ptr(&shmob_drm_pm_ops),
.of_match_table = of_match_ptr(shmob_drm_of_table),
.pm = &shmob_drm_pm_ops,
},
};
......
......@@ -20,23 +20,33 @@ struct clk;
struct device;
struct drm_device;
struct shmob_drm_config {
enum shmob_drm_clk_source clk_source;
unsigned int clk_div;
};
struct shmob_drm_device {
struct device *dev;
const struct shmob_drm_platform_data *pdata;
struct shmob_drm_config config;
void __iomem *mmio;
struct clk *clock;
u32 lddckr;
u32 ldmt1r;
unsigned int irq;
spinlock_t irq_lock; /* Protects hardware LDINTR register */
struct drm_device *ddev;
struct drm_device ddev;
struct shmob_drm_crtc crtc;
struct shmob_drm_encoder encoder;
struct shmob_drm_connector connector;
struct drm_encoder encoder;
struct drm_connector *connector;
};
static inline struct shmob_drm_device *to_shmob_device(struct drm_device *dev)
{
return container_of(dev, struct shmob_drm_device, ddev);
}
#endif /* __SHMOB_DRM_DRV_H__ */
......@@ -7,6 +7,7 @@
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fourcc.h>
......@@ -17,6 +18,7 @@
#include "shmob_drm_crtc.h"
#include "shmob_drm_drv.h"
#include "shmob_drm_kms.h"
#include "shmob_drm_plane.h"
#include "shmob_drm_regs.h"
/* -----------------------------------------------------------------------------
......@@ -27,53 +29,73 @@ static const struct shmob_drm_format_info shmob_drm_format_infos[] = {
{
.fourcc = DRM_FORMAT_RGB565,
.bpp = 16,
.yuv = false,
.lddfr = LDDFR_PKF_RGB16,
.ldddsr = LDDDSR_LS | LDDDSR_WS,
.ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW |
LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16,
}, {
.fourcc = DRM_FORMAT_RGB888,
.bpp = 24,
.yuv = false,
.lddfr = LDDFR_PKF_RGB24,
.ldddsr = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS,
.ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW |
LDBBSIFR_SWPB | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24,
}, {
.fourcc = DRM_FORMAT_ARGB8888,
.bpp = 32,
.yuv = false,
.lddfr = LDDFR_PKF_ARGB32,
.ldddsr = LDDDSR_LS,
.ldbbsifr = LDBBSIFR_AL_PK | LDBBSIFR_SWPL | LDBBSIFR_RY |
LDBBSIFR_RPKF_ARGB32,
}, {
.fourcc = DRM_FORMAT_XRGB8888,
.bpp = 32,
.yuv = false,
.lddfr = LDDFR_PKF_ARGB32,
.ldddsr = LDDDSR_LS,
.ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_RY |
LDBBSIFR_RPKF_ARGB32,
}, {
.fourcc = DRM_FORMAT_NV12,
.bpp = 12,
.yuv = true,
.lddfr = LDDFR_CC | LDDFR_YF_420,
.ldddsr = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS,
.ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW |
LDBBSIFR_SWPB | LDBBSIFR_CHRR_420,
}, {
.fourcc = DRM_FORMAT_NV21,
.bpp = 12,
.yuv = true,
.lddfr = LDDFR_CC | LDDFR_YF_420,
.ldddsr = LDDDSR_LS | LDDDSR_WS,
.ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW |
LDBBSIFR_CHRR_420,
}, {
.fourcc = DRM_FORMAT_NV16,
.bpp = 16,
.yuv = true,
.lddfr = LDDFR_CC | LDDFR_YF_422,
.ldddsr = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS,
.ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW |
LDBBSIFR_SWPB | LDBBSIFR_CHRR_422,
}, {
.fourcc = DRM_FORMAT_NV61,
.bpp = 16,
.yuv = true,
.lddfr = LDDFR_CC | LDDFR_YF_422,
.ldddsr = LDDDSR_LS | LDDDSR_WS,
.ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW |
LDBBSIFR_CHRR_422,
}, {
.fourcc = DRM_FORMAT_NV24,
.bpp = 24,
.yuv = true,
.lddfr = LDDFR_CC | LDDFR_YF_444,
.ldddsr = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS,
.ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW |
LDBBSIFR_SWPB | LDBBSIFR_CHRR_444,
}, {
.fourcc = DRM_FORMAT_NV42,
.bpp = 24,
.yuv = true,
.lddfr = LDDFR_CC | LDDFR_YF_444,
.ldddsr = LDDDSR_LS | LDDDSR_WS,
.ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW |
LDBBSIFR_CHRR_444,
},
};
......@@ -112,7 +134,7 @@ shmob_drm_fb_create(struct drm_device *dev, struct drm_file *file_priv,
return ERR_PTR(-EINVAL);
}
if (format->yuv) {
if (shmob_drm_format_is_yuv(format)) {
unsigned int chroma_cpp = format->bpp == 24 ? 2 : 1;
if (mode_cmd->pitches[1] != mode_cmd->pitches[0] * chroma_cpp) {
......@@ -127,29 +149,40 @@ shmob_drm_fb_create(struct drm_device *dev, struct drm_file *file_priv,
static const struct drm_mode_config_funcs shmob_drm_mode_config_funcs = {
.fb_create = shmob_drm_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
int shmob_drm_modeset_init(struct shmob_drm_device *sdev)
{
struct drm_device *dev = &sdev->ddev;
int ret;
ret = drmm_mode_config_init(sdev->ddev);
ret = drmm_mode_config_init(dev);
if (ret)
return ret;
shmob_drm_crtc_create(sdev);
shmob_drm_encoder_create(sdev);
shmob_drm_connector_create(sdev, &sdev->encoder.encoder);
ret = shmob_drm_crtc_create(sdev);
if (ret < 0)
return ret;
ret = shmob_drm_encoder_create(sdev);
if (ret < 0)
return ret;
ret = shmob_drm_connector_create(sdev, &sdev->encoder);
if (ret < 0)
return ret;
drm_kms_helper_poll_init(sdev->ddev);
drm_mode_config_reset(dev);
sdev->ddev->mode_config.min_width = 0;
sdev->ddev->mode_config.min_height = 0;
sdev->ddev->mode_config.max_width = 4095;
sdev->ddev->mode_config.max_height = 4095;
sdev->ddev->mode_config.funcs = &shmob_drm_mode_config_funcs;
drm_kms_helper_poll_init(dev);
drm_helper_disable_unused_functions(sdev->ddev);
sdev->ddev.mode_config.min_width = 0;
sdev->ddev.mode_config.min_height = 0;
sdev->ddev.mode_config.max_width = 4095;
sdev->ddev.mode_config.max_height = 4095;
sdev->ddev.mode_config.funcs = &shmob_drm_mode_config_funcs;
return 0;
}
......@@ -17,11 +17,14 @@ struct shmob_drm_device;
struct shmob_drm_format_info {
u32 fourcc;
unsigned int bpp;
bool yuv;
u32 lddfr;
u32 lddfr; /* LCD Data Format Register */
u16 ldbbsifr; /* CHn Source Image Format Register low bits */
u8 ldddsr; /* LCDC Input Image Data Swap Register low bits */
u8 bpp;
};
#define shmob_drm_format_is_yuv(format) ((format)->lddfr & LDDFR_CC)
const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc);
int shmob_drm_modeset_init(struct shmob_drm_device *sdev);
......
......@@ -13,7 +13,8 @@
struct drm_plane;
struct shmob_drm_device;
int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index);
void shmob_drm_plane_setup(struct drm_plane *plane);
struct drm_plane *shmob_drm_plane_create(struct shmob_drm_device *sdev,
enum drm_plane_type type,
unsigned int index);
#endif /* __SHMOB_DRM_PLANE_H__ */
......@@ -10,7 +10,7 @@
#ifndef __SHMOB_DRM_H__
#define __SHMOB_DRM_H__
#include <drm/drm_mode.h>
#include <video/videomode.h>
enum shmob_drm_clk_source {
SHMOB_DRM_CLK_BUS,
......@@ -18,72 +18,21 @@ enum shmob_drm_clk_source {
SHMOB_DRM_CLK_EXTERNAL,
};
enum shmob_drm_interface {
SHMOB_DRM_IFACE_RGB8, /* 24bpp, 8:8:8 */
SHMOB_DRM_IFACE_RGB9, /* 18bpp, 9:9 */
SHMOB_DRM_IFACE_RGB12A, /* 24bpp, 12:12 */
SHMOB_DRM_IFACE_RGB12B, /* 12bpp */
SHMOB_DRM_IFACE_RGB16, /* 16bpp */
SHMOB_DRM_IFACE_RGB18, /* 18bpp */
SHMOB_DRM_IFACE_RGB24, /* 24bpp */
SHMOB_DRM_IFACE_YUV422, /* 16bpp */
SHMOB_DRM_IFACE_SYS8A, /* 24bpp, 8:8:8 */
SHMOB_DRM_IFACE_SYS8B, /* 18bpp, 8:8:2 */
SHMOB_DRM_IFACE_SYS8C, /* 18bpp, 2:8:8 */
SHMOB_DRM_IFACE_SYS8D, /* 16bpp, 8:8 */
SHMOB_DRM_IFACE_SYS9, /* 18bpp, 9:9 */
SHMOB_DRM_IFACE_SYS12, /* 24bpp, 12:12 */
SHMOB_DRM_IFACE_SYS16A, /* 16bpp */
SHMOB_DRM_IFACE_SYS16B, /* 18bpp, 16:2 */
SHMOB_DRM_IFACE_SYS16C, /* 18bpp, 2:16 */
SHMOB_DRM_IFACE_SYS18, /* 18bpp */
SHMOB_DRM_IFACE_SYS24, /* 24bpp */
};
struct shmob_drm_backlight_data {
const char *name;
int max_brightness;
int (*get_brightness)(void);
int (*set_brightness)(int brightness);
};
struct shmob_drm_panel_data {
unsigned int width_mm; /* Panel width in mm */
unsigned int height_mm; /* Panel height in mm */
struct drm_mode_modeinfo mode;
struct videomode mode;
};
struct shmob_drm_sys_interface_data {
unsigned int read_latch:6;
unsigned int read_setup:8;
unsigned int read_cycle:8;
unsigned int read_strobe:8;
unsigned int write_setup:8;
unsigned int write_cycle:8;
unsigned int write_strobe:8;
unsigned int cs_setup:3;
unsigned int vsync_active_high:1;
unsigned int vsync_dir_input:1;
};
#define SHMOB_DRM_IFACE_FL_DWPOL (1 << 0) /* Rising edge dot clock data latch */
#define SHMOB_DRM_IFACE_FL_DIPOL (1 << 1) /* Active low display enable */
#define SHMOB_DRM_IFACE_FL_DAPOL (1 << 2) /* Active low display data */
#define SHMOB_DRM_IFACE_FL_HSCNT (1 << 3) /* Disable HSYNC during VBLANK */
#define SHMOB_DRM_IFACE_FL_DWCNT (1 << 4) /* Disable dotclock during blanking */
struct shmob_drm_interface_data {
enum shmob_drm_interface interface;
struct shmob_drm_sys_interface_data sys;
unsigned int bus_fmt; /* MEDIA_BUS_FMT_* */
unsigned int clk_div;
unsigned int flags;
};
struct shmob_drm_platform_data {
enum shmob_drm_clk_source clk_source;
struct shmob_drm_interface_data iface;
struct shmob_drm_panel_data panel;
struct shmob_drm_backlight_data backlight;
};
#endif /* __SHMOB_DRM_H__ */
......@@ -34,7 +34,7 @@
#define MEDIA_BUS_FMT_FIXED 0x0001
/* RGB - next is 0x1025 */
/* RGB - next is 0x1026 */
#define MEDIA_BUS_FMT_RGB444_1X12 0x1016
#define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001
#define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002
......@@ -46,6 +46,7 @@
#define MEDIA_BUS_FMT_RGB565_2X8_BE 0x1007
#define MEDIA_BUS_FMT_RGB565_2X8_LE 0x1008
#define MEDIA_BUS_FMT_RGB666_1X18 0x1009
#define MEDIA_BUS_FMT_RGB666_2X9_BE 0x1025
#define MEDIA_BUS_FMT_BGR666_1X18 0x1023
#define MEDIA_BUS_FMT_RBG888_1X24 0x100e
#define MEDIA_BUS_FMT_RGB666_1X24_CPADHI 0x1015
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment