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: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Link: https://lore.kernel.org/r/cover.1694767208.git.geert+renesas@glider.be/ -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQQ9qaHoIs/1I4cXmEiKwlD9ZEnxcAUCZS0HuQAKCRCKwlD9ZEnx cI+KAP9PThUJqV7z1YxVeM/qYWYhqR4wezD18QCanCguAIvx9wD9F2ccIsFOso35 iIv23N4D6gWJllaA2WBeKye3zYEjDQI= =RSnM -----END PGP SIGNATURE----- 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: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Link: https://lore.kernel.org/r/cover.1694767208.git.geert+renesas@glider.be/ Signed-off-by: Dave Airlie <airlied@redhat.com> From: Geert Uytterhoeven <geert@linux-m68k.org> Link: https://patchwork.freedesktop.org/patch/msgid/CAMuHMdUF61V5qNyKbrTGxZfEJvCVuLO7q2R5MqZYkzRC_cNr0w@mail.gmail.compull/318/merge
commit
2ba446f821
|
|
@ -0,0 +1,130 @@
|
|||
# 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
|
||||
|
|
|
|||
13
MAINTAINERS
13
MAINTAINERS
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
# 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.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
# 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
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
// 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);
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
/* 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__ */
|
||||
|
|
@ -7,9 +7,18 @@
|
|||
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/media-bus-format.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_atomic_state_helper.h>
|
||||
#include <drm/drm_atomic_uapi.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_bridge_connector.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_dma_helper.h>
|
||||
|
|
@ -18,85 +27,123 @@
|
|||
#include <drm/drm_gem_dma_helper.h>
|
||||
#include <drm/drm_modeset_helper.h>
|
||||
#include <drm/drm_modeset_helper_vtables.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#include "shmob_drm_backlight.h"
|
||||
#include <video/videomode.h>
|
||||
|
||||
#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"
|
||||
|
||||
/*
|
||||
* TODO: panel support
|
||||
*/
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Clock management
|
||||
* Page Flip
|
||||
*/
|
||||
|
||||
static int shmob_drm_clk_on(struct shmob_drm_device *sdev)
|
||||
void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc)
|
||||
{
|
||||
int ret;
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct drm_device *dev = scrtc->base.dev;
|
||||
unsigned long flags;
|
||||
|
||||
if (sdev->clock) {
|
||||
ret = clk_prepare_enable(sdev->clock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
event = scrtc->event;
|
||||
scrtc->event = NULL;
|
||||
if (event) {
|
||||
drm_crtc_send_vblank_event(&scrtc->base, event);
|
||||
wake_up(&scrtc->flip_wait);
|
||||
drm_crtc_vblank_put(&scrtc->base);
|
||||
}
|
||||
|
||||
return 0;
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
static void shmob_drm_clk_off(struct shmob_drm_device *sdev)
|
||||
static bool shmob_drm_crtc_page_flip_pending(struct shmob_drm_crtc *scrtc)
|
||||
{
|
||||
if (sdev->clock)
|
||||
clk_disable_unprepare(sdev->clock);
|
||||
struct drm_device *dev = scrtc->base.dev;
|
||||
unsigned long flags;
|
||||
bool pending;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
pending = scrtc->event != NULL;
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
static void shmob_drm_crtc_wait_page_flip(struct shmob_drm_crtc *scrtc)
|
||||
{
|
||||
struct drm_crtc *crtc = &scrtc->base;
|
||||
struct shmob_drm_device *sdev = to_shmob_device(crtc->dev);
|
||||
|
||||
if (wait_event_timeout(scrtc->flip_wait,
|
||||
!shmob_drm_crtc_page_flip_pending(scrtc),
|
||||
msecs_to_jiffies(50)))
|
||||
return;
|
||||
|
||||
dev_warn(sdev->dev, "page flip timeout\n");
|
||||
|
||||
shmob_drm_crtc_finish_page_flip(scrtc);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* CRTC
|
||||
*/
|
||||
|
||||
static const struct {
|
||||
u32 fmt;
|
||||
u32 ldmt1r;
|
||||
} shmob_drm_bus_fmts[] = {
|
||||
{ MEDIA_BUS_FMT_RGB888_3X8, LDMT1R_MIFTYP_RGB8 },
|
||||
{ MEDIA_BUS_FMT_RGB666_2X9_BE, LDMT1R_MIFTYP_RGB9 },
|
||||
{ MEDIA_BUS_FMT_RGB888_2X12_BE, LDMT1R_MIFTYP_RGB12A },
|
||||
{ MEDIA_BUS_FMT_RGB444_1X12, LDMT1R_MIFTYP_RGB12B },
|
||||
{ MEDIA_BUS_FMT_RGB565_1X16, LDMT1R_MIFTYP_RGB16 },
|
||||
{ MEDIA_BUS_FMT_RGB666_1X18, LDMT1R_MIFTYP_RGB18 },
|
||||
{ MEDIA_BUS_FMT_RGB888_1X24, LDMT1R_MIFTYP_RGB24 },
|
||||
{ MEDIA_BUS_FMT_UYVY8_1X16, LDMT1R_MIFTYP_YCBCR },
|
||||
};
|
||||
|
||||
static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc)
|
||||
{
|
||||
struct drm_crtc *crtc = &scrtc->crtc;
|
||||
struct shmob_drm_device *sdev = crtc->dev->dev_private;
|
||||
const struct shmob_drm_interface_data *idata = &sdev->pdata->iface;
|
||||
struct drm_crtc *crtc = &scrtc->base;
|
||||
struct shmob_drm_device *sdev = to_shmob_device(crtc->dev);
|
||||
const struct drm_display_info *info = &sdev->connector->display_info;
|
||||
const struct drm_display_mode *mode = &crtc->mode;
|
||||
unsigned int i;
|
||||
u32 value;
|
||||
|
||||
value = sdev->ldmt1r
|
||||
| ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : LDMT1R_VPOL)
|
||||
| ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : LDMT1R_HPOL)
|
||||
| ((idata->flags & SHMOB_DRM_IFACE_FL_DWPOL) ? LDMT1R_DWPOL : 0)
|
||||
| ((idata->flags & SHMOB_DRM_IFACE_FL_DIPOL) ? LDMT1R_DIPOL : 0)
|
||||
| ((idata->flags & SHMOB_DRM_IFACE_FL_DAPOL) ? LDMT1R_DAPOL : 0)
|
||||
| ((idata->flags & SHMOB_DRM_IFACE_FL_HSCNT) ? LDMT1R_HSCNT : 0)
|
||||
| ((idata->flags & SHMOB_DRM_IFACE_FL_DWCNT) ? LDMT1R_DWCNT : 0);
|
||||
lcdc_write(sdev, LDMT1R, value);
|
||||
|
||||
if (idata->interface >= SHMOB_DRM_IFACE_SYS8A &&
|
||||
idata->interface <= SHMOB_DRM_IFACE_SYS24) {
|
||||
/* Setup SYS bus. */
|
||||
value = (idata->sys.cs_setup << LDMT2R_CSUP_SHIFT)
|
||||
| (idata->sys.vsync_active_high ? LDMT2R_RSV : 0)
|
||||
| (idata->sys.vsync_dir_input ? LDMT2R_VSEL : 0)
|
||||
| (idata->sys.write_setup << LDMT2R_WCSC_SHIFT)
|
||||
| (idata->sys.write_cycle << LDMT2R_WCEC_SHIFT)
|
||||
| (idata->sys.write_strobe << LDMT2R_WCLW_SHIFT);
|
||||
lcdc_write(sdev, LDMT2R, value);
|
||||
|
||||
value = (idata->sys.read_latch << LDMT3R_RDLC_SHIFT)
|
||||
| (idata->sys.read_setup << LDMT3R_RCSC_SHIFT)
|
||||
| (idata->sys.read_cycle << LDMT3R_RCEC_SHIFT)
|
||||
| (idata->sys.read_strobe << LDMT3R_RCLW_SHIFT);
|
||||
lcdc_write(sdev, LDMT3R, value);
|
||||
if (!info->num_bus_formats || !info->bus_formats) {
|
||||
dev_warn(sdev->dev, "No bus format reported, using RGB888\n");
|
||||
value = LDMT1R_MIFTYP_RGB24;
|
||||
} else {
|
||||
for (i = 0; i < ARRAY_SIZE(shmob_drm_bus_fmts); i++) {
|
||||
if (shmob_drm_bus_fmts[i].fmt == info->bus_formats[0])
|
||||
break;
|
||||
}
|
||||
if (i < ARRAY_SIZE(shmob_drm_bus_fmts)) {
|
||||
value = shmob_drm_bus_fmts[i].ldmt1r;
|
||||
} else {
|
||||
dev_warn(sdev->dev,
|
||||
"unsupported bus format 0x%x, using RGB888\n",
|
||||
info->bus_formats[0]);
|
||||
value = LDMT1R_MIFTYP_RGB24;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
|
||||
value |= LDMT1R_DWPOL;
|
||||
if (info->bus_flags & DRM_BUS_FLAG_DE_LOW)
|
||||
value |= LDMT1R_DIPOL;
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
value |= LDMT1R_VPOL;
|
||||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
value |= LDMT1R_HPOL;
|
||||
lcdc_write(sdev, LDMT1R, value);
|
||||
|
||||
value = ((mode->hdisplay / 8) << 16) /* HDCN */
|
||||
| (mode->htotal / 8); /* HTCN */
|
||||
lcdc_write(sdev, LDHCNR, value);
|
||||
|
|
@ -121,7 +168,7 @@ static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc)
|
|||
|
||||
static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start)
|
||||
{
|
||||
struct shmob_drm_device *sdev = scrtc->crtc.dev->dev_private;
|
||||
struct shmob_drm_device *sdev = to_shmob_device(scrtc->base.dev);
|
||||
u32 value;
|
||||
|
||||
value = lcdc_read(sdev, LDCNT2R);
|
||||
|
|
@ -145,34 +192,23 @@ static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* shmob_drm_crtc_start - Configure and start the LCDC
|
||||
* @scrtc: the SH Mobile CRTC
|
||||
*
|
||||
* Configure and start the LCDC device. External devices (clocks, MERAM, panels,
|
||||
* ...) are not touched by this function.
|
||||
*/
|
||||
static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc)
|
||||
static inline struct shmob_drm_crtc *to_shmob_crtc(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_crtc *crtc = &scrtc->crtc;
|
||||
struct shmob_drm_device *sdev = crtc->dev->dev_private;
|
||||
const struct shmob_drm_interface_data *idata = &sdev->pdata->iface;
|
||||
const struct shmob_drm_format_info *format;
|
||||
struct drm_device *dev = sdev->ddev;
|
||||
struct drm_plane *plane;
|
||||
return container_of(crtc, struct shmob_drm_crtc, base);
|
||||
}
|
||||
|
||||
static void shmob_drm_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
|
||||
struct shmob_drm_device *sdev = to_shmob_device(crtc->dev);
|
||||
unsigned int clk_div = sdev->config.clk_div;
|
||||
struct device *dev = sdev->dev;
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
if (scrtc->started)
|
||||
return;
|
||||
|
||||
format = shmob_drm_format_info(crtc->primary->fb->format->format);
|
||||
if (WARN_ON(format == NULL))
|
||||
return;
|
||||
|
||||
/* Enable clocks before accessing the hardware. */
|
||||
ret = shmob_drm_clk_on(sdev);
|
||||
if (ret < 0)
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
/* Reset and enable the LCDC. */
|
||||
|
|
@ -188,79 +224,50 @@ static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc)
|
|||
lcdc_write(sdev, LDPMR, 0);
|
||||
|
||||
value = sdev->lddckr;
|
||||
if (idata->clk_div) {
|
||||
if (clk_div) {
|
||||
/* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider
|
||||
* denominator.
|
||||
*/
|
||||
lcdc_write(sdev, LDDCKPAT1R, 0);
|
||||
lcdc_write(sdev, LDDCKPAT2R, (1 << (idata->clk_div / 2)) - 1);
|
||||
lcdc_write(sdev, LDDCKPAT2R, (1 << (clk_div / 2)) - 1);
|
||||
|
||||
if (idata->clk_div == 1)
|
||||
if (clk_div == 1)
|
||||
value |= LDDCKR_MOSEL;
|
||||
else
|
||||
value |= idata->clk_div;
|
||||
value |= clk_div;
|
||||
}
|
||||
|
||||
lcdc_write(sdev, LDDCKR, value);
|
||||
lcdc_write(sdev, LDDCKSTPR, 0);
|
||||
lcdc_wait_bit(sdev, LDDCKSTPR, ~0, 0);
|
||||
|
||||
/* TODO: Setup SYS panel */
|
||||
|
||||
/* Setup geometry, format, frame buffer memory and operation mode. */
|
||||
shmob_drm_crtc_setup_geometry(scrtc);
|
||||
|
||||
/* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */
|
||||
lcdc_write(sdev, LDDFR, format->lddfr | LDDFR_CF1);
|
||||
lcdc_write(sdev, LDMLSR, scrtc->line_size);
|
||||
lcdc_write(sdev, LDSA1R, scrtc->dma[0]);
|
||||
if (format->yuv)
|
||||
lcdc_write(sdev, LDSA2R, scrtc->dma[1]);
|
||||
lcdc_write(sdev, LDSM1R, 0);
|
||||
|
||||
/* Word and long word swap. */
|
||||
switch (format->fourcc) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
case DRM_FORMAT_NV21:
|
||||
case DRM_FORMAT_NV61:
|
||||
case DRM_FORMAT_NV42:
|
||||
value = LDDDSR_LS | LDDDSR_WS;
|
||||
break;
|
||||
case DRM_FORMAT_RGB888:
|
||||
case DRM_FORMAT_NV12:
|
||||
case DRM_FORMAT_NV16:
|
||||
case DRM_FORMAT_NV24:
|
||||
value = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
default:
|
||||
value = LDDDSR_LS;
|
||||
break;
|
||||
}
|
||||
lcdc_write(sdev, LDDDSR, value);
|
||||
|
||||
/* Setup planes. */
|
||||
drm_for_each_legacy_plane(plane, dev) {
|
||||
if (plane->crtc == crtc)
|
||||
shmob_drm_plane_setup(plane);
|
||||
}
|
||||
|
||||
/* Enable the display output. */
|
||||
lcdc_write(sdev, LDCNT1R, LDCNT1R_DE);
|
||||
|
||||
shmob_drm_crtc_start_stop(scrtc, true);
|
||||
|
||||
scrtc->started = true;
|
||||
/* Turn vertical blank interrupt reporting back on. */
|
||||
drm_crtc_vblank_on(crtc);
|
||||
}
|
||||
|
||||
static void shmob_drm_crtc_stop(struct shmob_drm_crtc *scrtc)
|
||||
static void shmob_drm_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_crtc *crtc = &scrtc->crtc;
|
||||
struct shmob_drm_device *sdev = crtc->dev->dev_private;
|
||||
struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
|
||||
struct shmob_drm_device *sdev = to_shmob_device(crtc->dev);
|
||||
|
||||
if (!scrtc->started)
|
||||
return;
|
||||
/*
|
||||
* Disable vertical blank interrupt reporting. We first need to wait
|
||||
* for page flip completion before stopping the CRTC as userspace
|
||||
* expects page flips to eventually complete.
|
||||
*/
|
||||
shmob_drm_crtc_wait_page_flip(scrtc);
|
||||
drm_crtc_vblank_off(crtc);
|
||||
|
||||
/* Stop the LCDC. */
|
||||
shmob_drm_crtc_start_stop(scrtc, false);
|
||||
|
|
@ -268,145 +275,31 @@ static void shmob_drm_crtc_stop(struct shmob_drm_crtc *scrtc)
|
|||
/* Disable the display output. */
|
||||
lcdc_write(sdev, LDCNT1R, 0);
|
||||
|
||||
/* Stop clocks. */
|
||||
shmob_drm_clk_off(sdev);
|
||||
|
||||
scrtc->started = false;
|
||||
pm_runtime_put(sdev->dev);
|
||||
}
|
||||
|
||||
void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc)
|
||||
static void shmob_drm_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
shmob_drm_crtc_stop(scrtc);
|
||||
}
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct drm_device *dev = crtc->dev;
|
||||
unsigned long flags;
|
||||
|
||||
void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc)
|
||||
{
|
||||
if (scrtc->dpms != DRM_MODE_DPMS_ON)
|
||||
return;
|
||||
|
||||
shmob_drm_crtc_start(scrtc);
|
||||
}
|
||||
|
||||
static void shmob_drm_crtc_compute_base(struct shmob_drm_crtc *scrtc,
|
||||
int x, int y)
|
||||
{
|
||||
struct drm_crtc *crtc = &scrtc->crtc;
|
||||
struct drm_framebuffer *fb = crtc->primary->fb;
|
||||
struct drm_gem_dma_object *gem;
|
||||
unsigned int bpp;
|
||||
|
||||
bpp = scrtc->format->yuv ? 8 : scrtc->format->bpp;
|
||||
gem = drm_fb_dma_get_gem_obj(fb, 0);
|
||||
scrtc->dma[0] = gem->dma_addr + fb->offsets[0]
|
||||
+ y * fb->pitches[0] + x * bpp / 8;
|
||||
|
||||
if (scrtc->format->yuv) {
|
||||
bpp = scrtc->format->bpp - 8;
|
||||
gem = drm_fb_dma_get_gem_obj(fb, 1);
|
||||
scrtc->dma[1] = gem->dma_addr + fb->offsets[1]
|
||||
+ y / (bpp == 4 ? 2 : 1) * fb->pitches[1]
|
||||
+ x * (bpp == 16 ? 2 : 1);
|
||||
if (crtc->state->event) {
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
event = crtc->state->event;
|
||||
crtc->state->event = NULL;
|
||||
drm_crtc_send_vblank_event(crtc, event);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void shmob_drm_crtc_update_base(struct shmob_drm_crtc *scrtc)
|
||||
{
|
||||
struct drm_crtc *crtc = &scrtc->crtc;
|
||||
struct shmob_drm_device *sdev = crtc->dev->dev_private;
|
||||
|
||||
shmob_drm_crtc_compute_base(scrtc, crtc->x, crtc->y);
|
||||
|
||||
lcdc_write_mirror(sdev, LDSA1R, scrtc->dma[0]);
|
||||
if (scrtc->format->yuv)
|
||||
lcdc_write_mirror(sdev, LDSA2R, scrtc->dma[1]);
|
||||
|
||||
lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
|
||||
}
|
||||
|
||||
#define to_shmob_crtc(c) container_of(c, struct shmob_drm_crtc, crtc)
|
||||
|
||||
static void shmob_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
|
||||
{
|
||||
struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
|
||||
|
||||
if (scrtc->dpms == mode)
|
||||
return;
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
shmob_drm_crtc_start(scrtc);
|
||||
else
|
||||
shmob_drm_crtc_stop(scrtc);
|
||||
|
||||
scrtc->dpms = mode;
|
||||
}
|
||||
|
||||
static void shmob_drm_crtc_mode_prepare(struct drm_crtc *crtc)
|
||||
{
|
||||
shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
|
||||
static int shmob_drm_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 shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
|
||||
struct shmob_drm_device *sdev = crtc->dev->dev_private;
|
||||
const struct shmob_drm_format_info *format;
|
||||
|
||||
format = shmob_drm_format_info(crtc->primary->fb->format->format);
|
||||
if (format == NULL) {
|
||||
dev_dbg(sdev->dev, "mode_set: unsupported format %p4cc\n",
|
||||
&crtc->primary->fb->format->format);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
scrtc->format = format;
|
||||
scrtc->line_size = crtc->primary->fb->pitches[0];
|
||||
|
||||
shmob_drm_crtc_compute_base(scrtc, x, y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void shmob_drm_crtc_mode_commit(struct drm_crtc *crtc)
|
||||
{
|
||||
shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
|
||||
}
|
||||
|
||||
static int shmob_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
||||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
shmob_drm_crtc_update_base(to_shmob_crtc(crtc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
|
||||
.dpms = shmob_drm_crtc_dpms,
|
||||
.prepare = shmob_drm_crtc_mode_prepare,
|
||||
.commit = shmob_drm_crtc_mode_commit,
|
||||
.mode_set = shmob_drm_crtc_mode_set,
|
||||
.mode_set_base = shmob_drm_crtc_mode_set_base,
|
||||
.atomic_flush = shmob_drm_crtc_atomic_flush,
|
||||
.atomic_enable = shmob_drm_crtc_atomic_enable,
|
||||
.atomic_disable = shmob_drm_crtc_atomic_disable,
|
||||
};
|
||||
|
||||
void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc)
|
||||
{
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct drm_device *dev = scrtc->crtc.dev;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
event = scrtc->event;
|
||||
scrtc->event = NULL;
|
||||
if (event) {
|
||||
drm_crtc_send_vblank_event(&scrtc->crtc, event);
|
||||
drm_crtc_vblank_put(&scrtc->crtc);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *fb,
|
||||
struct drm_pending_vblank_event *event,
|
||||
|
|
@ -414,7 +307,7 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
|
|||
struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
|
||||
struct drm_device *dev = scrtc->crtc.dev;
|
||||
struct drm_device *dev = scrtc->base.dev;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
|
@ -424,12 +317,11 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
|
|||
}
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
crtc->primary->fb = fb;
|
||||
shmob_drm_crtc_update_base(scrtc);
|
||||
drm_atomic_set_fb_for_plane(crtc->primary->state, fb);
|
||||
|
||||
if (event) {
|
||||
event->pipe = 0;
|
||||
drm_crtc_vblank_get(&scrtc->crtc);
|
||||
drm_crtc_vblank_get(&scrtc->base);
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
scrtc->event = event;
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
|
@ -457,7 +349,7 @@ static void shmob_drm_crtc_enable_vblank(struct shmob_drm_device *sdev,
|
|||
|
||||
static int shmob_drm_enable_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
struct shmob_drm_device *sdev = crtc->dev->dev_private;
|
||||
struct shmob_drm_device *sdev = to_shmob_device(crtc->dev);
|
||||
|
||||
shmob_drm_crtc_enable_vblank(sdev, true);
|
||||
|
||||
|
|
@ -466,88 +358,65 @@ static int shmob_drm_enable_vblank(struct drm_crtc *crtc)
|
|||
|
||||
static void shmob_drm_disable_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
struct shmob_drm_device *sdev = crtc->dev->dev_private;
|
||||
struct shmob_drm_device *sdev = to_shmob_device(crtc->dev);
|
||||
|
||||
shmob_drm_crtc_enable_vblank(sdev, false);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs crtc_funcs = {
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.set_config = drm_crtc_helper_set_config,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = shmob_drm_crtc_page_flip,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
.enable_vblank = shmob_drm_enable_vblank,
|
||||
.disable_vblank = shmob_drm_disable_vblank,
|
||||
};
|
||||
|
||||
static const uint32_t modeset_formats[] = {
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_RGB888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs primary_plane_funcs = {
|
||||
DRM_PLANE_NON_ATOMIC_FUNCS,
|
||||
};
|
||||
|
||||
int shmob_drm_crtc_create(struct shmob_drm_device *sdev)
|
||||
{
|
||||
struct drm_crtc *crtc = &sdev->crtc.crtc;
|
||||
struct drm_plane *primary;
|
||||
struct drm_crtc *crtc = &sdev->crtc.base;
|
||||
struct drm_plane *primary, *plane;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
sdev->crtc.dpms = DRM_MODE_DPMS_OFF;
|
||||
init_waitqueue_head(&sdev->crtc.flip_wait);
|
||||
|
||||
primary = __drm_universal_plane_alloc(sdev->ddev, sizeof(*primary), 0,
|
||||
0, &primary_plane_funcs,
|
||||
modeset_formats,
|
||||
ARRAY_SIZE(modeset_formats),
|
||||
NULL, DRM_PLANE_TYPE_PRIMARY,
|
||||
NULL);
|
||||
primary = shmob_drm_plane_create(sdev, DRM_PLANE_TYPE_PRIMARY, 0);
|
||||
if (IS_ERR(primary))
|
||||
return PTR_ERR(primary);
|
||||
|
||||
ret = drm_crtc_init_with_planes(sdev->ddev, crtc, primary, NULL,
|
||||
&crtc_funcs, NULL);
|
||||
if (ret < 0) {
|
||||
drm_plane_cleanup(primary);
|
||||
kfree(primary);
|
||||
return ret;
|
||||
for (i = 1; i < 5; ++i) {
|
||||
plane = shmob_drm_plane_create(sdev, DRM_PLANE_TYPE_OVERLAY, i);
|
||||
if (IS_ERR(plane))
|
||||
return PTR_ERR(plane);
|
||||
}
|
||||
|
||||
ret = drm_crtc_init_with_planes(&sdev->ddev, crtc, primary, NULL,
|
||||
&crtc_funcs, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drm_crtc_helper_add(crtc, &crtc_helper_funcs);
|
||||
|
||||
/* Start with vertical blank interrupt reporting disabled. */
|
||||
drm_crtc_vblank_off(crtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Encoder
|
||||
* Legacy Encoder
|
||||
*/
|
||||
|
||||
#define to_shmob_encoder(e) \
|
||||
container_of(e, struct shmob_drm_encoder, encoder)
|
||||
|
||||
static void shmob_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct shmob_drm_encoder *senc = to_shmob_encoder(encoder);
|
||||
struct shmob_drm_device *sdev = encoder->dev->dev_private;
|
||||
struct shmob_drm_connector *scon = &sdev->connector;
|
||||
|
||||
if (senc->dpms == mode)
|
||||
return;
|
||||
|
||||
shmob_drm_backlight_dpms(scon, mode);
|
||||
|
||||
senc->dpms = mode;
|
||||
}
|
||||
|
||||
static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct shmob_drm_device *sdev = dev->dev_private;
|
||||
struct drm_connector *connector = &sdev->connector.connector;
|
||||
struct shmob_drm_device *sdev = to_shmob_device(dev);
|
||||
struct drm_connector *connector = sdev->connector;
|
||||
const struct drm_display_mode *panel_mode;
|
||||
|
||||
if (list_empty(&connector->modes)) {
|
||||
|
|
@ -563,60 +432,61 @@ static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder,
|
|||
return true;
|
||||
}
|
||||
|
||||
static void shmob_drm_encoder_mode_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
/* No-op, everything is handled in the CRTC code. */
|
||||
}
|
||||
|
||||
static void shmob_drm_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
/* No-op, everything is handled in the CRTC code. */
|
||||
}
|
||||
|
||||
static void shmob_drm_encoder_mode_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
/* No-op, everything is handled in the CRTC code. */
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
|
||||
.dpms = shmob_drm_encoder_dpms,
|
||||
.mode_fixup = shmob_drm_encoder_mode_fixup,
|
||||
.prepare = shmob_drm_encoder_mode_prepare,
|
||||
.commit = shmob_drm_encoder_mode_commit,
|
||||
.mode_set = shmob_drm_encoder_mode_set,
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Encoder
|
||||
*/
|
||||
|
||||
int shmob_drm_encoder_create(struct shmob_drm_device *sdev)
|
||||
{
|
||||
struct drm_encoder *encoder = &sdev->encoder.encoder;
|
||||
struct drm_encoder *encoder = &sdev->encoder;
|
||||
struct drm_bridge *bridge;
|
||||
int ret;
|
||||
|
||||
sdev->encoder.dpms = DRM_MODE_DPMS_OFF;
|
||||
|
||||
encoder->possible_crtcs = 1;
|
||||
|
||||
ret = drm_simple_encoder_init(sdev->ddev, encoder,
|
||||
DRM_MODE_ENCODER_LVDS);
|
||||
ret = drm_simple_encoder_init(&sdev->ddev, encoder,
|
||||
DRM_MODE_ENCODER_DPI);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drm_encoder_helper_add(encoder, &encoder_helper_funcs);
|
||||
if (sdev->pdata) {
|
||||
drm_encoder_helper_add(encoder, &encoder_helper_funcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a panel bridge */
|
||||
bridge = devm_drm_of_get_bridge(sdev->dev, sdev->dev->of_node, 0, 0);
|
||||
if (IS_ERR(bridge))
|
||||
return PTR_ERR(bridge);
|
||||
|
||||
/* Attach the bridge to the encoder */
|
||||
ret = drm_bridge_attach(encoder, bridge, NULL,
|
||||
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
if (ret) {
|
||||
dev_err(sdev->dev, "failed to attach bridge: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Connector
|
||||
* Legacy Connector
|
||||
*/
|
||||
|
||||
#define to_shmob_connector(c) \
|
||||
container_of(c, struct shmob_drm_connector, connector)
|
||||
static inline struct shmob_drm_connector *to_shmob_connector(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct shmob_drm_connector, base);
|
||||
}
|
||||
|
||||
static int shmob_drm_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct shmob_drm_device *sdev = connector->dev->dev_private;
|
||||
struct shmob_drm_connector *scon = to_shmob_connector(connector);
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_create(connector->dev);
|
||||
|
|
@ -624,23 +494,11 @@ static int shmob_drm_connector_get_modes(struct drm_connector *connector)
|
|||
return 0;
|
||||
|
||||
mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
|
||||
mode->clock = sdev->pdata->panel.mode.clock;
|
||||
mode->hdisplay = sdev->pdata->panel.mode.hdisplay;
|
||||
mode->hsync_start = sdev->pdata->panel.mode.hsync_start;
|
||||
mode->hsync_end = sdev->pdata->panel.mode.hsync_end;
|
||||
mode->htotal = sdev->pdata->panel.mode.htotal;
|
||||
mode->vdisplay = sdev->pdata->panel.mode.vdisplay;
|
||||
mode->vsync_start = sdev->pdata->panel.mode.vsync_start;
|
||||
mode->vsync_end = sdev->pdata->panel.mode.vsync_end;
|
||||
mode->vtotal = sdev->pdata->panel.mode.vtotal;
|
||||
mode->flags = sdev->pdata->panel.mode.flags;
|
||||
|
||||
drm_mode_set_name(mode);
|
||||
drm_display_mode_from_videomode(scon->mode, mode);
|
||||
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
connector->display_info.width_mm = sdev->pdata->panel.width_mm;
|
||||
connector->display_info.height_mm = sdev->pdata->panel.height_mm;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -659,54 +517,106 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = {
|
|||
|
||||
static void shmob_drm_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct shmob_drm_connector *scon = to_shmob_connector(connector);
|
||||
|
||||
shmob_drm_backlight_exit(scon);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
|
||||
kfree(connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = shmob_drm_connector_destroy,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static struct drm_connector *
|
||||
shmob_drm_connector_init(struct shmob_drm_device *sdev,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
u32 bus_fmt = sdev->pdata->iface.bus_fmt;
|
||||
struct shmob_drm_connector *scon;
|
||||
struct drm_connector *connector;
|
||||
struct drm_display_info *info;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(shmob_drm_bus_fmts); i++) {
|
||||
if (shmob_drm_bus_fmts[i].fmt == bus_fmt)
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(shmob_drm_bus_fmts)) {
|
||||
dev_err(sdev->dev, "unsupported bus format 0x%x\n", bus_fmt);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
scon = kzalloc(sizeof(*scon), GFP_KERNEL);
|
||||
if (!scon)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
connector = &scon->base;
|
||||
scon->encoder = encoder;
|
||||
scon->mode = &sdev->pdata->panel.mode;
|
||||
|
||||
info = &connector->display_info;
|
||||
info->width_mm = sdev->pdata->panel.width_mm;
|
||||
info->height_mm = sdev->pdata->panel.height_mm;
|
||||
|
||||
if (scon->mode->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
|
||||
info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
|
||||
if (scon->mode->flags & DISPLAY_FLAGS_DE_LOW)
|
||||
info->bus_flags |= DRM_BUS_FLAG_DE_LOW;
|
||||
|
||||
ret = drm_display_info_set_bus_formats(info, &bus_fmt, 1);
|
||||
if (ret < 0) {
|
||||
kfree(scon);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = drm_connector_init(&sdev->ddev, connector, &connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DPI);
|
||||
if (ret < 0) {
|
||||
kfree(scon);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
drm_connector_helper_add(connector, &connector_helper_funcs);
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Connector
|
||||
*/
|
||||
|
||||
int shmob_drm_connector_create(struct shmob_drm_device *sdev,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_connector *connector = &sdev->connector.connector;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
|
||||
sdev->connector.encoder = encoder;
|
||||
|
||||
connector->display_info.width_mm = sdev->pdata->panel.width_mm;
|
||||
connector->display_info.height_mm = sdev->pdata->panel.height_mm;
|
||||
|
||||
ret = drm_connector_init(sdev->ddev, connector, &connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drm_connector_helper_add(connector, &connector_helper_funcs);
|
||||
|
||||
ret = shmob_drm_backlight_init(&sdev->connector);
|
||||
if (ret < 0)
|
||||
goto err_cleanup;
|
||||
if (sdev->pdata)
|
||||
connector = shmob_drm_connector_init(sdev, encoder);
|
||||
else
|
||||
connector = drm_bridge_connector_init(&sdev->ddev, encoder);
|
||||
if (IS_ERR(connector)) {
|
||||
dev_err(sdev->dev, "failed to created connector: %pe\n",
|
||||
connector);
|
||||
return PTR_ERR(connector);
|
||||
}
|
||||
|
||||
ret = drm_connector_attach_encoder(connector, encoder);
|
||||
if (ret < 0)
|
||||
goto err_backlight;
|
||||
goto error;
|
||||
|
||||
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
|
||||
drm_object_property_set_value(&connector->base,
|
||||
sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
|
||||
connector->dpms = DRM_MODE_DPMS_OFF;
|
||||
|
||||
sdev->connector = connector;
|
||||
|
||||
return 0;
|
||||
|
||||
err_backlight:
|
||||
shmob_drm_backlight_exit(&sdev->connector);
|
||||
err_cleanup:
|
||||
error:
|
||||
drm_connector_cleanup(connector);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 0;
|
||||
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);
|
||||
|
||||
drm_modeset_lock_all(sdev->ddev);
|
||||
shmob_drm_crtc_resume(&sdev->crtc);
|
||||
drm_modeset_unlock_all(sdev->ddev);
|
||||
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);
|
||||
|
||||
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 int shmob_drm_pm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct shmob_drm_device *sdev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (sdev->clock) {
|
||||
ret = clk_prepare_enable(sdev->clock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
drm_kms_helper_poll_init(sdev->ddev);
|
||||
ret = shmob_drm_encoder_create(sdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
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;
|
||||
ret = shmob_drm_connector_create(sdev, &sdev->encoder);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drm_helper_disable_unused_functions(sdev->ddev);
|
||||
drm_mode_config_reset(dev);
|
||||
|
||||
drm_kms_helper_poll_init(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;
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -7,11 +7,14 @@
|
|||
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*/
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_fb_dma_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_gem_dma_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
|
||||
#include "shmob_drm_drv.h"
|
||||
#include "shmob_drm_kms.h"
|
||||
|
|
@ -19,102 +22,84 @@
|
|||
#include "shmob_drm_regs.h"
|
||||
|
||||
struct shmob_drm_plane {
|
||||
struct drm_plane plane;
|
||||
struct drm_plane base;
|
||||
unsigned int index;
|
||||
unsigned int alpha;
|
||||
|
||||
const struct shmob_drm_format_info *format;
|
||||
unsigned long dma[2];
|
||||
|
||||
unsigned int src_x;
|
||||
unsigned int src_y;
|
||||
unsigned int crtc_x;
|
||||
unsigned int crtc_y;
|
||||
unsigned int crtc_w;
|
||||
unsigned int crtc_h;
|
||||
};
|
||||
|
||||
#define to_shmob_plane(p) container_of(p, struct shmob_drm_plane, plane)
|
||||
struct shmob_drm_plane_state {
|
||||
struct drm_plane_state base;
|
||||
|
||||
static void shmob_drm_plane_compute_base(struct shmob_drm_plane *splane,
|
||||
struct drm_framebuffer *fb,
|
||||
int x, int y)
|
||||
const struct shmob_drm_format_info *format;
|
||||
u32 dma[2];
|
||||
};
|
||||
|
||||
static inline struct shmob_drm_plane *to_shmob_plane(struct drm_plane *plane)
|
||||
{
|
||||
return container_of(plane, struct shmob_drm_plane, base);
|
||||
}
|
||||
|
||||
static inline struct shmob_drm_plane_state *to_shmob_plane_state(struct drm_plane_state *state)
|
||||
{
|
||||
return container_of(state, struct shmob_drm_plane_state, base);
|
||||
}
|
||||
|
||||
static void shmob_drm_plane_compute_base(struct shmob_drm_plane_state *sstate)
|
||||
{
|
||||
struct drm_framebuffer *fb = sstate->base.fb;
|
||||
unsigned int x = sstate->base.src_x >> 16;
|
||||
unsigned int y = sstate->base.src_y >> 16;
|
||||
struct drm_gem_dma_object *gem;
|
||||
unsigned int bpp;
|
||||
|
||||
bpp = splane->format->yuv ? 8 : splane->format->bpp;
|
||||
bpp = shmob_drm_format_is_yuv(sstate->format) ? 8 : sstate->format->bpp;
|
||||
gem = drm_fb_dma_get_gem_obj(fb, 0);
|
||||
splane->dma[0] = gem->dma_addr + fb->offsets[0]
|
||||
sstate->dma[0] = gem->dma_addr + fb->offsets[0]
|
||||
+ y * fb->pitches[0] + x * bpp / 8;
|
||||
|
||||
if (splane->format->yuv) {
|
||||
bpp = splane->format->bpp - 8;
|
||||
if (shmob_drm_format_is_yuv(sstate->format)) {
|
||||
bpp = sstate->format->bpp - 8;
|
||||
gem = drm_fb_dma_get_gem_obj(fb, 1);
|
||||
splane->dma[1] = gem->dma_addr + fb->offsets[1]
|
||||
sstate->dma[1] = gem->dma_addr + fb->offsets[1]
|
||||
+ y / (bpp == 4 ? 2 : 1) * fb->pitches[1]
|
||||
+ x * (bpp == 16 ? 2 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane,
|
||||
struct drm_framebuffer *fb)
|
||||
static void shmob_drm_primary_plane_setup(struct shmob_drm_plane *splane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct shmob_drm_device *sdev = splane->plane.dev->dev_private;
|
||||
struct shmob_drm_plane_state *sstate = to_shmob_plane_state(state);
|
||||
struct shmob_drm_device *sdev = to_shmob_device(splane->base.dev);
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
|
||||
/* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */
|
||||
lcdc_write(sdev, LDDFR, sstate->format->lddfr | LDDFR_CF1);
|
||||
lcdc_write(sdev, LDMLSR, fb->pitches[0]);
|
||||
|
||||
/* Word and long word swap. */
|
||||
lcdc_write(sdev, LDDDSR, sstate->format->ldddsr);
|
||||
|
||||
lcdc_write_mirror(sdev, LDSA1R, sstate->dma[0]);
|
||||
if (shmob_drm_format_is_yuv(sstate->format))
|
||||
lcdc_write_mirror(sdev, LDSA2R, sstate->dma[1]);
|
||||
|
||||
lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
|
||||
}
|
||||
|
||||
static void shmob_drm_overlay_plane_setup(struct shmob_drm_plane *splane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct shmob_drm_plane_state *sstate = to_shmob_plane_state(state);
|
||||
struct shmob_drm_device *sdev = to_shmob_device(splane->base.dev);
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
u32 format;
|
||||
|
||||
/* TODO: Support ROP3 mode */
|
||||
format = LDBBSIFR_EN | (splane->alpha << LDBBSIFR_LAY_SHIFT);
|
||||
|
||||
switch (splane->format->fourcc) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
case DRM_FORMAT_NV21:
|
||||
case DRM_FORMAT_NV61:
|
||||
case DRM_FORMAT_NV42:
|
||||
format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW;
|
||||
break;
|
||||
case DRM_FORMAT_RGB888:
|
||||
case DRM_FORMAT_NV12:
|
||||
case DRM_FORMAT_NV16:
|
||||
case DRM_FORMAT_NV24:
|
||||
format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
default:
|
||||
format |= LDBBSIFR_SWPL;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (splane->format->fourcc) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16;
|
||||
break;
|
||||
case DRM_FORMAT_RGB888:
|
||||
format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32;
|
||||
break;
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDDFR_PKF_ARGB32;
|
||||
break;
|
||||
case DRM_FORMAT_NV12:
|
||||
case DRM_FORMAT_NV21:
|
||||
format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420;
|
||||
break;
|
||||
case DRM_FORMAT_NV16:
|
||||
case DRM_FORMAT_NV61:
|
||||
format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422;
|
||||
break;
|
||||
case DRM_FORMAT_NV24:
|
||||
case DRM_FORMAT_NV42:
|
||||
format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444;
|
||||
break;
|
||||
}
|
||||
format = LDBBSIFR_EN | ((state->alpha >> 8) << LDBBSIFR_LAY_SHIFT) |
|
||||
sstate->format->ldbbsifr;
|
||||
|
||||
#define plane_reg_dump(sdev, splane, reg) \
|
||||
dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \
|
||||
dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \
|
||||
splane->index, #reg, \
|
||||
lcdc_read(sdev, reg(splane->index)), \
|
||||
lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET))
|
||||
|
|
@ -127,29 +112,27 @@ static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane,
|
|||
plane_reg_dump(sdev, splane, LDBnBSACR);
|
||||
|
||||
lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index));
|
||||
dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,
|
||||
dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,
|
||||
"LDBCR", lcdc_read(sdev, LDBCR));
|
||||
|
||||
lcdc_write(sdev, LDBnBSIFR(splane->index), format);
|
||||
|
||||
lcdc_write(sdev, LDBnBSSZR(splane->index),
|
||||
(splane->crtc_h << LDBBSSZR_BVSS_SHIFT) |
|
||||
(splane->crtc_w << LDBBSSZR_BHSS_SHIFT));
|
||||
(state->crtc_h << LDBBSSZR_BVSS_SHIFT) |
|
||||
(state->crtc_w << LDBBSSZR_BHSS_SHIFT));
|
||||
lcdc_write(sdev, LDBnBLOCR(splane->index),
|
||||
(splane->crtc_y << LDBBLOCR_CVLC_SHIFT) |
|
||||
(splane->crtc_x << LDBBLOCR_CHLC_SHIFT));
|
||||
(state->crtc_y << LDBBLOCR_CVLC_SHIFT) |
|
||||
(state->crtc_x << LDBBLOCR_CHLC_SHIFT));
|
||||
lcdc_write(sdev, LDBnBSMWR(splane->index),
|
||||
fb->pitches[0] << LDBBSMWR_BSMW_SHIFT);
|
||||
|
||||
shmob_drm_plane_compute_base(splane, fb, splane->src_x, splane->src_y);
|
||||
|
||||
lcdc_write(sdev, LDBnBSAYR(splane->index), splane->dma[0]);
|
||||
if (splane->format->yuv)
|
||||
lcdc_write(sdev, LDBnBSACR(splane->index), splane->dma[1]);
|
||||
lcdc_write(sdev, LDBnBSAYR(splane->index), sstate->dma[0]);
|
||||
if (shmob_drm_format_is_yuv(sstate->format))
|
||||
lcdc_write(sdev, LDBnBSACR(splane->index), sstate->dma[1]);
|
||||
|
||||
lcdc_write(sdev, LDBCR,
|
||||
LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index));
|
||||
dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,
|
||||
dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,
|
||||
"LDBCR", lcdc_read(sdev, LDBCR));
|
||||
|
||||
plane_reg_dump(sdev, splane, LDBnBSIFR);
|
||||
|
|
@ -160,75 +143,143 @@ static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane,
|
|||
plane_reg_dump(sdev, splane, LDBnBSACR);
|
||||
}
|
||||
|
||||
void shmob_drm_plane_setup(struct drm_plane *plane)
|
||||
static int shmob_drm_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
struct shmob_drm_plane_state *sstate = to_shmob_plane_state(new_plane_state);
|
||||
struct drm_crtc_state *crtc_state;
|
||||
bool is_primary = plane->type == DRM_PLANE_TYPE_PRIMARY;
|
||||
int ret;
|
||||
|
||||
if (!new_plane_state->crtc) {
|
||||
/*
|
||||
* The visible field is not reset by the DRM core but only
|
||||
* updated by drm_atomic_helper_check_plane_state(), set it
|
||||
* manually.
|
||||
*/
|
||||
new_plane_state->visible = false;
|
||||
sstate->format = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc);
|
||||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
|
||||
DRM_PLANE_NO_SCALING,
|
||||
DRM_PLANE_NO_SCALING,
|
||||
!is_primary, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!new_plane_state->visible) {
|
||||
sstate->format = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sstate->format = shmob_drm_format_info(new_plane_state->fb->format->format);
|
||||
if (!sstate->format) {
|
||||
dev_dbg(plane->dev->dev,
|
||||
"plane_atomic_check: unsupported format %p4cc\n",
|
||||
&new_plane_state->fb->format->format);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
shmob_drm_plane_compute_base(sstate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void shmob_drm_plane_atomic_update(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
struct shmob_drm_plane *splane = to_shmob_plane(plane);
|
||||
|
||||
if (plane->fb == NULL)
|
||||
if (!new_plane_state->visible)
|
||||
return;
|
||||
|
||||
__shmob_drm_plane_setup(splane, plane->fb);
|
||||
if (plane->type == DRM_PLANE_TYPE_PRIMARY)
|
||||
shmob_drm_primary_plane_setup(splane, new_plane_state);
|
||||
else
|
||||
shmob_drm_overlay_plane_setup(splane, new_plane_state);
|
||||
}
|
||||
|
||||
static int
|
||||
shmob_drm_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 drm_modeset_acquire_ctx *ctx)
|
||||
static void shmob_drm_plane_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
|
||||
struct shmob_drm_device *sdev = to_shmob_device(plane->dev);
|
||||
struct shmob_drm_plane *splane = to_shmob_plane(plane);
|
||||
struct shmob_drm_device *sdev = plane->dev->dev_private;
|
||||
const struct shmob_drm_format_info *format;
|
||||
|
||||
format = shmob_drm_format_info(fb->format->format);
|
||||
if (format == NULL) {
|
||||
dev_dbg(sdev->dev, "update_plane: unsupported format %08x\n",
|
||||
fb->format->format);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!old_state->crtc)
|
||||
return;
|
||||
|
||||
if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) {
|
||||
dev_dbg(sdev->dev, "%s: scaling not supported\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
splane->format = format;
|
||||
|
||||
splane->src_x = src_x >> 16;
|
||||
splane->src_y = src_y >> 16;
|
||||
splane->crtc_x = crtc_x;
|
||||
splane->crtc_y = crtc_y;
|
||||
splane->crtc_w = crtc_w;
|
||||
splane->crtc_h = crtc_h;
|
||||
|
||||
__shmob_drm_plane_setup(splane, fb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shmob_drm_plane_disable(struct drm_plane *plane,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
struct shmob_drm_plane *splane = to_shmob_plane(plane);
|
||||
struct shmob_drm_device *sdev = plane->dev->dev_private;
|
||||
|
||||
splane->format = NULL;
|
||||
if (plane->type != DRM_PLANE_TYPE_OVERLAY)
|
||||
return;
|
||||
|
||||
lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index));
|
||||
lcdc_write(sdev, LDBnBSIFR(splane->index), 0);
|
||||
return 0;
|
||||
lcdc_write(sdev, LDBCR,
|
||||
LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index));
|
||||
}
|
||||
|
||||
static void shmob_drm_plane_destroy(struct drm_plane *plane)
|
||||
static struct drm_plane_state *
|
||||
shmob_drm_plane_atomic_duplicate_state(struct drm_plane *plane)
|
||||
{
|
||||
drm_plane_force_disable(plane);
|
||||
drm_plane_cleanup(plane);
|
||||
struct shmob_drm_plane_state *state;
|
||||
struct shmob_drm_plane_state *copy;
|
||||
|
||||
if (WARN_ON(!plane->state))
|
||||
return NULL;
|
||||
|
||||
state = to_shmob_plane_state(plane->state);
|
||||
copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
|
||||
if (copy == NULL)
|
||||
return NULL;
|
||||
|
||||
__drm_atomic_helper_plane_duplicate_state(plane, ©->base);
|
||||
|
||||
return ©->base;
|
||||
}
|
||||
|
||||
static void shmob_drm_plane_atomic_destroy_state(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
__drm_atomic_helper_plane_destroy_state(state);
|
||||
kfree(to_shmob_plane_state(state));
|
||||
}
|
||||
|
||||
static void shmob_drm_plane_reset(struct drm_plane *plane)
|
||||
{
|
||||
struct shmob_drm_plane_state *state;
|
||||
|
||||
if (plane->state) {
|
||||
shmob_drm_plane_atomic_destroy_state(plane, plane->state);
|
||||
plane->state = NULL;
|
||||
}
|
||||
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (state == NULL)
|
||||
return;
|
||||
|
||||
__drm_atomic_helper_plane_reset(plane, &state->base);
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs shmob_drm_plane_helper_funcs = {
|
||||
.atomic_check = shmob_drm_plane_atomic_check,
|
||||
.atomic_update = shmob_drm_plane_atomic_update,
|
||||
.atomic_disable = shmob_drm_plane_atomic_disable,
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs shmob_drm_plane_funcs = {
|
||||
.update_plane = shmob_drm_plane_update,
|
||||
.disable_plane = shmob_drm_plane_disable,
|
||||
.destroy = shmob_drm_plane_destroy,
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.reset = shmob_drm_plane_reset,
|
||||
.atomic_duplicate_state = shmob_drm_plane_atomic_duplicate_state,
|
||||
.atomic_destroy_state = shmob_drm_plane_atomic_destroy_state,
|
||||
};
|
||||
|
||||
static const uint32_t formats[] = {
|
||||
|
|
@ -244,22 +295,23 @@ static const uint32_t formats[] = {
|
|||
DRM_FORMAT_NV42,
|
||||
};
|
||||
|
||||
int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index)
|
||||
struct drm_plane *shmob_drm_plane_create(struct shmob_drm_device *sdev,
|
||||
enum drm_plane_type type,
|
||||
unsigned int index)
|
||||
{
|
||||
struct shmob_drm_plane *splane;
|
||||
int ret;
|
||||
|
||||
splane = devm_kzalloc(sdev->dev, sizeof(*splane), GFP_KERNEL);
|
||||
if (splane == NULL)
|
||||
return -ENOMEM;
|
||||
splane = drmm_universal_plane_alloc(&sdev->ddev,
|
||||
struct shmob_drm_plane, base, 1,
|
||||
&shmob_drm_plane_funcs, formats,
|
||||
ARRAY_SIZE(formats), NULL, type,
|
||||
NULL);
|
||||
if (IS_ERR(splane))
|
||||
return ERR_CAST(splane);
|
||||
|
||||
splane->index = index;
|
||||
splane->alpha = 255;
|
||||
|
||||
ret = drm_universal_plane_init(sdev->ddev, &splane->plane, 1,
|
||||
&shmob_drm_plane_funcs,
|
||||
formats, ARRAY_SIZE(formats), NULL,
|
||||
DRM_PLANE_TYPE_OVERLAY, NULL);
|
||||
drm_plane_helper_add(&splane->base, &shmob_drm_plane_helper_funcs);
|
||||
|
||||
return ret;
|
||||
return &splane->base;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue