Merge branch 'drm/next/du' of git://linuxtv.org/pinchartl/media into drm-next
RCAR GEN3 DU HDMI support. * 'drm/next/du' of git://linuxtv.org/pinchartl/media: (22 commits) drm: rcar-du: Add HDMI outputs to R8A7795 device description drm: rcar-du: Add DPLL support drm: rcar-du: Skip disabled outputs drm: rcar-du: Add Gen3 HDMI encoder support dt-bindings: display: renesas: Add R-Car Gen3 HDMI TX DT bindings drm: rcar-du: Hardcode encoders types to DRM_MODE_ENCODER_NONE drm: rcar-du: Replace manual bridge implementation with DRM bridge drm: rcar-du: Add support for LVDS mode selection drm: rcar-du: Use the DRM panel API drm: rcar-du: Document the vsps property in the DT bindings drm: rcar-du: Remove wait field from rcar_du_device structure drm: rcar-du: Make sure the VSP is initialized on platforms that need it drm: rcar-du: Use DRM core's atomic commit helper drm: rcar-du: Clear handled event pointer in CRTC state drm: rcar-du: Handle event when disabling CRTCs drm: rcar-du: Don't open code of_device_get_match_data() drm: rcar-du: Switch to encoder .atomic_mode_set() helper function drm: panels: Add LVDS panel driver drm: Add data transmission order bus flag devicetree/bindings: display: Add bindings for two Mitsubishi panels ...
This commit is contained in:
commit
fabe2be10f
@ -0,0 +1,75 @@
|
||||
Renesas Gen3 DWC HDMI TX Encoder
|
||||
================================
|
||||
|
||||
The HDMI transmitter is a Synopsys DesignWare HDMI 1.4 TX controller IP
|
||||
with a companion PHY IP.
|
||||
|
||||
These DT bindings follow the Synopsys DWC HDMI TX bindings defined in
|
||||
Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt with the
|
||||
following device-specific properties.
|
||||
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Shall contain one or more of
|
||||
- "renesas,r8a7795-hdmi" for R8A7795 (R-Car H3) compatible HDMI TX
|
||||
- "renesas,rcar-gen3-hdmi" for the generic R-Car Gen3 compatible HDMI TX
|
||||
|
||||
When compatible with generic versions, nodes must list the SoC-specific
|
||||
version corresponding to the platform first, followed by the
|
||||
family-specific version.
|
||||
|
||||
- reg: See dw_hdmi.txt.
|
||||
- interrupts: HDMI interrupt number
|
||||
- clocks: See dw_hdmi.txt.
|
||||
- clock-names: Shall contain "iahb" and "isfr" as defined in dw_hdmi.txt.
|
||||
- ports: See dw_hdmi.txt. The DWC HDMI shall have one port numbered 0
|
||||
corresponding to the video input of the controller and one port numbered 1
|
||||
corresponding to its HDMI output. Each port shall have a single endpoint.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- power-domains: Shall reference the power domain that contains the DWC HDMI,
|
||||
if any.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
hdmi0: hdmi0@fead0000 {
|
||||
compatible = "renesas,r8a7795-dw-hdmi";
|
||||
reg = <0 0xfead0000 0 0x10000>;
|
||||
interrupts = <0 389 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cpg CPG_CORE R8A7795_CLK_S0D4>, <&cpg CPG_MOD 729>;
|
||||
clock-names = "iahb", "isfr";
|
||||
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
|
||||
status = "disabled";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
dw_hdmi0_in: endpoint {
|
||||
remote-endpoint = <&du_out_hdmi0>;
|
||||
};
|
||||
};
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
rcar_dw_hdmi0_out: endpoint {
|
||||
remote-endpoint = <&hdmi0_con>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hdmi0-out {
|
||||
compatible = "hdmi-connector";
|
||||
label = "HDMI0 OUT";
|
||||
type = "a";
|
||||
|
||||
port {
|
||||
hdmi0_con: endpoint {
|
||||
remote-endpoint = <&rcar_dw_hdmi0_out>;
|
||||
};
|
||||
};
|
||||
};
|
@ -0,0 +1,47 @@
|
||||
Mitsubishi AA204XD12 LVDS Display Panel
|
||||
=======================================
|
||||
|
||||
The AA104XD12 is a 10.4" XGA TFT-LCD display panel.
|
||||
|
||||
These DT bindings follow the LVDS panel bindings defined in panel-lvds.txt
|
||||
with the following device-specific properties.
|
||||
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Shall contain "mitsubishi,aa121td01" and "panel-lvds", in that
|
||||
order.
|
||||
- vcc-supply: Reference to the regulator powering the panel VCC pins.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
panel {
|
||||
compatible = "mitsubishi,aa104xd12", "panel-lvds";
|
||||
vcc-supply = <&vcc_3v3>;
|
||||
|
||||
width-mm = <210>;
|
||||
height-mm = <158>;
|
||||
|
||||
data-mapping = "jeida-24";
|
||||
|
||||
panel-timing {
|
||||
/* 1024x768 @65Hz */
|
||||
clock-frequency = <65000000>;
|
||||
hactive = <1024>;
|
||||
vactive = <768>;
|
||||
hsync-len = <136>;
|
||||
hfront-porch = <20>;
|
||||
hback-porch = <160>;
|
||||
vfront-porch = <3>;
|
||||
vback-porch = <29>;
|
||||
vsync-len = <6>;
|
||||
};
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&lvds_encoder>;
|
||||
};
|
||||
};
|
||||
};
|
@ -0,0 +1,47 @@
|
||||
Mitsubishi AA121TD01 LVDS Display Panel
|
||||
=======================================
|
||||
|
||||
The AA121TD01 is a 12.1" WXGA TFT-LCD display panel.
|
||||
|
||||
These DT bindings follow the LVDS panel bindings defined in panel-lvds.txt
|
||||
with the following device-specific properties.
|
||||
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Shall contain "mitsubishi,aa121td01" and "panel-lvds", in that
|
||||
order.
|
||||
- vcc-supply: Reference to the regulator powering the panel VCC pins.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
panel {
|
||||
compatible = "mitsubishi,aa121td01", "panel-lvds";
|
||||
vcc-supply = <&vcc_3v3>;
|
||||
|
||||
width-mm = <261>;
|
||||
height-mm = <163>;
|
||||
|
||||
data-mapping = "jeida-24";
|
||||
|
||||
panel-timing {
|
||||
/* 1280x800 @60Hz */
|
||||
clock-frequency = <71000000>;
|
||||
hactive = <1280>;
|
||||
vactive = <800>;
|
||||
hsync-len = <70>;
|
||||
hfront-porch = <20>;
|
||||
hback-porch = <70>;
|
||||
vsync-len = <5>;
|
||||
vfront-porch = <3>;
|
||||
vback-porch = <15>;
|
||||
};
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&lvds_encoder>;
|
||||
};
|
||||
};
|
||||
};
|
@ -0,0 +1,91 @@
|
||||
Common Properties for Display Panel
|
||||
===================================
|
||||
|
||||
This document defines device tree properties common to several classes of
|
||||
display panels. It doesn't constitue a device tree binding specification by
|
||||
itself but is meant to be referenced by device tree bindings.
|
||||
|
||||
When referenced from panel device tree bindings the properties defined in this
|
||||
document are defined as follows. The panel device tree bindings are
|
||||
responsible for defining whether each property is required or optional.
|
||||
|
||||
|
||||
Descriptive Properties
|
||||
----------------------
|
||||
|
||||
- width-mm,
|
||||
- height-mm: The width-mm and height-mm specify the width and height of the
|
||||
physical area where images are displayed. These properties are expressed in
|
||||
millimeters and rounded to the closest unit.
|
||||
|
||||
- label: The label property specifies a symbolic name for the panel as a
|
||||
string suitable for use by humans. It typically contains a name inscribed on
|
||||
the system (e.g. as an affixed label) or specified in the system's
|
||||
documentation (e.g. in the user's manual).
|
||||
|
||||
If no such name exists, and unless the property is mandatory according to
|
||||
device tree bindings, it shall rather be omitted than constructed of
|
||||
non-descriptive information. For instance an LCD panel in a system that
|
||||
contains a single panel shall not be labelled "LCD" if that name is not
|
||||
inscribed on the system or used in a descriptive fashion in system
|
||||
documentation.
|
||||
|
||||
|
||||
Display Timings
|
||||
---------------
|
||||
|
||||
- panel-timing: Most display panels are restricted to a single resolution and
|
||||
require specific display timings. The panel-timing subnode expresses those
|
||||
timings as specified in the timing subnode section of the display timing
|
||||
bindings defined in
|
||||
Documentation/devicetree/bindings/display/display-timing.txt.
|
||||
|
||||
|
||||
Connectivity
|
||||
------------
|
||||
|
||||
- ports: Panels receive video data through one or multiple connections. While
|
||||
the nature of those connections is specific to the panel type, the
|
||||
connectivity is expressed in a standard fashion using ports as specified in
|
||||
the device graph bindings defined in
|
||||
Documentation/devicetree/bindings/graph.txt.
|
||||
|
||||
- ddc-i2c-bus: Some panels expose EDID information through an I2C-compatible
|
||||
bus such as DDC2 or E-DDC. For such panels the ddc-i2c-bus contains a
|
||||
phandle to the system I2C controller connected to that bus.
|
||||
|
||||
|
||||
Control I/Os
|
||||
------------
|
||||
|
||||
Many display panels can be controlled through pins driven by GPIOs. The nature
|
||||
and timing of those control signals are device-specific and left for panel
|
||||
device tree bindings to specify. The following GPIO specifiers can however be
|
||||
used for panels that implement compatible control signals.
|
||||
|
||||
- enable-gpios: Specifier for a GPIO connected to the panel enable control
|
||||
signal. The enable signal is active high and enables operation of the panel.
|
||||
This property can also be used for panels implementing an active low power
|
||||
down signal, which is a negated version of the enable signal. Active low
|
||||
enable signals (or active high power down signals) can be supported by
|
||||
inverting the GPIO specifier polarity flag.
|
||||
|
||||
Note that the enable signal control panel operation only and must not be
|
||||
confused with a backlight enable signal.
|
||||
|
||||
- reset-gpios: Specifier for a GPIO coonnected to the panel reset control
|
||||
signal. The reset signal is active low and resets the panel internal logic
|
||||
while active. Active high reset signals can be supported by inverting the
|
||||
GPIO specifier polarity flag.
|
||||
|
||||
|
||||
Backlight
|
||||
---------
|
||||
|
||||
Most display panels include a backlight. Some of them also include a backlight
|
||||
controller exposed through a control bus such as I2C or DSI. Others expose
|
||||
backlight control through GPIO, PWM or other signals connected to an external
|
||||
backlight controller.
|
||||
|
||||
- backlight: For panels whose backlight is controlled by an external backlight
|
||||
controller, this property contains a phandle that references the controller.
|
120
Documentation/devicetree/bindings/display/panel/panel-lvds.txt
Normal file
120
Documentation/devicetree/bindings/display/panel/panel-lvds.txt
Normal file
@ -0,0 +1,120 @@
|
||||
LVDS Display Panel
|
||||
==================
|
||||
|
||||
LVDS is a physical layer specification defined in ANSI/TIA/EIA-644-A. Multiple
|
||||
incompatible data link layers have been used over time to transmit image data
|
||||
to LVDS panels. This bindings supports display panels compatible with the
|
||||
following specifications.
|
||||
|
||||
[JEIDA] "Digital Interface Standards for Monitor", JEIDA-59-1999, February
|
||||
1999 (Version 1.0), Japan Electronic Industry Development Association (JEIDA)
|
||||
[LDI] "Open LVDS Display Interface", May 1999 (Version 0.95), National
|
||||
Semiconductor
|
||||
[VESA] "VESA Notebook Panel Standard", October 2007 (Version 1.0), Video
|
||||
Electronics Standards Association (VESA)
|
||||
|
||||
Device compatible with those specifications have been marketed under the
|
||||
FPD-Link and FlatLink brands.
|
||||
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Shall contain "panel-lvds" in addition to a mandatory
|
||||
panel-specific compatible string defined in individual panel bindings. The
|
||||
"panel-lvds" value shall never be used on its own.
|
||||
- width-mm: See panel-common.txt.
|
||||
- height-mm: See panel-common.txt.
|
||||
- data-mapping: The color signals mapping order, "jeida-18", "jeida-24"
|
||||
or "vesa-24".
|
||||
|
||||
Optional properties:
|
||||
|
||||
- label: See panel-common.txt.
|
||||
- gpios: See panel-common.txt.
|
||||
- backlight: See panel-common.txt.
|
||||
- data-mirror: If set, reverse the bit order described in the data mappings
|
||||
below on all data lanes, transmitting bits for slots 6 to 0 instead of
|
||||
0 to 6.
|
||||
|
||||
Required nodes:
|
||||
|
||||
- panel-timing: See panel-common.txt.
|
||||
- ports: See panel-common.txt. These bindings require a single port subnode
|
||||
corresponding to the panel LVDS input.
|
||||
|
||||
|
||||
LVDS data mappings are defined as follows.
|
||||
|
||||
- "jeida-18" - 18-bit data mapping compatible with the [JEIDA], [LDI] and
|
||||
[VESA] specifications. Data are transferred as follows on 3 LVDS lanes.
|
||||
|
||||
Slot 0 1 2 3 4 5 6
|
||||
________________ _________________
|
||||
Clock \_______________________/
|
||||
______ ______ ______ ______ ______ ______ ______
|
||||
DATA0 ><__G0__><__R5__><__R4__><__R3__><__R2__><__R1__><__R0__><
|
||||
DATA1 ><__B1__><__B0__><__G5__><__G4__><__G3__><__G2__><__G1__><
|
||||
DATA2 ><_CTL2_><_CTL1_><_CTL0_><__B5__><__B4__><__B3__><__B2__><
|
||||
|
||||
- "jeida-24" - 24-bit data mapping compatible with the [DSIM] and [LDI]
|
||||
specifications. Data are transferred as follows on 4 LVDS lanes.
|
||||
|
||||
Slot 0 1 2 3 4 5 6
|
||||
________________ _________________
|
||||
Clock \_______________________/
|
||||
______ ______ ______ ______ ______ ______ ______
|
||||
DATA0 ><__G2__><__R7__><__R6__><__R5__><__R4__><__R3__><__R2__><
|
||||
DATA1 ><__B3__><__B2__><__G7__><__G6__><__G5__><__G4__><__G3__><
|
||||
DATA2 ><_CTL2_><_CTL1_><_CTL0_><__B7__><__B6__><__B5__><__B4__><
|
||||
DATA3 ><_CTL3_><__B1__><__B0__><__G1__><__G0__><__R1__><__R0__><
|
||||
|
||||
- "vesa-24" - 24-bit data mapping compatible with the [VESA] specification.
|
||||
Data are transferred as follows on 4 LVDS lanes.
|
||||
|
||||
Slot 0 1 2 3 4 5 6
|
||||
________________ _________________
|
||||
Clock \_______________________/
|
||||
______ ______ ______ ______ ______ ______ ______
|
||||
DATA0 ><__G0__><__R5__><__R4__><__R3__><__R2__><__R1__><__R0__><
|
||||
DATA1 ><__B1__><__B0__><__G5__><__G4__><__G3__><__G2__><__G1__><
|
||||
DATA2 ><_CTL2_><_CTL1_><_CTL0_><__B5__><__B4__><__B3__><__B2__><
|
||||
DATA3 ><_CTL3_><__B7__><__B6__><__G7__><__G6__><__R7__><__R6__><
|
||||
|
||||
Control signals are mapped as follows.
|
||||
|
||||
CTL0: HSync
|
||||
CTL1: VSync
|
||||
CTL2: Data Enable
|
||||
CTL3: 0
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
panel {
|
||||
compatible = "mitsubishi,aa121td01", "panel-lvds";
|
||||
|
||||
width-mm = <261>;
|
||||
height-mm = <163>;
|
||||
|
||||
data-mapping = "jeida-24";
|
||||
|
||||
panel-timing {
|
||||
/* 1280x800 @60Hz */
|
||||
clock-frequency = <71000000>;
|
||||
hactive = <1280>;
|
||||
vactive = <800>;
|
||||
hsync-len = <70>;
|
||||
hfront-porch = <20>;
|
||||
hback-porch = <70>;
|
||||
vsync-len = <5>;
|
||||
vfront-porch = <3>;
|
||||
vback-porch = <15>;
|
||||
};
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&lvds_encoder>;
|
||||
};
|
||||
};
|
||||
};
|
@ -36,6 +36,9 @@ Required Properties:
|
||||
When supplied they must be named "dclkin.x" with "x" being the input
|
||||
clock numerical index.
|
||||
|
||||
- vsps: A list of phandles to the VSP nodes that handle the memory
|
||||
interfaces for the DU channels.
|
||||
|
||||
Required nodes:
|
||||
|
||||
The connections to the DU output video ports are modeled using the OF graph
|
||||
|
@ -4383,6 +4383,7 @@ S: Supported
|
||||
F: drivers/gpu/drm/rcar-du/
|
||||
F: drivers/gpu/drm/shmobile/
|
||||
F: include/linux/platform_data/shmob_drm.h
|
||||
F: Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt
|
||||
F: Documentation/devicetree/bindings/display/renesas,du.txt
|
||||
|
||||
DRM DRIVER FOR QXL VIRTUAL GPU
|
||||
|
@ -7,6 +7,16 @@ config DRM_PANEL
|
||||
menu "Display Panels"
|
||||
depends on DRM && DRM_PANEL
|
||||
|
||||
config DRM_PANEL_LVDS
|
||||
tristate "Generic LVDS panel driver"
|
||||
depends on OF
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
select VIDEOMODE_HELPERS
|
||||
help
|
||||
This driver supports LVDS panels that don't require device-specific
|
||||
handling of power supplies or control signals. It implements automatic
|
||||
backlight handling if the panel is attached to a backlight controller.
|
||||
|
||||
config DRM_PANEL_SIMPLE
|
||||
tristate "support for simple panels"
|
||||
depends on OF
|
||||
|
@ -1,3 +1,4 @@
|
||||
obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
|
||||
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
|
||||
obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
|
||||
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
|
||||
|
286
drivers/gpu/drm/panel/panel-lvds.c
Normal file
286
drivers/gpu/drm/panel/panel-lvds.c
Normal file
@ -0,0 +1,286 @@
|
||||
/*
|
||||
* rcar_du_crtc.c -- R-Car Display Unit CRTCs
|
||||
*
|
||||
* Copyright (C) 2016 Laurent Pinchart
|
||||
* Copyright (C) 2016 Renesas Electronics Corporation
|
||||
*
|
||||
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include <video/display_timing.h>
|
||||
#include <video/of_display_timing.h>
|
||||
#include <video/videomode.h>
|
||||
|
||||
struct panel_lvds {
|
||||
struct drm_panel panel;
|
||||
struct device *dev;
|
||||
|
||||
const char *label;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
struct videomode video_mode;
|
||||
unsigned int bus_format;
|
||||
bool data_mirror;
|
||||
|
||||
struct backlight_device *backlight;
|
||||
|
||||
struct gpio_desc *enable_gpio;
|
||||
struct gpio_desc *reset_gpio;
|
||||
};
|
||||
|
||||
static inline struct panel_lvds *to_panel_lvds(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct panel_lvds, panel);
|
||||
}
|
||||
|
||||
static int panel_lvds_disable(struct drm_panel *panel)
|
||||
{
|
||||
struct panel_lvds *lvds = to_panel_lvds(panel);
|
||||
|
||||
if (lvds->backlight) {
|
||||
lvds->backlight->props.power = FB_BLANK_POWERDOWN;
|
||||
lvds->backlight->props.state |= BL_CORE_FBBLANK;
|
||||
backlight_update_status(lvds->backlight);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int panel_lvds_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct panel_lvds *lvds = to_panel_lvds(panel);
|
||||
|
||||
if (lvds->enable_gpio)
|
||||
gpiod_set_value_cansleep(lvds->enable_gpio, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int panel_lvds_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct panel_lvds *lvds = to_panel_lvds(panel);
|
||||
|
||||
if (lvds->enable_gpio)
|
||||
gpiod_set_value_cansleep(lvds->enable_gpio, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int panel_lvds_enable(struct drm_panel *panel)
|
||||
{
|
||||
struct panel_lvds *lvds = to_panel_lvds(panel);
|
||||
|
||||
if (lvds->backlight) {
|
||||
lvds->backlight->props.state &= ~BL_CORE_FBBLANK;
|
||||
lvds->backlight->props.power = FB_BLANK_UNBLANK;
|
||||
backlight_update_status(lvds->backlight);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int panel_lvds_get_modes(struct drm_panel *panel)
|
||||
{
|
||||
struct panel_lvds *lvds = to_panel_lvds(panel);
|
||||
struct drm_connector *connector = lvds->panel.connector;
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_create(lvds->panel.drm);
|
||||
if (!mode)
|
||||
return 0;
|
||||
|
||||
drm_display_mode_from_videomode(&lvds->video_mode, mode);
|
||||
mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
connector->display_info.width_mm = lvds->width;
|
||||
connector->display_info.height_mm = lvds->height;
|
||||
drm_display_info_set_bus_formats(&connector->display_info,
|
||||
&lvds->bus_format, 1);
|
||||
connector->display_info.bus_flags = lvds->data_mirror
|
||||
? DRM_BUS_FLAG_DATA_LSB_TO_MSB
|
||||
: DRM_BUS_FLAG_DATA_MSB_TO_LSB;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs panel_lvds_funcs = {
|
||||
.disable = panel_lvds_disable,
|
||||
.unprepare = panel_lvds_unprepare,
|
||||
.prepare = panel_lvds_prepare,
|
||||
.enable = panel_lvds_enable,
|
||||
.get_modes = panel_lvds_get_modes,
|
||||
};
|
||||
|
||||
static int panel_lvds_parse_dt(struct panel_lvds *lvds)
|
||||
{
|
||||
struct device_node *np = lvds->dev->of_node;
|
||||
struct display_timing timing;
|
||||
const char *mapping;
|
||||
int ret;
|
||||
|
||||
ret = of_get_display_timing(np, "panel-timing", &timing);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
videomode_from_timing(&timing, &lvds->video_mode);
|
||||
|
||||
ret = of_property_read_u32(np, "width-mm", &lvds->width);
|
||||
if (ret < 0) {
|
||||
dev_err(lvds->dev, "%s: invalid or missing %s DT property\n",
|
||||
of_node_full_name(np), "width-mm");
|
||||
return -ENODEV;
|
||||
}
|
||||
ret = of_property_read_u32(np, "height-mm", &lvds->height);
|
||||
if (ret < 0) {
|
||||
dev_err(lvds->dev, "%s: invalid or missing %s DT property\n",
|
||||
of_node_full_name(np), "height-mm");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
of_property_read_string(np, "label", &lvds->label);
|
||||
|
||||
ret = of_property_read_string(np, "data-mapping", &mapping);
|
||||
if (ret < 0) {
|
||||
dev_err(lvds->dev, "%s: invalid or missing %s DT property\n",
|
||||
of_node_full_name(np), "data-mapping");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!strcmp(mapping, "jeida-18")) {
|
||||
lvds->bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG;
|
||||
} else if (!strcmp(mapping, "jeida-24")) {
|
||||
lvds->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
|
||||
} else if (!strcmp(mapping, "vesa-24")) {
|
||||
lvds->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
|
||||
} else {
|
||||
dev_err(lvds->dev, "%s: invalid or missing %s DT property\n",
|
||||
of_node_full_name(np), "data-mapping");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lvds->data_mirror = of_property_read_bool(np, "data-mirror");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int panel_lvds_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_lvds *lvds;
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
|
||||
if (!lvds)
|
||||
return -ENOMEM;
|
||||
|
||||
lvds->dev = &pdev->dev;
|
||||
|
||||
ret = panel_lvds_parse_dt(lvds);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Get GPIOs and backlight controller. */
|
||||
lvds->enable_gpio = devm_gpiod_get_optional(lvds->dev, "enable",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(lvds->enable_gpio)) {
|
||||
ret = PTR_ERR(lvds->enable_gpio);
|
||||
dev_err(lvds->dev, "failed to request %s GPIO: %d\n",
|
||||
"enable", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
lvds->reset_gpio = devm_gpiod_get_optional(lvds->dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(lvds->reset_gpio)) {
|
||||
ret = PTR_ERR(lvds->reset_gpio);
|
||||
dev_err(lvds->dev, "failed to request %s GPIO: %d\n",
|
||||
"reset", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
np = of_parse_phandle(lvds->dev->of_node, "backlight", 0);
|
||||
if (np) {
|
||||
lvds->backlight = of_find_backlight_by_node(np);
|
||||
of_node_put(np);
|
||||
|
||||
if (!lvds->backlight)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Handle all power supplies specified in the DT node in a generic
|
||||
* way for panels that don't care about power supply ordering. LVDS
|
||||
* panels that require a specific power sequence will need a dedicated
|
||||
* driver.
|
||||
*/
|
||||
|
||||
/* Register the panel. */
|
||||
drm_panel_init(&lvds->panel);
|
||||
lvds->panel.dev = lvds->dev;
|
||||
lvds->panel.funcs = &panel_lvds_funcs;
|
||||
|
||||
ret = drm_panel_add(&lvds->panel);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
dev_set_drvdata(lvds->dev, lvds);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
put_device(&lvds->backlight->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int panel_lvds_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_lvds *lvds = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
drm_panel_detach(&lvds->panel);
|
||||
drm_panel_remove(&lvds->panel);
|
||||
|
||||
panel_lvds_disable(&lvds->panel);
|
||||
|
||||
if (lvds->backlight)
|
||||
put_device(&lvds->backlight->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id panel_lvds_of_table[] = {
|
||||
{ .compatible = "panel-lvds", },
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, panel_lvds_of_table);
|
||||
|
||||
static struct platform_driver panel_lvds_driver = {
|
||||
.probe = panel_lvds_probe,
|
||||
.remove = panel_lvds_remove,
|
||||
.driver = {
|
||||
.name = "panel-lvds",
|
||||
.of_match_table = panel_lvds_of_table,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(panel_lvds_driver);
|
||||
|
||||
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
|
||||
MODULE_DESCRIPTION("LVDS Panel Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -11,15 +11,17 @@ config DRM_RCAR_DU
|
||||
Choose this option if you have an R-Car chipset.
|
||||
If M is selected the module will be called rcar-du-drm.
|
||||
|
||||
config DRM_RCAR_HDMI
|
||||
bool "R-Car DU HDMI Encoder Support"
|
||||
depends on DRM_RCAR_DU
|
||||
config DRM_RCAR_DW_HDMI
|
||||
tristate "R-Car DU Gen3 HDMI Encoder Support"
|
||||
depends on DRM && OF
|
||||
select DRM_DW_HDMI
|
||||
help
|
||||
Enable support for external HDMI encoders.
|
||||
Enable support for R-Car Gen3 internal HDMI encoder.
|
||||
|
||||
config DRM_RCAR_LVDS
|
||||
bool "R-Car DU LVDS Encoder Support"
|
||||
depends on DRM_RCAR_DU
|
||||
select DRM_PANEL
|
||||
help
|
||||
Enable support for the R-Car Display Unit embedded LVDS encoders.
|
||||
|
||||
|
@ -4,13 +4,11 @@ rcar-du-drm-y := rcar_du_crtc.o \
|
||||
rcar_du_group.o \
|
||||
rcar_du_kms.o \
|
||||
rcar_du_lvdscon.o \
|
||||
rcar_du_plane.o \
|
||||
rcar_du_vgacon.o
|
||||
|
||||
rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI) += rcar_du_hdmienc.o
|
||||
rcar_du_plane.o
|
||||
|
||||
rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o
|
||||
|
||||
rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
|
||||
|
||||
obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
|
||||
obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
|
||||
|
@ -106,9 +106,62 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
|
||||
* Hardware Setup
|
||||
*/
|
||||
|
||||
struct dpll_info {
|
||||
unsigned int output;
|
||||
unsigned int fdpll;
|
||||
unsigned int n;
|
||||
unsigned int m;
|
||||
};
|
||||
|
||||
static void rcar_du_dpll_divider(struct rcar_du_crtc *rcrtc,
|
||||
struct dpll_info *dpll,
|
||||
unsigned long input,
|
||||
unsigned long target)
|
||||
{
|
||||
unsigned long best_diff = (unsigned long)-1;
|
||||
unsigned long diff;
|
||||
unsigned int fdpll;
|
||||
unsigned int m;
|
||||
unsigned int n;
|
||||
|
||||
for (n = 39; n < 120; n++) {
|
||||
for (m = 0; m < 4; m++) {
|
||||
for (fdpll = 1; fdpll < 32; fdpll++) {
|
||||
unsigned long output;
|
||||
|
||||
/* 1/2 (FRQSEL=1) for duty rate 50% */
|
||||
output = input * (n + 1) / (m + 1)
|
||||
/ (fdpll + 1) / 2;
|
||||
|
||||
if (output >= 400000000)
|
||||
continue;
|
||||
|
||||
diff = abs((long)output - (long)target);
|
||||
if (best_diff > diff) {
|
||||
best_diff = diff;
|
||||
dpll->n = n;
|
||||
dpll->m = m;
|
||||
dpll->fdpll = fdpll;
|
||||
dpll->output = output;
|
||||
}
|
||||
|
||||
if (diff == 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
dev_dbg(rcrtc->group->dev->dev,
|
||||
"output:%u, fdpll:%u, n:%u, m:%u, diff:%lu\n",
|
||||
dpll->output, dpll->fdpll, dpll->n, dpll->m,
|
||||
best_diff);
|
||||
}
|
||||
|
||||
static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
|
||||
{
|
||||
const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
|
||||
struct rcar_du_device *rcdu = rcrtc->group->dev;
|
||||
unsigned long mode_clock = mode->clock * 1000;
|
||||
unsigned long clk;
|
||||
u32 value;
|
||||
@ -124,12 +177,18 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
|
||||
escr = div | ESCR_DCLKSEL_CLKS;
|
||||
|
||||
if (rcrtc->extclock) {
|
||||
struct dpll_info dpll = { 0 };
|
||||
unsigned long extclk;
|
||||
unsigned long extrate;
|
||||
unsigned long rate;
|
||||
u32 extdiv;
|
||||
|
||||
extclk = clk_get_rate(rcrtc->extclock);
|
||||
if (rcdu->info->dpll_ch & (1 << rcrtc->index)) {
|
||||
rcar_du_dpll_divider(rcrtc, &dpll, extclk, mode_clock);
|
||||
extclk = dpll.output;
|
||||
}
|
||||
|
||||
extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock);
|
||||
extdiv = clamp(extdiv, 1U, 64U) - 1;
|
||||
|
||||
@ -140,7 +199,27 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
|
||||
abs((long)rate - (long)mode_clock)) {
|
||||
dev_dbg(rcrtc->group->dev->dev,
|
||||
"crtc%u: using external clock\n", rcrtc->index);
|
||||
escr = extdiv | ESCR_DCLKSEL_DCLKIN;
|
||||
|
||||
if (rcdu->info->dpll_ch & (1 << rcrtc->index)) {
|
||||
u32 dpllcr = DPLLCR_CODE | DPLLCR_CLKE
|
||||
| DPLLCR_FDPLL(dpll.fdpll)
|
||||
| DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m)
|
||||
| DPLLCR_STBY;
|
||||
|
||||
if (rcrtc->index == 1)
|
||||
dpllcr |= DPLLCR_PLCS1
|
||||
| DPLLCR_INCS_DOTCLKIN1;
|
||||
else
|
||||
dpllcr |= DPLLCR_PLCS0
|
||||
| DPLLCR_INCS_DOTCLKIN0;
|
||||
|
||||
rcar_du_group_write(rcrtc->group, DPLLCR,
|
||||
dpllcr);
|
||||
|
||||
escr = ESCR_DCLKSEL_DCLKIN | 1;
|
||||
} else {
|
||||
escr = ESCR_DCLKSEL_DCLKIN | extdiv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -488,22 +567,29 @@ static void rcar_du_crtc_disable(struct drm_crtc *crtc)
|
||||
rcar_du_crtc_stop(rcrtc);
|
||||
rcar_du_crtc_put(rcrtc);
|
||||
|
||||
spin_lock_irq(&crtc->dev->event_lock);
|
||||
if (crtc->state->event) {
|
||||
drm_crtc_send_vblank_event(crtc, crtc->state->event);
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
spin_unlock_irq(&crtc->dev->event_lock);
|
||||
|
||||
rcrtc->outputs = 0;
|
||||
}
|
||||
|
||||
static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
struct drm_pending_vblank_event *event = crtc->state->event;
|
||||
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
|
||||
struct drm_device *dev = rcrtc->crtc.dev;
|
||||
unsigned long flags;
|
||||
|
||||
if (event) {
|
||||
if (crtc->state->event) {
|
||||
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
rcrtc->event = event;
|
||||
rcrtc->event = crtc->state->event;
|
||||
crtc->state->event = NULL;
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* rcar_du_crtc.h -- R-Car Display Unit CRTCs
|
||||
*
|
||||
* Copyright (C) 2013-2014 Renesas Electronics Corporation
|
||||
* Copyright (C) 2013-2015 Renesas Electronics Corporation
|
||||
*
|
||||
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*
|
||||
@ -61,6 +61,8 @@ enum rcar_du_output {
|
||||
RCAR_DU_OUTPUT_DPAD1,
|
||||
RCAR_DU_OUTPUT_LVDS0,
|
||||
RCAR_DU_OUTPUT_LVDS1,
|
||||
RCAR_DU_OUTPUT_HDMI0,
|
||||
RCAR_DU_OUTPUT_HDMI1,
|
||||
RCAR_DU_OUTPUT_TCON,
|
||||
RCAR_DU_OUTPUT_MAX,
|
||||
};
|
||||
|
@ -44,12 +44,10 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = {
|
||||
*/
|
||||
[RCAR_DU_OUTPUT_DPAD0] = {
|
||||
.possible_crtcs = BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_DPAD1] = {
|
||||
.possible_crtcs = BIT(1) | BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 1,
|
||||
},
|
||||
},
|
||||
@ -68,17 +66,14 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = {
|
||||
*/
|
||||
[RCAR_DU_OUTPUT_DPAD0] = {
|
||||
.possible_crtcs = BIT(2) | BIT(1) | BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_LVDS0] = {
|
||||
.possible_crtcs = BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_LVDS,
|
||||
.port = 1,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_LVDS1] = {
|
||||
.possible_crtcs = BIT(2) | BIT(1),
|
||||
.encoder_type = DRM_MODE_ENCODER_LVDS,
|
||||
.port = 2,
|
||||
},
|
||||
},
|
||||
@ -97,12 +92,10 @@ static const struct rcar_du_device_info rcar_du_r8a7791_info = {
|
||||
*/
|
||||
[RCAR_DU_OUTPUT_DPAD0] = {
|
||||
.possible_crtcs = BIT(1) | BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_LVDS0] = {
|
||||
.possible_crtcs = BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_LVDS,
|
||||
.port = 1,
|
||||
},
|
||||
},
|
||||
@ -118,12 +111,10 @@ static const struct rcar_du_device_info rcar_du_r8a7792_info = {
|
||||
/* R8A7792 has two RGB outputs. */
|
||||
[RCAR_DU_OUTPUT_DPAD0] = {
|
||||
.possible_crtcs = BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_DPAD1] = {
|
||||
.possible_crtcs = BIT(1),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 1,
|
||||
},
|
||||
},
|
||||
@ -141,12 +132,10 @@ static const struct rcar_du_device_info rcar_du_r8a7794_info = {
|
||||
*/
|
||||
[RCAR_DU_OUTPUT_DPAD0] = {
|
||||
.possible_crtcs = BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_DPAD1] = {
|
||||
.possible_crtcs = BIT(1),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 1,
|
||||
},
|
||||
},
|
||||
@ -160,21 +149,28 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = {
|
||||
| RCAR_DU_FEATURE_VSP1_SOURCE,
|
||||
.num_crtcs = 4,
|
||||
.routes = {
|
||||
/* R8A7795 has one RGB output, one LVDS output and two
|
||||
* (currently unsupported) HDMI outputs.
|
||||
/* R8A7795 has one RGB output, two HDMI outputs and one
|
||||
* LVDS output.
|
||||
*/
|
||||
[RCAR_DU_OUTPUT_DPAD0] = {
|
||||
.possible_crtcs = BIT(3),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_HDMI0] = {
|
||||
.possible_crtcs = BIT(1),
|
||||
.port = 1,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_HDMI1] = {
|
||||
.possible_crtcs = BIT(2),
|
||||
.port = 2,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_LVDS0] = {
|
||||
.possible_crtcs = BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_LVDS,
|
||||
.port = 3,
|
||||
},
|
||||
},
|
||||
.num_lvds = 1,
|
||||
.dpll_ch = BIT(1) | BIT(2),
|
||||
};
|
||||
|
||||
static const struct rcar_du_device_info rcar_du_r8a7796_info = {
|
||||
@ -189,12 +185,10 @@ static const struct rcar_du_device_info rcar_du_r8a7796_info = {
|
||||
*/
|
||||
[RCAR_DU_OUTPUT_DPAD0] = {
|
||||
.possible_crtcs = BIT(2),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_LVDS0] = {
|
||||
.possible_crtcs = BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_LVDS,
|
||||
.port = 2,
|
||||
},
|
||||
},
|
||||
@ -318,10 +312,8 @@ static int rcar_du_probe(struct platform_device *pdev)
|
||||
if (rcdu == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
init_waitqueue_head(&rcdu->commit.wait);
|
||||
|
||||
rcdu->dev = &pdev->dev;
|
||||
rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data;
|
||||
rcdu->info = of_device_get_match_data(rcdu->dev);
|
||||
|
||||
platform_set_drvdata(pdev, rcdu);
|
||||
|
||||
|
@ -38,7 +38,6 @@ struct rcar_du_lvdsenc;
|
||||
/*
|
||||
* struct rcar_du_output_routing - Output routing specification
|
||||
* @possible_crtcs: bitmask of possible CRTCs for the output
|
||||
* @encoder_type: DRM type of the internal encoder associated with the output
|
||||
* @port: device tree port number corresponding to this output route
|
||||
*
|
||||
* The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data
|
||||
@ -47,7 +46,6 @@ struct rcar_du_lvdsenc;
|
||||
*/
|
||||
struct rcar_du_output_routing {
|
||||
unsigned int possible_crtcs;
|
||||
unsigned int encoder_type;
|
||||
unsigned int port;
|
||||
};
|
||||
|
||||
@ -67,6 +65,7 @@ struct rcar_du_device_info {
|
||||
unsigned int num_crtcs;
|
||||
struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
|
||||
unsigned int num_lvds;
|
||||
unsigned int dpll_ch;
|
||||
};
|
||||
|
||||
#define RCAR_DU_MAX_CRTCS 4
|
||||
@ -98,11 +97,6 @@ struct rcar_du_device {
|
||||
unsigned int vspd1_sink;
|
||||
|
||||
struct rcar_du_lvdsenc *lvds[RCAR_DU_MAX_LVDS];
|
||||
|
||||
struct {
|
||||
wait_queue_head_t wait;
|
||||
u32 pending;
|
||||
} commit;
|
||||
};
|
||||
|
||||
static inline bool rcar_du_has(struct rcar_du_device *rcdu,
|
||||
|
@ -16,14 +16,13 @@
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_encoder.h"
|
||||
#include "rcar_du_hdmienc.h"
|
||||
#include "rcar_du_kms.h"
|
||||
#include "rcar_du_lvdscon.h"
|
||||
#include "rcar_du_lvdsenc.h"
|
||||
#include "rcar_du_vgacon.h"
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Encoder
|
||||
@ -33,6 +32,11 @@ static void rcar_du_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
|
||||
|
||||
if (renc->connector && renc->connector->panel) {
|
||||
drm_panel_disable(renc->connector->panel);
|
||||
drm_panel_unprepare(renc->connector->panel);
|
||||
}
|
||||
|
||||
if (renc->lvds)
|
||||
rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false);
|
||||
}
|
||||
@ -43,6 +47,11 @@ static void rcar_du_encoder_enable(struct drm_encoder *encoder)
|
||||
|
||||
if (renc->lvds)
|
||||
rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true);
|
||||
|
||||
if (renc->connector && renc->connector->panel) {
|
||||
drm_panel_prepare(renc->connector->panel);
|
||||
drm_panel_enable(renc->connector->panel);
|
||||
}
|
||||
}
|
||||
|
||||
static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
@ -52,30 +61,36 @@ static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
|
||||
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
|
||||
const struct drm_display_mode *mode = &crtc_state->mode;
|
||||
const struct drm_display_mode *panel_mode;
|
||||
struct drm_connector *connector = conn_state->connector;
|
||||
struct drm_device *dev = encoder->dev;
|
||||
|
||||
/* DAC encoders have currently no restriction on the mode. */
|
||||
if (encoder->encoder_type == DRM_MODE_ENCODER_DAC)
|
||||
return 0;
|
||||
/*
|
||||
* Only panel-related encoder types require validation here, everything
|
||||
* else is handled by the bridge drivers.
|
||||
*/
|
||||
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
|
||||
const struct drm_display_mode *panel_mode;
|
||||
|
||||
if (list_empty(&connector->modes)) {
|
||||
dev_dbg(dev->dev, "encoder: empty modes list\n");
|
||||
return -EINVAL;
|
||||
if (list_empty(&connector->modes)) {
|
||||
dev_dbg(dev->dev, "encoder: empty modes list\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
panel_mode = list_first_entry(&connector->modes,
|
||||
struct drm_display_mode, head);
|
||||
|
||||
/* We're not allowed to modify the resolution. */
|
||||
if (mode->hdisplay != panel_mode->hdisplay ||
|
||||
mode->vdisplay != panel_mode->vdisplay)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The flat panel mode is fixed, just copy it to the adjusted
|
||||
* mode.
|
||||
*/
|
||||
drm_mode_copy(adjusted_mode, panel_mode);
|
||||
}
|
||||
|
||||
panel_mode = list_first_entry(&connector->modes,
|
||||
struct drm_display_mode, head);
|
||||
|
||||
/* We're not allowed to modify the resolution. */
|
||||
if (mode->hdisplay != panel_mode->hdisplay ||
|
||||
mode->vdisplay != panel_mode->vdisplay)
|
||||
return -EINVAL;
|
||||
|
||||
/* The flat panel mode is fixed, just copy it to the adjusted mode. */
|
||||
drm_mode_copy(adjusted_mode, panel_mode);
|
||||
|
||||
if (renc->lvds)
|
||||
rcar_du_lvdsenc_atomic_check(renc->lvds, adjusted_mode);
|
||||
|
||||
@ -83,16 +98,54 @@ static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
}
|
||||
|
||||
static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
|
||||
struct drm_display_info *info = &conn_state->connector->display_info;
|
||||
enum rcar_lvds_mode mode;
|
||||
|
||||
rcar_du_crtc_route_output(encoder->crtc, renc->output);
|
||||
rcar_du_crtc_route_output(crtc_state->crtc, renc->output);
|
||||
|
||||
if (!renc->lvds) {
|
||||
/*
|
||||
* The DU driver creates connectors only for the outputs of the
|
||||
* internal LVDS encoders.
|
||||
*/
|
||||
renc->connector = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
renc->connector = to_rcar_connector(conn_state->connector);
|
||||
|
||||
if (!info->num_bus_formats || !info->bus_formats) {
|
||||
dev_err(encoder->dev->dev, "no LVDS bus format reported\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (info->bus_formats[0]) {
|
||||
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
|
||||
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
|
||||
mode = RCAR_LVDS_MODE_JEIDA;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
|
||||
mode = RCAR_LVDS_MODE_VESA;
|
||||
break;
|
||||
default:
|
||||
dev_err(encoder->dev->dev,
|
||||
"unsupported LVDS bus format 0x%04x\n",
|
||||
info->bus_formats[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB)
|
||||
mode |= RCAR_LVDS_MODE_MIRROR;
|
||||
|
||||
rcar_du_lvdsenc_set_mode(renc->lvds, mode);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
|
||||
.mode_set = rcar_du_encoder_mode_set,
|
||||
.atomic_mode_set = rcar_du_encoder_mode_set,
|
||||
.disable = rcar_du_encoder_disable,
|
||||
.enable = rcar_du_encoder_enable,
|
||||
.atomic_check = rcar_du_encoder_atomic_check,
|
||||
@ -103,14 +156,13 @@ static const struct drm_encoder_funcs encoder_funcs = {
|
||||
};
|
||||
|
||||
int rcar_du_encoder_init(struct rcar_du_device *rcdu,
|
||||
enum rcar_du_encoder_type type,
|
||||
enum rcar_du_output output,
|
||||
struct device_node *enc_node,
|
||||
struct device_node *con_node)
|
||||
{
|
||||
struct rcar_du_encoder *renc;
|
||||
struct drm_encoder *encoder;
|
||||
unsigned int encoder_type;
|
||||
struct drm_bridge *bridge = NULL;
|
||||
int ret;
|
||||
|
||||
renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
|
||||
@ -133,52 +185,51 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
|
||||
break;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case RCAR_DU_ENCODER_VGA:
|
||||
encoder_type = DRM_MODE_ENCODER_DAC;
|
||||
break;
|
||||
case RCAR_DU_ENCODER_LVDS:
|
||||
encoder_type = DRM_MODE_ENCODER_LVDS;
|
||||
break;
|
||||
case RCAR_DU_ENCODER_HDMI:
|
||||
encoder_type = DRM_MODE_ENCODER_TMDS;
|
||||
break;
|
||||
case RCAR_DU_ENCODER_NONE:
|
||||
default:
|
||||
/* No external encoder, use the internal encoder type. */
|
||||
encoder_type = rcdu->info->routes[output].encoder_type;
|
||||
break;
|
||||
}
|
||||
if (enc_node) {
|
||||
dev_dbg(rcdu->dev, "initializing encoder %s for output %u\n",
|
||||
of_node_full_name(enc_node), output);
|
||||
|
||||
if (type == RCAR_DU_ENCODER_HDMI) {
|
||||
ret = rcar_du_hdmienc_init(rcdu, renc, enc_node);
|
||||
if (ret < 0)
|
||||
/* Locate the DRM bridge from the encoder DT node. */
|
||||
bridge = of_drm_find_bridge(enc_node);
|
||||
if (!bridge) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
|
||||
encoder_type, NULL);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
drm_encoder_helper_add(encoder, &encoder_helper_funcs);
|
||||
dev_dbg(rcdu->dev,
|
||||
"initializing internal encoder for output %u\n",
|
||||
output);
|
||||
}
|
||||
|
||||
switch (encoder_type) {
|
||||
case DRM_MODE_ENCODER_LVDS:
|
||||
ret = rcar_du_lvds_connector_init(rcdu, renc, con_node);
|
||||
break;
|
||||
ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
|
||||
DRM_MODE_ENCODER_NONE, NULL);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
case DRM_MODE_ENCODER_DAC:
|
||||
ret = rcar_du_vga_connector_init(rcdu, renc);
|
||||
break;
|
||||
drm_encoder_helper_add(encoder, &encoder_helper_funcs);
|
||||
|
||||
case DRM_MODE_ENCODER_TMDS:
|
||||
/* connector managed by the bridge driver */
|
||||
break;
|
||||
if (bridge) {
|
||||
/*
|
||||
* Attach the bridge to the encoder. The bridge will create the
|
||||
* connector.
|
||||
*/
|
||||
ret = drm_bridge_attach(encoder, bridge, NULL);
|
||||
if (ret) {
|
||||
drm_encoder_cleanup(encoder);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
/* There's no bridge, create the connector manually. */
|
||||
switch (output) {
|
||||
case RCAR_DU_OUTPUT_LVDS0:
|
||||
case RCAR_DU_OUTPUT_LVDS1:
|
||||
ret = rcar_du_lvds_connector_init(rcdu, renc, con_node);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -17,22 +17,14 @@
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
|
||||
struct drm_panel;
|
||||
struct rcar_du_device;
|
||||
struct rcar_du_hdmienc;
|
||||
struct rcar_du_lvdsenc;
|
||||
|
||||
enum rcar_du_encoder_type {
|
||||
RCAR_DU_ENCODER_UNUSED = 0,
|
||||
RCAR_DU_ENCODER_NONE,
|
||||
RCAR_DU_ENCODER_VGA,
|
||||
RCAR_DU_ENCODER_LVDS,
|
||||
RCAR_DU_ENCODER_HDMI,
|
||||
};
|
||||
|
||||
struct rcar_du_encoder {
|
||||
struct drm_encoder base;
|
||||
enum rcar_du_output output;
|
||||
struct rcar_du_hdmienc *hdmi;
|
||||
struct rcar_du_connector *connector;
|
||||
struct rcar_du_lvdsenc *lvds;
|
||||
};
|
||||
|
||||
@ -44,13 +36,13 @@ struct rcar_du_encoder {
|
||||
struct rcar_du_connector {
|
||||
struct drm_connector connector;
|
||||
struct rcar_du_encoder *encoder;
|
||||
struct drm_panel *panel;
|
||||
};
|
||||
|
||||
#define to_rcar_connector(c) \
|
||||
container_of(c, struct rcar_du_connector, connector)
|
||||
|
||||
int rcar_du_encoder_init(struct rcar_du_device *rcdu,
|
||||
enum rcar_du_encoder_type type,
|
||||
enum rcar_du_output output,
|
||||
struct device_node *enc_node,
|
||||
struct device_node *con_node);
|
||||
|
@ -1,134 +0,0 @@
|
||||
/*
|
||||
* R-Car Display Unit HDMI Encoder
|
||||
*
|
||||
* Copyright (C) 2014 Renesas Electronics Corporation
|
||||
*
|
||||
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_encoder.h"
|
||||
#include "rcar_du_hdmienc.h"
|
||||
#include "rcar_du_lvdsenc.h"
|
||||
|
||||
struct rcar_du_hdmienc {
|
||||
struct rcar_du_encoder *renc;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
#define to_rcar_hdmienc(e) (to_rcar_encoder(e)->hdmi)
|
||||
|
||||
static void rcar_du_hdmienc_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
|
||||
|
||||
if (hdmienc->renc->lvds)
|
||||
rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
|
||||
false);
|
||||
|
||||
hdmienc->enabled = false;
|
||||
}
|
||||
|
||||
static void rcar_du_hdmienc_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
|
||||
|
||||
if (hdmienc->renc->lvds)
|
||||
rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
|
||||
true);
|
||||
|
||||
hdmienc->enabled = true;
|
||||
}
|
||||
|
||||
static int rcar_du_hdmienc_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
|
||||
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
|
||||
|
||||
if (hdmienc->renc->lvds)
|
||||
rcar_du_lvdsenc_atomic_check(hdmienc->renc->lvds,
|
||||
adjusted_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
|
||||
|
||||
rcar_du_crtc_route_output(encoder->crtc, hdmienc->renc->output);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
|
||||
.mode_set = rcar_du_hdmienc_mode_set,
|
||||
.disable = rcar_du_hdmienc_disable,
|
||||
.enable = rcar_du_hdmienc_enable,
|
||||
.atomic_check = rcar_du_hdmienc_atomic_check,
|
||||
};
|
||||
|
||||
static void rcar_du_hdmienc_cleanup(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
|
||||
|
||||
if (hdmienc->enabled)
|
||||
rcar_du_hdmienc_disable(encoder);
|
||||
|
||||
drm_encoder_cleanup(encoder);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs encoder_funcs = {
|
||||
.destroy = rcar_du_hdmienc_cleanup,
|
||||
};
|
||||
|
||||
int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc, struct device_node *np)
|
||||
{
|
||||
struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
|
||||
struct drm_bridge *bridge;
|
||||
struct rcar_du_hdmienc *hdmienc;
|
||||
int ret;
|
||||
|
||||
hdmienc = devm_kzalloc(rcdu->dev, sizeof(*hdmienc), GFP_KERNEL);
|
||||
if (hdmienc == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Locate the DRM bridge from the HDMI encoder DT node. */
|
||||
bridge = of_drm_find_bridge(np);
|
||||
if (!bridge)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
|
||||
DRM_MODE_ENCODER_TMDS, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drm_encoder_helper_add(encoder, &encoder_helper_funcs);
|
||||
|
||||
renc->hdmi = hdmienc;
|
||||
hdmienc->renc = renc;
|
||||
|
||||
/* Link the bridge to the encoder. */
|
||||
ret = drm_bridge_attach(encoder, bridge, NULL);
|
||||
if (ret) {
|
||||
drm_encoder_cleanup(encoder);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* R-Car Display Unit HDMI Encoder
|
||||
*
|
||||
* Copyright (C) 2014 Renesas Electronics Corporation
|
||||
*
|
||||
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __RCAR_DU_HDMIENC_H__
|
||||
#define __RCAR_DU_HDMIENC_H__
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
struct device_node;
|
||||
struct rcar_du_device;
|
||||
struct rcar_du_encoder;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_RCAR_HDMI)
|
||||
int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc, struct device_node *np);
|
||||
#else
|
||||
static inline int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc,
|
||||
struct device_node *np)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __RCAR_DU_HDMIENC_H__ */
|
@ -249,18 +249,9 @@ static int rcar_du_atomic_check(struct drm_device *dev,
|
||||
return rcar_du_atomic_check_planes(dev, state);
|
||||
}
|
||||
|
||||
struct rcar_du_commit {
|
||||
struct work_struct work;
|
||||
struct drm_device *dev;
|
||||
struct drm_atomic_state *state;
|
||||
u32 crtcs;
|
||||
};
|
||||
|
||||
static void rcar_du_atomic_complete(struct rcar_du_commit *commit)
|
||||
static void rcar_du_atomic_commit_tail(struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct drm_device *dev = commit->dev;
|
||||
struct rcar_du_device *rcdu = dev->dev_private;
|
||||
struct drm_atomic_state *old_state = commit->state;
|
||||
struct drm_device *dev = old_state->dev;
|
||||
|
||||
/* Apply the atomic update. */
|
||||
drm_atomic_helper_commit_modeset_disables(dev, old_state);
|
||||
@ -268,114 +259,31 @@ static void rcar_du_atomic_complete(struct rcar_du_commit *commit)
|
||||
drm_atomic_helper_commit_planes(dev, old_state,
|
||||
DRM_PLANE_COMMIT_ACTIVE_ONLY);
|
||||
|
||||
drm_atomic_helper_commit_hw_done(old_state);
|
||||
drm_atomic_helper_wait_for_vblanks(dev, old_state);
|
||||
|
||||
drm_atomic_helper_cleanup_planes(dev, old_state);
|
||||
|
||||
drm_atomic_state_put(old_state);
|
||||
|
||||
/* Complete the commit, wake up any waiter. */
|
||||
spin_lock(&rcdu->commit.wait.lock);
|
||||
rcdu->commit.pending &= ~commit->crtcs;
|
||||
wake_up_all_locked(&rcdu->commit.wait);
|
||||
spin_unlock(&rcdu->commit.wait.lock);
|
||||
|
||||
kfree(commit);
|
||||
}
|
||||
|
||||
static void rcar_du_atomic_work(struct work_struct *work)
|
||||
{
|
||||
struct rcar_du_commit *commit =
|
||||
container_of(work, struct rcar_du_commit, work);
|
||||
|
||||
rcar_du_atomic_complete(commit);
|
||||
}
|
||||
|
||||
static int rcar_du_atomic_commit(struct drm_device *dev,
|
||||
struct drm_atomic_state *state,
|
||||
bool nonblock)
|
||||
{
|
||||
struct rcar_du_device *rcdu = dev->dev_private;
|
||||
struct rcar_du_commit *commit;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
ret = drm_atomic_helper_prepare_planes(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Allocate the commit object. */
|
||||
commit = kzalloc(sizeof(*commit), GFP_KERNEL);
|
||||
if (commit == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
INIT_WORK(&commit->work, rcar_du_atomic_work);
|
||||
commit->dev = dev;
|
||||
commit->state = state;
|
||||
|
||||
/* Wait until all affected CRTCs have completed previous commits and
|
||||
* mark them as pending.
|
||||
*/
|
||||
for_each_crtc_in_state(state, crtc, crtc_state, i)
|
||||
commit->crtcs |= drm_crtc_mask(crtc);
|
||||
|
||||
spin_lock(&rcdu->commit.wait.lock);
|
||||
ret = wait_event_interruptible_locked(rcdu->commit.wait,
|
||||
!(rcdu->commit.pending & commit->crtcs));
|
||||
if (ret == 0)
|
||||
rcdu->commit.pending |= commit->crtcs;
|
||||
spin_unlock(&rcdu->commit.wait.lock);
|
||||
|
||||
if (ret) {
|
||||
kfree(commit);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Swap the state, this is the point of no return. */
|
||||
drm_atomic_helper_swap_state(state, true);
|
||||
|
||||
drm_atomic_state_get(state);
|
||||
if (nonblock)
|
||||
schedule_work(&commit->work);
|
||||
else
|
||||
rcar_du_atomic_complete(commit);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
drm_atomic_helper_cleanup_planes(dev, state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
|
||||
static const struct drm_mode_config_helper_funcs rcar_du_mode_config_helper = {
|
||||
.atomic_commit_tail = rcar_du_atomic_commit_tail,
|
||||
};
|
||||
|
||||
static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
|
||||
.fb_create = rcar_du_fb_create,
|
||||
.output_poll_changed = rcar_du_output_poll_changed,
|
||||
.atomic_check = rcar_du_atomic_check,
|
||||
.atomic_commit = rcar_du_atomic_commit,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
|
||||
enum rcar_du_output output,
|
||||
struct of_endpoint *ep)
|
||||
{
|
||||
static const struct {
|
||||
const char *compatible;
|
||||
enum rcar_du_encoder_type type;
|
||||
} encoders[] = {
|
||||
{ "adi,adv7123", RCAR_DU_ENCODER_VGA },
|
||||
{ "adi,adv7511w", RCAR_DU_ENCODER_HDMI },
|
||||
{ "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS },
|
||||
};
|
||||
|
||||
enum rcar_du_encoder_type enc_type = RCAR_DU_ENCODER_NONE;
|
||||
struct device_node *connector = NULL;
|
||||
struct device_node *encoder = NULL;
|
||||
struct device_node *ep_node = NULL;
|
||||
@ -394,6 +302,13 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!of_device_is_available(entity)) {
|
||||
dev_dbg(rcdu->dev,
|
||||
"connected entity %s is disabled, skipping\n",
|
||||
entity->full_name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
entity_ep_node = of_parse_phandle(ep->local_node, "remote-endpoint", 0);
|
||||
|
||||
for_each_endpoint_of_node(entity, ep_node) {
|
||||
@ -422,30 +337,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
|
||||
|
||||
of_node_put(entity_ep_node);
|
||||
|
||||
if (encoder) {
|
||||
/*
|
||||
* If an encoder has been found, get its type based on its
|
||||
* compatible string.
|
||||
*/
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(encoders); ++i) {
|
||||
if (of_device_is_compatible(encoder,
|
||||
encoders[i].compatible)) {
|
||||
enc_type = encoders[i].type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(encoders)) {
|
||||
dev_warn(rcdu->dev,
|
||||
"unknown encoder type for %s, skipping\n",
|
||||
encoder->full_name);
|
||||
of_node_put(encoder);
|
||||
of_node_put(connector);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
if (!encoder) {
|
||||
/*
|
||||
* If no encoder has been found the entity must be the
|
||||
* connector.
|
||||
@ -453,7 +345,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
|
||||
connector = entity;
|
||||
}
|
||||
|
||||
ret = rcar_du_encoder_init(rcdu, enc_type, output, encoder, connector);
|
||||
ret = rcar_du_encoder_init(rcdu, output, encoder, connector);
|
||||
if (ret && ret != -EPROBE_DEFER)
|
||||
dev_warn(rcdu->dev,
|
||||
"failed to initialize encoder %s on output %u (%d), skipping\n",
|
||||
@ -561,6 +453,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
|
||||
dev->mode_config.max_width = 4095;
|
||||
dev->mode_config.max_height = 2047;
|
||||
dev->mode_config.funcs = &rcar_du_mode_config_funcs;
|
||||
dev->mode_config.helper_private = &rcar_du_mode_config_helper;
|
||||
|
||||
rcdu->num_crtcs = rcdu->info->num_crtcs;
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include <video/display_timing.h>
|
||||
#include <video/of_display_timing.h>
|
||||
@ -25,47 +26,30 @@
|
||||
#include "rcar_du_kms.h"
|
||||
#include "rcar_du_lvdscon.h"
|
||||
|
||||
struct rcar_du_lvds_connector {
|
||||
struct rcar_du_connector connector;
|
||||
|
||||
struct {
|
||||
unsigned int width_mm; /* Panel width in mm */
|
||||
unsigned int height_mm; /* Panel height in mm */
|
||||
struct videomode mode;
|
||||
} panel;
|
||||
};
|
||||
|
||||
#define to_rcar_lvds_connector(c) \
|
||||
container_of(c, struct rcar_du_lvds_connector, connector.connector)
|
||||
|
||||
static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct rcar_du_lvds_connector *lvdscon =
|
||||
to_rcar_lvds_connector(connector);
|
||||
struct drm_display_mode *mode;
|
||||
struct rcar_du_connector *rcon = to_rcar_connector(connector);
|
||||
|
||||
mode = drm_mode_create(connector->dev);
|
||||
if (mode == NULL)
|
||||
return 0;
|
||||
|
||||
mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
|
||||
|
||||
drm_display_mode_from_videomode(&lvdscon->panel.mode, mode);
|
||||
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
return 1;
|
||||
return drm_panel_get_modes(rcon->panel);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs connector_helper_funcs = {
|
||||
.get_modes = rcar_du_lvds_connector_get_modes,
|
||||
};
|
||||
|
||||
static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct rcar_du_connector *rcon = to_rcar_connector(connector);
|
||||
|
||||
drm_panel_detach(rcon->panel);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.destroy = rcar_du_lvds_connector_destroy,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
@ -75,27 +59,19 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
|
||||
const struct device_node *np)
|
||||
{
|
||||
struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
|
||||
struct rcar_du_lvds_connector *lvdscon;
|
||||
struct rcar_du_connector *rcon;
|
||||
struct drm_connector *connector;
|
||||
struct display_timing timing;
|
||||
int ret;
|
||||
|
||||
lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL);
|
||||
if (lvdscon == NULL)
|
||||
rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
|
||||
if (rcon == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_get_display_timing(np, "panel-timing", &timing);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
connector = &rcon->connector;
|
||||
|
||||
videomode_from_timing(&timing, &lvdscon->panel.mode);
|
||||
|
||||
of_property_read_u32(np, "width-mm", &lvdscon->panel.width_mm);
|
||||
of_property_read_u32(np, "height-mm", &lvdscon->panel.height_mm);
|
||||
|
||||
connector = &lvdscon->connector.connector;
|
||||
connector->display_info.width_mm = lvdscon->panel.width_mm;
|
||||
connector->display_info.height_mm = lvdscon->panel.height_mm;
|
||||
rcon->panel = of_drm_find_panel(np);
|
||||
if (!rcon->panel)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
@ -112,7 +88,11 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
lvdscon->connector.encoder = renc;
|
||||
ret = drm_panel_attach(rcon->panel, connector);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
rcon->encoder = renc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ struct rcar_du_lvdsenc {
|
||||
bool enabled;
|
||||
|
||||
enum rcar_lvds_input input;
|
||||
enum rcar_lvds_mode mode;
|
||||
};
|
||||
|
||||
static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data)
|
||||
@ -61,7 +62,7 @@ static void rcar_du_lvdsenc_start_gen2(struct rcar_du_lvdsenc *lvds,
|
||||
/* Select the input, hardcode mode 0, enable LVDS operation and turn
|
||||
* bias circuitry on.
|
||||
*/
|
||||
lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN;
|
||||
lvdcr0 = (lvds->mode << LVDCR0_LVMD_SHIFT) | LVDCR0_BEN | LVDCR0_LVEN;
|
||||
if (rcrtc->index == 2)
|
||||
lvdcr0 |= LVDCR0_DUSEL;
|
||||
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
|
||||
@ -114,7 +115,7 @@ static void rcar_du_lvdsenc_start_gen3(struct rcar_du_lvdsenc *lvds,
|
||||
* Turn the PLL on, set it to LVDS normal mode, wait for the startup
|
||||
* delay and turn the output on.
|
||||
*/
|
||||
lvdcr0 = LVDCR0_PLLON;
|
||||
lvdcr0 = (lvds->mode << LVDCR0_LVMD_SHIFT) | LVDCR0_PLLON;
|
||||
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
|
||||
|
||||
lvdcr0 |= LVDCR0_PWD;
|
||||
@ -211,6 +212,12 @@ void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
|
||||
mode->clock = clamp(mode->clock, 25175, 148500);
|
||||
}
|
||||
|
||||
void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds,
|
||||
enum rcar_lvds_mode mode)
|
||||
{
|
||||
lvds->mode = mode;
|
||||
}
|
||||
|
||||
static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
|
@ -26,8 +26,17 @@ enum rcar_lvds_input {
|
||||
RCAR_LVDS_INPUT_DU2,
|
||||
};
|
||||
|
||||
/* Keep in sync with the LVDCR0.LVMD hardware register values. */
|
||||
enum rcar_lvds_mode {
|
||||
RCAR_LVDS_MODE_JEIDA = 0,
|
||||
RCAR_LVDS_MODE_MIRROR = 1,
|
||||
RCAR_LVDS_MODE_VESA = 4,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
|
||||
int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu);
|
||||
void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds,
|
||||
enum rcar_lvds_mode mode);
|
||||
int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
|
||||
struct drm_crtc *crtc, bool enable);
|
||||
void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
|
||||
@ -37,6 +46,10 @@ static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds,
|
||||
enum rcar_lvds_mode mode)
|
||||
{
|
||||
}
|
||||
static inline int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
|
||||
struct drm_crtc *crtc, bool enable)
|
||||
{
|
||||
|
@ -277,6 +277,29 @@
|
||||
#define DEFR10_TSEL_H3_TCON1 (0 << 1) /* DEFR102 register only (DU2/DU3) */
|
||||
#define DEFR10_DEFE10 (1 << 0)
|
||||
|
||||
#define DPLLCR 0x20044
|
||||
#define DPLLCR_CODE (0x95 << 24)
|
||||
#define DPLLCR_PLCS1 (1 << 23)
|
||||
/*
|
||||
* PLCS0 is bit 21, but H3 ES1.x requires bit 20 to be set as well. As bit 20
|
||||
* isn't implemented by other SoC in the Gen3 family it can safely be set
|
||||
* unconditionally.
|
||||
*/
|
||||
#define DPLLCR_PLCS0 (3 << 20)
|
||||
#define DPLLCR_CLKE (1 << 18)
|
||||
#define DPLLCR_FDPLL(n) ((n) << 12)
|
||||
#define DPLLCR_N(n) ((n) << 5)
|
||||
#define DPLLCR_M(n) ((n) << 3)
|
||||
#define DPLLCR_STBY (1 << 2)
|
||||
#define DPLLCR_INCS_DOTCLKIN0 (0 << 0)
|
||||
#define DPLLCR_INCS_DOTCLKIN1 (1 << 1)
|
||||
|
||||
#define DPLLC2R 0x20048
|
||||
#define DPLLC2R_CODE (0x95 << 24)
|
||||
#define DPLLC2R_SELC (1 << 12)
|
||||
#define DPLLC2R_M(n) ((n) << 8)
|
||||
#define DPLLC2R_FDPLL(n) ((n) << 0)
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Display Timing Generation Registers
|
||||
*/
|
||||
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
* rcar_du_vgacon.c -- R-Car Display Unit VGA Connector
|
||||
*
|
||||
* Copyright (C) 2013-2014 Renesas Electronics Corporation
|
||||
*
|
||||
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_encoder.h"
|
||||
#include "rcar_du_kms.h"
|
||||
#include "rcar_du_vgacon.h"
|
||||
|
||||
static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs connector_helper_funcs = {
|
||||
.get_modes = rcar_du_vga_connector_get_modes,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.detect = rcar_du_vga_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc)
|
||||
{
|
||||
struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
|
||||
struct rcar_du_connector *rcon;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
|
||||
rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
|
||||
if (rcon == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
connector = &rcon->connector;
|
||||
connector->display_info.width_mm = 0;
|
||||
connector->display_info.height_mm = 0;
|
||||
connector->interlace_allowed = true;
|
||||
|
||||
ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
|
||||
DRM_MODE_CONNECTOR_VGA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drm_connector_helper_add(connector, &connector_helper_funcs);
|
||||
|
||||
connector->dpms = DRM_MODE_DPMS_OFF;
|
||||
drm_object_property_set_value(&connector->base,
|
||||
rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
|
||||
|
||||
ret = drm_mode_connector_attach_encoder(connector, encoder);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* rcar_du_vgacon.h -- R-Car Display Unit VGA Connector
|
||||
*
|
||||
* Copyright (C) 2013-2014 Renesas Electronics Corporation
|
||||
*
|
||||
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __RCAR_DU_VGACON_H__
|
||||
#define __RCAR_DU_VGACON_H__
|
||||
|
||||
struct rcar_du_device;
|
||||
struct rcar_du_encoder;
|
||||
|
||||
int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc);
|
||||
|
||||
#endif /* __RCAR_DU_VGACON_H__ */
|
@ -68,7 +68,7 @@ void rcar_du_vsp_disable(struct rcar_du_crtc *crtc);
|
||||
void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc);
|
||||
void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc);
|
||||
#else
|
||||
static inline int rcar_du_vsp_init(struct rcar_du_vsp *vsp) { return 0; };
|
||||
static inline int rcar_du_vsp_init(struct rcar_du_vsp *vsp) { return -ENXIO; };
|
||||
static inline void rcar_du_vsp_enable(struct rcar_du_crtc *crtc) { };
|
||||
static inline void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) { };
|
||||
static inline void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) { };
|
||||
|
100
drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c
Normal file
100
drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* R-Car Gen3 HDMI PHY
|
||||
*
|
||||
* Copyright (C) 2016 Renesas Electronics Corporation
|
||||
*
|
||||
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <drm/bridge/dw_hdmi.h>
|
||||
|
||||
#define RCAR_HDMI_PHY_OPMODE_PLLCFG 0x06 /* Mode of operation and PLL dividers */
|
||||
#define RCAR_HDMI_PHY_PLLCURRGMPCTRL 0x10 /* PLL current and Gmp (conductance) */
|
||||
#define RCAR_HDMI_PHY_PLLDIVCTRL 0x11 /* PLL dividers */
|
||||
|
||||
struct rcar_hdmi_phy_params {
|
||||
unsigned long mpixelclock;
|
||||
u16 opmode_div; /* Mode of operation and PLL dividers */
|
||||
u16 curr_gmp; /* PLL current and Gmp (conductance) */
|
||||
u16 div; /* PLL dividers */
|
||||
};
|
||||
|
||||
static const struct rcar_hdmi_phy_params rcar_hdmi_phy_params[] = {
|
||||
{ 35500000, 0x0003, 0x0344, 0x0328 },
|
||||
{ 44900000, 0x0003, 0x0285, 0x0128 },
|
||||
{ 71000000, 0x0002, 0x1184, 0x0314 },
|
||||
{ 90000000, 0x0002, 0x1144, 0x0114 },
|
||||
{ 140250000, 0x0001, 0x20c4, 0x030a },
|
||||
{ 182750000, 0x0001, 0x2084, 0x010a },
|
||||
{ 281250000, 0x0000, 0x0084, 0x0305 },
|
||||
{ 297000000, 0x0000, 0x0084, 0x0105 },
|
||||
{ ~0UL, 0x0000, 0x0000, 0x0000 },
|
||||
};
|
||||
|
||||
static int rcar_hdmi_phy_configure(struct dw_hdmi *hdmi,
|
||||
const struct dw_hdmi_plat_data *pdata,
|
||||
unsigned long mpixelclock)
|
||||
{
|
||||
const struct rcar_hdmi_phy_params *params = rcar_hdmi_phy_params;
|
||||
|
||||
for (; params && params->mpixelclock != ~0UL; ++params) {
|
||||
if (mpixelclock <= params->mpixelclock)
|
||||
break;
|
||||
}
|
||||
|
||||
if (params->mpixelclock == ~0UL)
|
||||
return -EINVAL;
|
||||
|
||||
dw_hdmi_phy_i2c_write(hdmi, params->opmode_div,
|
||||
RCAR_HDMI_PHY_OPMODE_PLLCFG);
|
||||
dw_hdmi_phy_i2c_write(hdmi, params->curr_gmp,
|
||||
RCAR_HDMI_PHY_PLLCURRGMPCTRL);
|
||||
dw_hdmi_phy_i2c_write(hdmi, params->div, RCAR_HDMI_PHY_PLLDIVCTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dw_hdmi_plat_data rcar_dw_hdmi_plat_data = {
|
||||
.configure_phy = rcar_hdmi_phy_configure,
|
||||
};
|
||||
|
||||
static int rcar_dw_hdmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
return dw_hdmi_probe(pdev, &rcar_dw_hdmi_plat_data);
|
||||
}
|
||||
|
||||
static int rcar_dw_hdmi_remove(struct platform_device *pdev)
|
||||
{
|
||||
dw_hdmi_remove(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rcar_dw_hdmi_of_table[] = {
|
||||
{ .compatible = "renesas,rcar-gen3-hdmi" },
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_dw_hdmi_of_table);
|
||||
|
||||
static struct platform_driver rcar_dw_hdmi_platform_driver = {
|
||||
.probe = rcar_dw_hdmi_probe,
|
||||
.remove = rcar_dw_hdmi_remove,
|
||||
.driver = {
|
||||
.name = "rcar-dw-hdmi",
|
||||
.of_match_table = rcar_dw_hdmi_of_table,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(rcar_dw_hdmi_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
|
||||
MODULE_DESCRIPTION("Renesas R-Car Gen3 HDMI Encoder Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -224,6 +224,10 @@ struct drm_display_info {
|
||||
#define DRM_BUS_FLAG_PIXDATA_POSEDGE (1<<2)
|
||||
/* drive data on neg. edge */
|
||||
#define DRM_BUS_FLAG_PIXDATA_NEGEDGE (1<<3)
|
||||
/* data is transmitted MSB to LSB on the bus */
|
||||
#define DRM_BUS_FLAG_DATA_MSB_TO_LSB (1<<4)
|
||||
/* data is transmitted LSB to MSB on the bus */
|
||||
#define DRM_BUS_FLAG_DATA_LSB_TO_MSB (1<<5)
|
||||
|
||||
/**
|
||||
* @bus_flags: Additional information (like pixel signal polarity) for
|
||||
|
Loading…
Reference in New Issue
Block a user