1
0
Fork 0

drm-misc-next for $kernel-version:

UAPI Changes:
 
 Cross-subsystem Changes:
 
 Core Changes:
   - bridge: huge rework to get rid of omap_dss custom display drivers
 
 Driver Changes:
   - hisilicon: some fixes related to modes it can deal with / default to
   - virtio: shmem and gpu context fixes and enhancements
   - sun4i: Support for LVDS on the A33
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQRcEzekXsqa64kGDp7j7w1vZxhRxQUCXleoiAAKCRDj7w1vZxhR
 xbT4AP4zzM/NP++JUReg/kevOHLPHdPH4sg1+EtC0F4nJ25MEgD/UqYafdB/DN+Y
 GXCPiYyYbJ8HpCOMGZiLgPHGa37xJAA=
 =6VIX
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2020-02-27' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for 5.7

UAPI Changes:

Cross-subsystem Changes:

Core Changes:
  - bridge: huge rework to get rid of omap_dss custom display drivers

Driver Changes:
  - hisilicon: some fixes related to modes it can deal with / default to
  - virtio: shmem and gpu context fixes and enhancements
  - sun4i: Support for LVDS on the A33

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maxime Ripard <maxime@cerno.tech>
Link: https://patchwork.freedesktop.org/patch/msgid/20200227113222.cdwzy4cvcqjtbmou@gilmour.lan
alistair/sensors
Dave Airlie 2020-02-28 16:21:14 +10:00
commit 60347451dd
128 changed files with 3474 additions and 2595 deletions

View File

@ -139,11 +139,17 @@ Overview
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: overview
Default bridge callback sequence
--------------------------------
Bridge Operations
-----------------
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: bridge callbacks
:doc: bridge operations
Bridge Connector Helper
-----------------------
.. kernel-doc:: drivers/gpu/drm/drm_bridge_connector.c
:doc: overview
Bridge Helper Reference
@ -155,6 +161,12 @@ Bridge Helper Reference
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:export:
Bridge Connector Helper Reference
---------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_bridge_connector.c
:export:
Panel-Bridge Helper Reference
-----------------------------

View File

@ -407,6 +407,20 @@ Contact: Daniel Vetter
Level: Intermediate
Replace drm_detect_hdmi_monitor() with drm_display_info.is_hdmi
---------------------------------------------------------------
Once EDID is parsed, the monitor HDMI support information is available through
drm_display_info.is_hdmi. Many drivers still call drm_detect_hdmi_monitor() to
retrieve the same information, which is less efficient.
Audit each individual driver calling drm_detect_hdmi_monitor() and switch to
drm_display_info.is_hdmi if applicable.
Contact: Laurent Pinchart, respective driver maintainers
Level: Intermediate
Core refactorings
=================

View File

@ -5600,12 +5600,13 @@ S: Maintained
F: drivers/gpu/drm/gma500/
DRM DRIVERS FOR HISILICON
M: Xinliang Liu <z.liuxinliang@hisilicon.com>
M: Xinliang Liu <xinliang.liu@linaro.org>
M: Rongrong Zou <zourongrong@gmail.com>
R: John Stultz <john.stultz@linaro.org>
R: Xinwei Kong <kong.kongxinwei@hisilicon.com>
R: Chen Feng <puck.chen@hisilicon.com>
L: dri-devel@lists.freedesktop.org
T: git git://github.com/xin3liang/linux.git
T: git git://anongit.freedesktop.org/drm/drm-misc
S: Maintained
F: drivers/gpu/drm/hisilicon/
F: Documentation/devicetree/bindings/display/hisilicon/

View File

@ -158,7 +158,7 @@ CONFIG_VIDEO_TVP514X=m
CONFIG_VIDEO_ADV7343=m
CONFIG_DRM=m
CONFIG_DRM_TILCDC=m
CONFIG_DRM_DUMB_VGA_DAC=m
CONFIG_DRM_SIMPLE_BRIDGE=m
CONFIG_DRM_TINYDRM=m
CONFIG_TINYDRM_ST7586=m
CONFIG_FB=y

View File

@ -55,7 +55,7 @@ CONFIG_SMC91X=y
# CONFIG_KEYBOARD_ATKBD is not set
# CONFIG_SERIO_SERPORT is not set
CONFIG_DRM=y
CONFIG_DRM_DUMB_VGA_DAC=y
CONFIG_DRM_SIMPLE_BRIDGE=y
CONFIG_DRM_PL111=y
CONFIG_FB_MODE_HELPERS=y
CONFIG_FB_MATROX=y

View File

@ -670,11 +670,11 @@ CONFIG_DRM_PANEL_ORISETECH_OTM8009A=m
CONFIG_DRM_PANEL_RAYDIUM_RM68200=m
CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03=m
CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0=m
CONFIG_DRM_DUMB_VGA_DAC=m
CONFIG_DRM_NXP_PTN3460=m
CONFIG_DRM_PARADE_PS8622=m
CONFIG_DRM_SII902X=m
CONFIG_DRM_SII9234=m
CONFIG_DRM_SIMPLE_BRIDGE=m
CONFIG_DRM_TOSHIBA_TC358764=m
CONFIG_DRM_I2C_ADV7511=m
CONFIG_DRM_I2C_ADV7511_AUDIO=y

View File

@ -350,14 +350,13 @@ CONFIG_DRM_OMAP=m
CONFIG_OMAP5_DSS_HDMI=y
CONFIG_OMAP2_DSS_SDI=y
CONFIG_OMAP2_DSS_DSI=y
CONFIG_DRM_OMAP_ENCODER_OPA362=m
CONFIG_DRM_OMAP_ENCODER_TPD12S015=m
CONFIG_DRM_OMAP_CONNECTOR_HDMI=m
CONFIG_DRM_OMAP_CONNECTOR_ANALOG_TV=m
CONFIG_DRM_OMAP_PANEL_DSI_CM=m
CONFIG_DRM_TILCDC=m
CONFIG_DRM_PANEL_SIMPLE=m
CONFIG_DRM_DISPLAY_CONNECTOR=m
CONFIG_DRM_SIMPLE_BRIDGE=m
CONFIG_DRM_TI_TFP410=m
CONFIG_DRM_TI_TPD12S015=m
CONFIG_DRM_PANEL_LG_LB035Q02=m
CONFIG_DRM_PANEL_NEC_NL8048HL11=m
CONFIG_DRM_PANEL_SHARP_LS037V7DW01=m

View File

@ -125,9 +125,9 @@ CONFIG_VIDEO_ML86V7667=y
CONFIG_DRM=y
CONFIG_DRM_RCAR_DU=y
CONFIG_DRM_PANEL_SIMPLE=y
CONFIG_DRM_DUMB_VGA_DAC=y
CONFIG_DRM_LVDS_CODEC=y
CONFIG_DRM_SII902X=y
CONFIG_DRM_SIMPLE_BRIDGE=y
CONFIG_DRM_I2C_ADV7511=y
CONFIG_DRM_I2C_ADV7511_AUDIO=y
CONFIG_FB_SH_MOBILE_LCDC=y

View File

@ -101,7 +101,7 @@ CONFIG_RC_DEVICES=y
CONFIG_IR_SUNXI=y
CONFIG_DRM=y
CONFIG_DRM_SUN4I=y
CONFIG_DRM_DUMB_VGA_DAC=y
CONFIG_DRM_SIMPLE_BRIDGE=y
CONFIG_FB_SIMPLE=y
CONFIG_SOUND=y
CONFIG_SND=y

View File

@ -59,7 +59,7 @@ CONFIG_GPIO_PL061=y
CONFIG_DRM=y
CONFIG_DRM_PANEL_ARM_VERSATILE=y
CONFIG_DRM_PANEL_SIMPLE=y
CONFIG_DRM_DUMB_VGA_DAC=y
CONFIG_DRM_SIMPLE_BRIDGE=y
CONFIG_DRM_PL111=y
CONFIG_FB_MODE_HELPERS=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y

View File

@ -39,7 +39,8 @@ obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o
drm_ttm_helper-y := drm_gem_ttm_helper.o
obj-$(CONFIG_DRM_TTM_HELPER) += drm_ttm_helper.o
drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_dsc.o drm_probe_helper.o \
drm_kms_helper-y := drm_bridge_connector.o drm_crtc_helper.o drm_dp_helper.o \
drm_dsc.o drm_probe_helper.o \
drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
drm_kms_helper_common.o drm_dp_dual_mode_helper.o \
drm_simple_kms_helper.o drm_modeset_helper.o \

View File

@ -40,7 +40,7 @@ int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np)
return ret;
/* Link drm_bridge to encoder */
ret = drm_bridge_attach(encoder, bridge, NULL);
ret = drm_bridge_attach(encoder, bridge, NULL, 0);
if (ret)
drm_encoder_cleanup(encoder);

View File

@ -114,7 +114,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
}
if (bridge) {
ret = drm_bridge_attach(&output->encoder, bridge, NULL);
ret = drm_bridge_attach(&output->encoder, bridge, NULL, 0);
if (!ret)
return 0;

View File

@ -27,13 +27,16 @@ config DRM_CDNS_DSI
Support Cadence DPI to DSI bridge. This is an internal
bridge and is meant to be directly embedded in a SoC.
config DRM_DUMB_VGA_DAC
tristate "Dumb VGA DAC Bridge support"
config DRM_DISPLAY_CONNECTOR
tristate "Display connector support"
depends on OF
select DRM_KMS_HELPER
help
Support for non-programmable RGB to VGA DAC bridges, such as ADI
ADV7123, TI THS8134 and THS8135 or passive resistor ladder DACs.
Driver for display connectors with support for DDC and hot-plug
detection. Most display controller handle display connectors
internally and don't need this driver, but the DRM subsystem is
moving towards separating connector handling from display controllers
on ARM-based platforms. Saying Y here when this driver is not needed
will not cause any issue.
config DRM_LVDS_CODEC
tristate "Transparent LVDS encoders and decoders support"
@ -110,6 +113,14 @@ config DRM_SII9234
It is an I2C driver, that detects connection of MHL bridge
and starts encapsulation of HDMI signal.
config DRM_SIMPLE_BRIDGE
tristate "Simple DRM bridge support"
depends on OF
select DRM_KMS_HELPER
help
Support for non-programmable DRM bridges, such as ADI ADV7123, TI
THS8134 and THS8135 or passive resistor ladder DACs.
config DRM_THINE_THC63LVD1024
tristate "Thine THC63LVD1024 LVDS decoder bridge"
depends on OF
@ -161,6 +172,14 @@ config DRM_TI_SN65DSI86
help
Texas Instruments SN65DSI86 DSI to eDP Bridge driver
config DRM_TI_TPD12S015
tristate "TI TPD12S015 HDMI level shifter and ESD protection"
depends on OF
select DRM_KMS_HELPER
help
Texas Instruments TPD12S015 HDMI level shifter and ESD protection
driver.
source "drivers/gpu/drm/bridge/analogix/Kconfig"
source "drivers/gpu/drm/bridge/adv7511/Kconfig"

View File

@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o
obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
@ -9,6 +9,7 @@ obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
obj-$(CONFIG_DRM_SII902X) += sii902x.o
obj-$(CONFIG_DRM_SII9234) += sii9234.o
obj-$(CONFIG_DRM_SIMPLE_BRIDGE) += simple-bridge.o
obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o
obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o
obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
@ -16,6 +17,7 @@ obj-$(CONFIG_DRM_TOSHIBA_TC358768) += tc358768.o
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
obj-$(CONFIG_DRM_TI_TPD12S015) += ti-tpd12s015.o
obj-y += analogix/
obj-y += synopsys/

View File

@ -847,11 +847,17 @@ static void adv7511_bridge_mode_set(struct drm_bridge *bridge,
adv7511_mode_set(adv, mode, adj_mode);
}
static int adv7511_bridge_attach(struct drm_bridge *bridge)
static int adv7511_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct adv7511 *adv = bridge_to_adv7511(bridge);
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
if (!bridge->encoder) {
DRM_ERROR("Parent encoder object not found");
return -ENODEV;

View File

@ -520,11 +520,17 @@ static const struct drm_connector_funcs anx6345_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int anx6345_bridge_attach(struct drm_bridge *bridge)
static int anx6345_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct anx6345 *anx6345 = bridge_to_anx6345(bridge);
int err;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
if (!bridge->encoder) {
DRM_ERROR("Parent encoder object not found");
return -ENODEV;
@ -712,14 +718,14 @@ static int anx6345_i2c_probe(struct i2c_client *client,
DRM_DEBUG("No panel found\n");
/* 1.2V digital core power regulator */
anx6345->dvdd12 = devm_regulator_get(dev, "dvdd12-supply");
anx6345->dvdd12 = devm_regulator_get(dev, "dvdd12");
if (IS_ERR(anx6345->dvdd12)) {
DRM_ERROR("dvdd12-supply not found\n");
return PTR_ERR(anx6345->dvdd12);
}
/* 2.5V digital core power regulator */
anx6345->dvdd25 = devm_regulator_get(dev, "dvdd25-supply");
anx6345->dvdd25 = devm_regulator_get(dev, "dvdd25");
if (IS_ERR(anx6345->dvdd25)) {
DRM_ERROR("dvdd25-supply not found\n");
return PTR_ERR(anx6345->dvdd25);

View File

@ -722,10 +722,9 @@ static int anx78xx_dp_link_training(struct anx78xx *anx78xx)
if (err)
return err;
dpcd[0] = drm_dp_max_link_rate(anx78xx->dpcd);
dpcd[0] = drm_dp_link_rate_to_bw_code(dpcd[0]);
err = regmap_write(anx78xx->map[I2C_IDX_TX_P0],
SP_DP_MAIN_LINK_BW_SET_REG, dpcd[0]);
SP_DP_MAIN_LINK_BW_SET_REG,
anx78xx->dpcd[DP_MAX_LINK_RATE]);
if (err)
return err;
@ -887,11 +886,17 @@ static const struct drm_connector_funcs anx78xx_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int anx78xx_bridge_attach(struct drm_bridge *bridge)
static int anx78xx_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct anx78xx *anx78xx = bridge_to_anx78xx(bridge);
int err;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
if (!bridge->encoder) {
DRM_ERROR("Parent encoder object not found");
return -ENODEV;

View File

@ -1216,13 +1216,19 @@ static const struct drm_connector_funcs analogix_dp_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int analogix_dp_bridge_attach(struct drm_bridge *bridge)
static int analogix_dp_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct analogix_dp_device *dp = bridge->driver_private;
struct drm_encoder *encoder = dp->encoder;
struct drm_connector *connector = NULL;
int ret = 0;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
if (!bridge->encoder) {
DRM_ERROR("Parent encoder object not found");
return -ENODEV;
@ -1598,7 +1604,7 @@ static int analogix_dp_create_bridge(struct drm_device *drm_dev,
bridge->driver_private = dp;
bridge->funcs = &analogix_dp_bridge_funcs;
ret = drm_bridge_attach(dp->encoder, bridge, NULL);
ret = drm_bridge_attach(dp->encoder, bridge, NULL, 0);
if (ret) {
DRM_ERROR("failed to attach drm bridge\n");
return -EINVAL;

View File

@ -644,7 +644,8 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
return 0;
}
static int cdns_dsi_bridge_attach(struct drm_bridge *bridge)
static int cdns_dsi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
struct cdns_dsi *dsi = input_to_dsi(input);
@ -656,7 +657,8 @@ static int cdns_dsi_bridge_attach(struct drm_bridge *bridge)
return -ENOTSUPP;
}
return drm_bridge_attach(bridge->encoder, output->bridge, bridge);
return drm_bridge_attach(bridge->encoder, output->bridge, bridge,
flags);
}
static enum drm_mode_status

View File

@ -0,0 +1,295 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <drm/drm_bridge.h>
#include <drm/drm_edid.h>
struct display_connector {
struct drm_bridge bridge;
struct gpio_desc *hpd_gpio;
int hpd_irq;
};
static inline struct display_connector *
to_display_connector(struct drm_bridge *bridge)
{
return container_of(bridge, struct display_connector, bridge);
}
static int display_connector_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;
}
static enum drm_connector_status
display_connector_detect(struct drm_bridge *bridge)
{
struct display_connector *conn = to_display_connector(bridge);
if (conn->hpd_gpio) {
if (gpiod_get_value_cansleep(conn->hpd_gpio))
return connector_status_connected;
else
return connector_status_disconnected;
}
if (conn->bridge.ddc && drm_probe_ddc(conn->bridge.ddc))
return connector_status_connected;
switch (conn->bridge.type) {
case DRM_MODE_CONNECTOR_DVIA:
case DRM_MODE_CONNECTOR_DVID:
case DRM_MODE_CONNECTOR_DVII:
case DRM_MODE_CONNECTOR_HDMIA:
case DRM_MODE_CONNECTOR_HDMIB:
/*
* For DVI and HDMI connectors a DDC probe failure indicates
* that no cable is connected.
*/
return connector_status_disconnected;
case DRM_MODE_CONNECTOR_Composite:
case DRM_MODE_CONNECTOR_SVIDEO:
case DRM_MODE_CONNECTOR_VGA:
default:
/*
* Composite and S-Video connectors have no other detection
* mean than the HPD GPIO. For VGA connectors, even if we have
* an I2C bus, we can't assume that the cable is disconnected
* if drm_probe_ddc fails, as some cables don't wire the DDC
* pins.
*/
return connector_status_unknown;
}
}
static struct edid *display_connector_get_edid(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct display_connector *conn = to_display_connector(bridge);
return drm_get_edid(connector, conn->bridge.ddc);
}
static const struct drm_bridge_funcs display_connector_bridge_funcs = {
.attach = display_connector_attach,
.detect = display_connector_detect,
.get_edid = display_connector_get_edid,
};
static irqreturn_t display_connector_hpd_irq(int irq, void *arg)
{
struct display_connector *conn = arg;
struct drm_bridge *bridge = &conn->bridge;
drm_bridge_hpd_notify(bridge, display_connector_detect(bridge));
return IRQ_HANDLED;
}
static int display_connector_probe(struct platform_device *pdev)
{
struct display_connector *conn;
unsigned int type;
const char *label;
int ret;
conn = devm_kzalloc(&pdev->dev, sizeof(*conn), GFP_KERNEL);
if (!conn)
return -ENOMEM;
platform_set_drvdata(pdev, conn);
type = (uintptr_t)of_device_get_match_data(&pdev->dev);
/* Get the exact connector type. */
switch (type) {
case DRM_MODE_CONNECTOR_DVII: {
bool analog, digital;
analog = of_property_read_bool(pdev->dev.of_node, "analog");
digital = of_property_read_bool(pdev->dev.of_node, "digital");
if (analog && !digital) {
conn->bridge.type = DRM_MODE_CONNECTOR_DVIA;
} else if (!analog && digital) {
conn->bridge.type = DRM_MODE_CONNECTOR_DVID;
} else if (analog && digital) {
conn->bridge.type = DRM_MODE_CONNECTOR_DVII;
} else {
dev_err(&pdev->dev, "DVI connector with no type\n");
return -EINVAL;
}
break;
}
case DRM_MODE_CONNECTOR_HDMIA: {
const char *hdmi_type;
ret = of_property_read_string(pdev->dev.of_node, "type",
&hdmi_type);
if (ret < 0) {
dev_err(&pdev->dev, "HDMI connector with no type\n");
return -EINVAL;
}
if (!strcmp(hdmi_type, "a") || !strcmp(hdmi_type, "c") ||
!strcmp(hdmi_type, "d") || !strcmp(hdmi_type, "e")) {
conn->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
} else if (!strcmp(hdmi_type, "b")) {
conn->bridge.type = DRM_MODE_CONNECTOR_HDMIB;
} else {
dev_err(&pdev->dev,
"Unsupported HDMI connector type '%s'\n",
hdmi_type);
return -EINVAL;
}
break;
}
default:
conn->bridge.type = type;
break;
}
/* All the supported connector types support interlaced modes. */
conn->bridge.interlace_allowed = true;
/* Get the optional connector label. */
of_property_read_string(pdev->dev.of_node, "label", &label);
/*
* Get the HPD GPIO for DVI and HDMI connectors. If the GPIO can provide
* edge interrupts, register an interrupt handler.
*/
if (type == DRM_MODE_CONNECTOR_DVII ||
type == DRM_MODE_CONNECTOR_HDMIA) {
conn->hpd_gpio = devm_gpiod_get_optional(&pdev->dev, "hpd",
GPIOD_IN);
if (IS_ERR(conn->hpd_gpio)) {
if (PTR_ERR(conn->hpd_gpio) != -EPROBE_DEFER)
dev_err(&pdev->dev,
"Unable to retrieve HPD GPIO\n");
return PTR_ERR(conn->hpd_gpio);
}
conn->hpd_irq = gpiod_to_irq(conn->hpd_gpio);
} else {
conn->hpd_irq = -EINVAL;
}
if (conn->hpd_irq >= 0) {
ret = devm_request_threaded_irq(&pdev->dev, conn->hpd_irq,
NULL, display_connector_hpd_irq,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"HPD", conn);
if (ret) {
dev_info(&pdev->dev,
"Failed to request HPD edge interrupt, falling back to polling\n");
conn->hpd_irq = -EINVAL;
}
}
/* Retrieve the DDC I2C adapter for DVI, HDMI and VGA connectors. */
if (type == DRM_MODE_CONNECTOR_DVII ||
type == DRM_MODE_CONNECTOR_HDMIA ||
type == DRM_MODE_CONNECTOR_VGA) {
struct device_node *phandle;
phandle = of_parse_phandle(pdev->dev.of_node, "ddc-i2c-bus", 0);
if (phandle) {
conn->bridge.ddc = of_get_i2c_adapter_by_node(phandle);
of_node_put(phandle);
if (!conn->bridge.ddc)
return -EPROBE_DEFER;
} else {
dev_dbg(&pdev->dev,
"No I2C bus specified, disabling EDID readout\n");
}
}
conn->bridge.funcs = &display_connector_bridge_funcs;
conn->bridge.of_node = pdev->dev.of_node;
if (conn->bridge.ddc)
conn->bridge.ops |= DRM_BRIDGE_OP_EDID
| DRM_BRIDGE_OP_DETECT;
if (conn->hpd_gpio)
conn->bridge.ops |= DRM_BRIDGE_OP_DETECT;
if (conn->hpd_irq >= 0)
conn->bridge.ops |= DRM_BRIDGE_OP_HPD;
dev_dbg(&pdev->dev,
"Found %s display connector '%s' %s DDC bus and %s HPD GPIO (ops 0x%x)\n",
drm_get_connector_type_name(conn->bridge.type),
label ? label : "<unlabelled>",
conn->bridge.ddc ? "with" : "without",
conn->hpd_gpio ? "with" : "without",
conn->bridge.ops);
drm_bridge_add(&conn->bridge);
return 0;
}
static int display_connector_remove(struct platform_device *pdev)
{
struct display_connector *conn = platform_get_drvdata(pdev);
drm_bridge_remove(&conn->bridge);
if (!IS_ERR(conn->bridge.ddc))
i2c_put_adapter(conn->bridge.ddc);
return 0;
}
static const struct of_device_id display_connector_match[] = {
{
.compatible = "composite-video-connector",
.data = (void *)DRM_MODE_CONNECTOR_Composite,
}, {
.compatible = "dvi-connector",
.data = (void *)DRM_MODE_CONNECTOR_DVII,
}, {
.compatible = "hdmi-connector",
.data = (void *)DRM_MODE_CONNECTOR_HDMIA,
}, {
.compatible = "svideo-connector",
.data = (void *)DRM_MODE_CONNECTOR_SVIDEO,
}, {
.compatible = "vga-connector",
.data = (void *)DRM_MODE_CONNECTOR_VGA,
},
{},
};
MODULE_DEVICE_TABLE(of, display_connector_match);
static struct platform_driver display_connector_driver = {
.probe = display_connector_probe,
.remove = display_connector_remove,
.driver = {
.name = "display-connector",
.of_match_table = display_connector_match,
},
};
module_platform_driver(display_connector_driver);
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_DESCRIPTION("Display connector driver");
MODULE_LICENSE("GPL");

View File

@ -1,300 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015-2016 Free Electrons
* Copyright (C) 2015-2016 NextThing Co
*
* Maxime Ripard <maxime.ripard@free-electrons.com>
*/
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
struct dumb_vga {
struct drm_bridge bridge;
struct drm_connector connector;
struct i2c_adapter *ddc;
struct regulator *vdd;
};
static inline struct dumb_vga *
drm_bridge_to_dumb_vga(struct drm_bridge *bridge)
{
return container_of(bridge, struct dumb_vga, bridge);
}
static inline struct dumb_vga *
drm_connector_to_dumb_vga(struct drm_connector *connector)
{
return container_of(connector, struct dumb_vga, connector);
}
static int dumb_vga_get_modes(struct drm_connector *connector)
{
struct dumb_vga *vga = drm_connector_to_dumb_vga(connector);
struct edid *edid;
int ret;
if (!vga->ddc)
goto fallback;
edid = drm_get_edid(connector, vga->ddc);
if (!edid) {
DRM_INFO("EDID readout failed, falling back to standard modes\n");
goto fallback;
}
drm_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
kfree(edid);
return ret;
fallback:
/*
* In case we cannot retrieve the EDIDs (broken or missing i2c
* bus), fallback on the XGA standards
*/
ret = drm_add_modes_noedid(connector, 1920, 1200);
/* And prefer a mode pretty much anyone can handle */
drm_set_preferred_mode(connector, 1024, 768);
return ret;
}
static const struct drm_connector_helper_funcs dumb_vga_con_helper_funcs = {
.get_modes = dumb_vga_get_modes,
};
static enum drm_connector_status
dumb_vga_connector_detect(struct drm_connector *connector, bool force)
{
struct dumb_vga *vga = drm_connector_to_dumb_vga(connector);
/*
* Even if we have an I2C bus, we can't assume that the cable
* is disconnected if drm_probe_ddc fails. Some cables don't
* wire the DDC pins, or the I2C bus might not be working at
* all.
*/
if (vga->ddc && drm_probe_ddc(vga->ddc))
return connector_status_connected;
return connector_status_unknown;
}
static const struct drm_connector_funcs dumb_vga_con_funcs = {
.detect = dumb_vga_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int dumb_vga_attach(struct drm_bridge *bridge)
{
struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge);
int ret;
if (!bridge->encoder) {
DRM_ERROR("Missing encoder\n");
return -ENODEV;
}
drm_connector_helper_add(&vga->connector,
&dumb_vga_con_helper_funcs);
ret = drm_connector_init_with_ddc(bridge->dev, &vga->connector,
&dumb_vga_con_funcs,
DRM_MODE_CONNECTOR_VGA,
vga->ddc);
if (ret) {
DRM_ERROR("Failed to initialize connector\n");
return ret;
}
drm_connector_attach_encoder(&vga->connector,
bridge->encoder);
return 0;
}
static void dumb_vga_enable(struct drm_bridge *bridge)
{
struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge);
int ret = 0;
if (vga->vdd)
ret = regulator_enable(vga->vdd);
if (ret)
DRM_ERROR("Failed to enable vdd regulator: %d\n", ret);
}
static void dumb_vga_disable(struct drm_bridge *bridge)
{
struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge);
if (vga->vdd)
regulator_disable(vga->vdd);
}
static const struct drm_bridge_funcs dumb_vga_bridge_funcs = {
.attach = dumb_vga_attach,
.enable = dumb_vga_enable,
.disable = dumb_vga_disable,
};
static struct i2c_adapter *dumb_vga_retrieve_ddc(struct device *dev)
{
struct device_node *phandle, *remote;
struct i2c_adapter *ddc;
remote = of_graph_get_remote_node(dev->of_node, 1, -1);
if (!remote)
return ERR_PTR(-EINVAL);
phandle = of_parse_phandle(remote, "ddc-i2c-bus", 0);
of_node_put(remote);
if (!phandle)
return ERR_PTR(-ENODEV);
ddc = of_get_i2c_adapter_by_node(phandle);
of_node_put(phandle);
if (!ddc)
return ERR_PTR(-EPROBE_DEFER);
return ddc;
}
static int dumb_vga_probe(struct platform_device *pdev)
{
struct dumb_vga *vga;
vga = devm_kzalloc(&pdev->dev, sizeof(*vga), GFP_KERNEL);
if (!vga)
return -ENOMEM;
platform_set_drvdata(pdev, vga);
vga->vdd = devm_regulator_get_optional(&pdev->dev, "vdd");
if (IS_ERR(vga->vdd)) {
int ret = PTR_ERR(vga->vdd);
if (ret == -EPROBE_DEFER)
return -EPROBE_DEFER;
vga->vdd = NULL;
dev_dbg(&pdev->dev, "No vdd regulator found: %d\n", ret);
}
vga->ddc = dumb_vga_retrieve_ddc(&pdev->dev);
if (IS_ERR(vga->ddc)) {
if (PTR_ERR(vga->ddc) == -ENODEV) {
dev_dbg(&pdev->dev,
"No i2c bus specified. Disabling EDID readout\n");
vga->ddc = NULL;
} else {
dev_err(&pdev->dev, "Couldn't retrieve i2c bus\n");
return PTR_ERR(vga->ddc);
}
}
vga->bridge.funcs = &dumb_vga_bridge_funcs;
vga->bridge.of_node = pdev->dev.of_node;
vga->bridge.timings = of_device_get_match_data(&pdev->dev);
drm_bridge_add(&vga->bridge);
return 0;
}
static int dumb_vga_remove(struct platform_device *pdev)
{
struct dumb_vga *vga = platform_get_drvdata(pdev);
drm_bridge_remove(&vga->bridge);
if (vga->ddc)
i2c_put_adapter(vga->ddc);
return 0;
}
/*
* We assume the ADV7123 DAC is the "default" for historical reasons
* Information taken from the ADV7123 datasheet, revision D.
* NOTE: the ADV7123EP seems to have other timings and need a new timings
* set if used.
*/
static const struct drm_bridge_timings default_dac_timings = {
/* Timing specifications, datasheet page 7 */
.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
.setup_time_ps = 500,
.hold_time_ps = 1500,
};
/*
* Information taken from the THS8134, THS8134A, THS8134B datasheet named
* "SLVS205D", dated May 1990, revised March 2000.
*/
static const struct drm_bridge_timings ti_ths8134_dac_timings = {
/* From timing diagram, datasheet page 9 */
.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
/* From datasheet, page 12 */
.setup_time_ps = 3000,
/* I guess this means latched input */
.hold_time_ps = 0,
};
/*
* Information taken from the THS8135 datasheet named "SLAS343B", dated
* May 2001, revised April 2013.
*/
static const struct drm_bridge_timings ti_ths8135_dac_timings = {
/* From timing diagram, datasheet page 14 */
.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
/* From datasheet, page 16 */
.setup_time_ps = 2000,
.hold_time_ps = 500,
};
static const struct of_device_id dumb_vga_match[] = {
{
.compatible = "dumb-vga-dac",
.data = NULL,
},
{
.compatible = "adi,adv7123",
.data = &default_dac_timings,
},
{
.compatible = "ti,ths8135",
.data = &ti_ths8135_dac_timings,
},
{
.compatible = "ti,ths8134",
.data = &ti_ths8134_dac_timings,
},
{},
};
MODULE_DEVICE_TABLE(of, dumb_vga_match);
static struct platform_driver dumb_vga_driver = {
.probe = dumb_vga_probe,
.remove = dumb_vga_remove,
.driver = {
.name = "dumb-vga-dac",
.of_match_table = dumb_vga_match,
},
};
module_platform_driver(dumb_vga_driver);
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
MODULE_DESCRIPTION("Dumb VGA DAC bridge driver");
MODULE_LICENSE("GPL");

View File

@ -21,19 +21,23 @@ struct lvds_codec {
u32 connector_type;
};
static int lvds_codec_attach(struct drm_bridge *bridge)
static inline struct lvds_codec *to_lvds_codec(struct drm_bridge *bridge)
{
struct lvds_codec *lvds_codec = container_of(bridge,
struct lvds_codec, bridge);
return container_of(bridge, struct lvds_codec, bridge);
}
static int lvds_codec_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct lvds_codec *lvds_codec = to_lvds_codec(bridge);
return drm_bridge_attach(bridge->encoder, lvds_codec->panel_bridge,
bridge);
bridge, flags);
}
static void lvds_codec_enable(struct drm_bridge *bridge)
{
struct lvds_codec *lvds_codec = container_of(bridge,
struct lvds_codec, bridge);
struct lvds_codec *lvds_codec = to_lvds_codec(bridge);
if (lvds_codec->powerdown_gpio)
gpiod_set_value_cansleep(lvds_codec->powerdown_gpio, 0);
@ -41,14 +45,13 @@ static void lvds_codec_enable(struct drm_bridge *bridge)
static void lvds_codec_disable(struct drm_bridge *bridge)
{
struct lvds_codec *lvds_codec = container_of(bridge,
struct lvds_codec, bridge);
struct lvds_codec *lvds_codec = to_lvds_codec(bridge);
if (lvds_codec->powerdown_gpio)
gpiod_set_value_cansleep(lvds_codec->powerdown_gpio, 1);
}
static struct drm_bridge_funcs funcs = {
static const struct drm_bridge_funcs funcs = {
.attach = lvds_codec_attach,
.enable = lvds_codec_enable,
.disable = lvds_codec_disable,

View File

@ -206,13 +206,19 @@ static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int ge_b850v3_lvds_attach(struct drm_bridge *bridge)
static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector;
struct i2c_client *stdp4028_i2c
= ge_b850v3_lvds_ptr->stdp4028_i2c;
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
if (!bridge->encoder) {
DRM_ERROR("Parent encoder object not found");
return -ENODEV;

View File

@ -236,11 +236,17 @@ static const struct drm_connector_funcs ptn3460_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int ptn3460_bridge_attach(struct drm_bridge *bridge)
static int ptn3460_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
if (!bridge->encoder) {
DRM_ERROR("Parent encoder object not found");
return -ENODEV;

View File

@ -53,12 +53,16 @@ static const struct drm_connector_funcs panel_bridge_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int panel_bridge_attach(struct drm_bridge *bridge)
static int panel_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
struct drm_connector *connector = &panel_bridge->connector;
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
if (!bridge->encoder) {
DRM_ERROR("Missing encoder\n");
return -ENODEV;
@ -120,6 +124,14 @@ static void panel_bridge_post_disable(struct drm_bridge *bridge)
drm_panel_unprepare(panel_bridge->panel);
}
static int panel_bridge_get_modes(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
return drm_panel_get_modes(panel_bridge->panel, connector);
}
static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
.attach = panel_bridge_attach,
.detach = panel_bridge_detach,
@ -127,6 +139,11 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
.enable = panel_bridge_enable,
.disable = panel_bridge_disable,
.post_disable = panel_bridge_post_disable,
.get_modes = panel_bridge_get_modes,
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
};
/**
@ -196,6 +213,8 @@ struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
#ifdef CONFIG_OF
panel_bridge->bridge.of_node = panel->dev->of_node;
#endif
panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES;
panel_bridge->bridge.type = connector_type;
drm_bridge_add(&panel_bridge->bridge);

View File

@ -476,11 +476,17 @@ static const struct drm_connector_funcs ps8622_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int ps8622_attach(struct drm_bridge *bridge)
static int ps8622_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
if (!bridge->encoder) {
DRM_ERROR("Parent encoder object not found");
return -ENODEV;

View File

@ -187,7 +187,8 @@ static void ps8640_post_disable(struct drm_bridge *bridge)
DRM_ERROR("cannot disable regulators %d\n", ret);
}
static int ps8640_bridge_attach(struct drm_bridge *bridge)
static int ps8640_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
struct device *dev = &ps_bridge->page[0]->dev;
@ -234,7 +235,7 @@ static int ps8640_bridge_attach(struct drm_bridge *bridge)
/* Attach the panel-bridge to the dsi bridge */
return drm_bridge_attach(bridge->encoder, ps_bridge->panel_bridge,
&ps_bridge->bridge);
&ps_bridge->bridge, flags);
err_dsi_attach:
mipi_dsi_device_unregister(dsi);

View File

@ -399,12 +399,18 @@ out:
mutex_unlock(&sii902x->mutex);
}
static int sii902x_bridge_attach(struct drm_bridge *bridge)
static int sii902x_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct sii902x *sii902x = bridge_to_sii902x(bridge);
struct drm_device *drm = bridge->dev;
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
drm_connector_helper_add(&sii902x->connector,
&sii902x_connector_helper_funcs);

View File

@ -2202,7 +2202,8 @@ static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
return container_of(bridge, struct sii8620, bridge);
}
static int sii8620_attach(struct drm_bridge *bridge)
static int sii8620_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct sii8620 *ctx = bridge_to_sii8620(bridge);

View File

@ -0,0 +1,342 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015-2016 Free Electrons
* Copyright (C) 2015-2016 NextThing Co
*
* Maxime Ripard <maxime.ripard@free-electrons.com>
*/
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
struct simple_bridge_info {
const struct drm_bridge_timings *timings;
unsigned int connector_type;
};
struct simple_bridge {
struct drm_bridge bridge;
struct drm_connector connector;
const struct simple_bridge_info *info;
struct i2c_adapter *ddc;
struct regulator *vdd;
struct gpio_desc *enable;
};
static inline struct simple_bridge *
drm_bridge_to_simple_bridge(struct drm_bridge *bridge)
{
return container_of(bridge, struct simple_bridge, bridge);
}
static inline struct simple_bridge *
drm_connector_to_simple_bridge(struct drm_connector *connector)
{
return container_of(connector, struct simple_bridge, connector);
}
static int simple_bridge_get_modes(struct drm_connector *connector)
{
struct simple_bridge *sbridge = drm_connector_to_simple_bridge(connector);
struct edid *edid;
int ret;
if (!sbridge->ddc)
goto fallback;
edid = drm_get_edid(connector, sbridge->ddc);
if (!edid) {
DRM_INFO("EDID readout failed, falling back to standard modes\n");
goto fallback;
}
drm_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
kfree(edid);
return ret;
fallback:
/*
* In case we cannot retrieve the EDIDs (broken or missing i2c
* bus), fallback on the XGA standards
*/
ret = drm_add_modes_noedid(connector, 1920, 1200);
/* And prefer a mode pretty much anyone can handle */
drm_set_preferred_mode(connector, 1024, 768);
return ret;
}
static const struct drm_connector_helper_funcs simple_bridge_con_helper_funcs = {
.get_modes = simple_bridge_get_modes,
};
static enum drm_connector_status
simple_bridge_connector_detect(struct drm_connector *connector, bool force)
{
struct simple_bridge *sbridge = drm_connector_to_simple_bridge(connector);
/*
* Even if we have an I2C bus, we can't assume that the cable
* is disconnected if drm_probe_ddc fails. Some cables don't
* wire the DDC pins, or the I2C bus might not be working at
* all.
*/
if (sbridge->ddc && drm_probe_ddc(sbridge->ddc))
return connector_status_connected;
return connector_status_unknown;
}
static const struct drm_connector_funcs simple_bridge_con_funcs = {
.detect = simple_bridge_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int simple_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
if (!bridge->encoder) {
DRM_ERROR("Missing encoder\n");
return -ENODEV;
}
drm_connector_helper_add(&sbridge->connector,
&simple_bridge_con_helper_funcs);
ret = drm_connector_init_with_ddc(bridge->dev, &sbridge->connector,
&simple_bridge_con_funcs,
sbridge->info->connector_type,
sbridge->ddc);
if (ret) {
DRM_ERROR("Failed to initialize connector\n");
return ret;
}
drm_connector_attach_encoder(&sbridge->connector,
bridge->encoder);
return 0;
}
static void simple_bridge_enable(struct drm_bridge *bridge)
{
struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
int ret;
if (sbridge->vdd) {
ret = regulator_enable(sbridge->vdd);
if (ret)
DRM_ERROR("Failed to enable vdd regulator: %d\n", ret);
}
gpiod_set_value_cansleep(sbridge->enable, 1);
}
static void simple_bridge_disable(struct drm_bridge *bridge)
{
struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
gpiod_set_value_cansleep(sbridge->enable, 0);
if (sbridge->vdd)
regulator_disable(sbridge->vdd);
}
static const struct drm_bridge_funcs simple_bridge_bridge_funcs = {
.attach = simple_bridge_attach,
.enable = simple_bridge_enable,
.disable = simple_bridge_disable,
};
static struct i2c_adapter *simple_bridge_retrieve_ddc(struct device *dev)
{
struct device_node *phandle, *remote;
struct i2c_adapter *ddc;
remote = of_graph_get_remote_node(dev->of_node, 1, -1);
if (!remote)
return ERR_PTR(-EINVAL);
phandle = of_parse_phandle(remote, "ddc-i2c-bus", 0);
of_node_put(remote);
if (!phandle)
return ERR_PTR(-ENODEV);
ddc = of_get_i2c_adapter_by_node(phandle);
of_node_put(phandle);
if (!ddc)
return ERR_PTR(-EPROBE_DEFER);
return ddc;
}
static int simple_bridge_probe(struct platform_device *pdev)
{
struct simple_bridge *sbridge;
sbridge = devm_kzalloc(&pdev->dev, sizeof(*sbridge), GFP_KERNEL);
if (!sbridge)
return -ENOMEM;
platform_set_drvdata(pdev, sbridge);
sbridge->info = of_device_get_match_data(&pdev->dev);
sbridge->vdd = devm_regulator_get_optional(&pdev->dev, "vdd");
if (IS_ERR(sbridge->vdd)) {
int ret = PTR_ERR(sbridge->vdd);
if (ret == -EPROBE_DEFER)
return -EPROBE_DEFER;
sbridge->vdd = NULL;
dev_dbg(&pdev->dev, "No vdd regulator found: %d\n", ret);
}
sbridge->enable = devm_gpiod_get_optional(&pdev->dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR(sbridge->enable)) {
if (PTR_ERR(sbridge->enable) != -EPROBE_DEFER)
dev_err(&pdev->dev, "Unable to retrieve enable GPIO\n");
return PTR_ERR(sbridge->enable);
}
sbridge->ddc = simple_bridge_retrieve_ddc(&pdev->dev);
if (IS_ERR(sbridge->ddc)) {
if (PTR_ERR(sbridge->ddc) == -ENODEV) {
dev_dbg(&pdev->dev,
"No i2c bus specified. Disabling EDID readout\n");
sbridge->ddc = NULL;
} else {
dev_err(&pdev->dev, "Couldn't retrieve i2c bus\n");
return PTR_ERR(sbridge->ddc);
}
}
sbridge->bridge.funcs = &simple_bridge_bridge_funcs;
sbridge->bridge.of_node = pdev->dev.of_node;
sbridge->bridge.timings = sbridge->info->timings;
drm_bridge_add(&sbridge->bridge);
return 0;
}
static int simple_bridge_remove(struct platform_device *pdev)
{
struct simple_bridge *sbridge = platform_get_drvdata(pdev);
drm_bridge_remove(&sbridge->bridge);
if (sbridge->ddc)
i2c_put_adapter(sbridge->ddc);
return 0;
}
/*
* We assume the ADV7123 DAC is the "default" for historical reasons
* Information taken from the ADV7123 datasheet, revision D.
* NOTE: the ADV7123EP seems to have other timings and need a new timings
* set if used.
*/
static const struct drm_bridge_timings default_bridge_timings = {
/* Timing specifications, datasheet page 7 */
.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
.setup_time_ps = 500,
.hold_time_ps = 1500,
};
/*
* Information taken from the THS8134, THS8134A, THS8134B datasheet named
* "SLVS205D", dated May 1990, revised March 2000.
*/
static const struct drm_bridge_timings ti_ths8134_bridge_timings = {
/* From timing diagram, datasheet page 9 */
.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
/* From datasheet, page 12 */
.setup_time_ps = 3000,
/* I guess this means latched input */
.hold_time_ps = 0,
};
/*
* Information taken from the THS8135 datasheet named "SLAS343B", dated
* May 2001, revised April 2013.
*/
static const struct drm_bridge_timings ti_ths8135_bridge_timings = {
/* From timing diagram, datasheet page 14 */
.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
/* From datasheet, page 16 */
.setup_time_ps = 2000,
.hold_time_ps = 500,
};
static const struct of_device_id simple_bridge_match[] = {
{
.compatible = "dumb-vga-dac",
.data = &(const struct simple_bridge_info) {
.connector_type = DRM_MODE_CONNECTOR_VGA,
},
}, {
.compatible = "adi,adv7123",
.data = &(const struct simple_bridge_info) {
.timings = &default_bridge_timings,
.connector_type = DRM_MODE_CONNECTOR_VGA,
},
}, {
.compatible = "ti,opa362",
.data = &(const struct simple_bridge_info) {
.connector_type = DRM_MODE_CONNECTOR_Composite,
},
}, {
.compatible = "ti,ths8135",
.data = &(const struct simple_bridge_info) {
.timings = &ti_ths8135_bridge_timings,
.connector_type = DRM_MODE_CONNECTOR_VGA,
},
}, {
.compatible = "ti,ths8134",
.data = &(const struct simple_bridge_info) {
.timings = &ti_ths8134_bridge_timings,
.connector_type = DRM_MODE_CONNECTOR_VGA,
},
},
{},
};
MODULE_DEVICE_TABLE(of, simple_bridge_match);
static struct platform_driver simple_bridge_driver = {
.probe = simple_bridge_probe,
.remove = simple_bridge_remove,
.driver = {
.name = "simple-bridge",
.of_match_table = simple_bridge_match,
},
};
module_platform_driver(simple_bridge_driver);
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
MODULE_DESCRIPTION("Simple DRM bridge driver");
MODULE_LICENSE("GPL");

View File

@ -2371,7 +2371,8 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs =
.atomic_check = dw_hdmi_connector_atomic_check,
};
static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct dw_hdmi *hdmi = bridge->driver_private;
struct drm_encoder *encoder = bridge->encoder;
@ -2379,6 +2380,11 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
struct cec_connector_info conn_info;
struct cec_notifier *notifier;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
connector->interlace_allowed = 1;
connector->polled = DRM_CONNECTOR_POLL_HPD;
@ -3076,7 +3082,7 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
if (IS_ERR(hdmi))
return hdmi;
ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL);
ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0);
if (ret) {
dw_hdmi_remove(hdmi);
DRM_ERROR("Failed to initialize bridge with drm\n");

View File

@ -936,7 +936,8 @@ dw_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge,
return mode_status;
}
static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge)
static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
@ -949,7 +950,8 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge)
bridge->encoder->encoder_type = DRM_MODE_ENCODER_DSI;
/* Attach the panel-bridge to the dsi bridge */
return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge);
return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge,
flags);
}
static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
@ -1120,7 +1122,7 @@ int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder)
{
int ret;
ret = drm_bridge_attach(encoder, &dsi->bridge, NULL);
ret = drm_bridge_attach(encoder, &dsi->bridge, NULL, 0);
if (ret) {
DRM_ERROR("Failed to initialize bridge with drm\n");
return ret;

View File

@ -349,12 +349,18 @@ static void tc358764_enable(struct drm_bridge *bridge)
dev_err(ctx->dev, "error enabling panel (%d)\n", ret);
}
static int tc358764_attach(struct drm_bridge *bridge)
static int tc358764_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct tc358764 *ctx = bridge_to_tc358764(bridge);
struct drm_device *drm = bridge->dev;
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
ctx->connector.polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(drm, &ctx->connector,
&tc358764_connector_funcs,

View File

@ -31,6 +31,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
/* Registers */
@ -1403,13 +1404,19 @@ static const struct drm_connector_funcs tc_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int tc_bridge_attach(struct drm_bridge *bridge)
static int tc_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
struct tc_data *tc = bridge_to_tc(bridge);
struct drm_device *drm = bridge->dev;
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
/* Create DP/eDP connector */
drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs);
ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs,

View File

@ -511,7 +511,8 @@ static const struct mipi_dsi_host_ops tc358768_dsi_host_ops = {
.transfer = tc358768_dsi_host_transfer,
};
static int tc358768_bridge_attach(struct drm_bridge *bridge)
static int tc358768_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct tc358768_priv *priv = bridge_to_tc358768(bridge);
@ -520,7 +521,8 @@ static int tc358768_bridge_attach(struct drm_bridge *bridge)
return -ENOTSUPP;
}
return drm_bridge_attach(bridge->encoder, priv->output.bridge, bridge);
return drm_bridge_attach(bridge->encoder, priv->output.bridge, bridge,
flags);
}
static enum drm_mode_status

View File

@ -42,11 +42,12 @@ static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge)
return container_of(bridge, struct thc63_dev, bridge);
}
static int thc63_attach(struct drm_bridge *bridge)
static int thc63_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct thc63_dev *thc63 = to_thc63(bridge);
return drm_bridge_attach(bridge->encoder, thc63->next, bridge);
return drm_bridge_attach(bridge->encoder, thc63->next, bridge, flags);
}
static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge,

View File

@ -266,7 +266,8 @@ static int ti_sn_bridge_parse_regulators(struct ti_sn_bridge *pdata)
pdata->supplies);
}
static int ti_sn_bridge_attach(struct drm_bridge *bridge)
static int ti_sn_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
int ret, val;
struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge);
@ -277,6 +278,11 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge)
.node = NULL,
};
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
ret = drm_connector_init(bridge->dev, &pdata->connector,
&ti_sn_bridge_connector_funcs,
DRM_MODE_CONNECTOR_eDP);

View File

@ -4,14 +4,12 @@
* Author: Jyri Sarha <jsarha@ti.com>
*/
#include <linux/delay.h>
#include <linux/fwnode.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
@ -24,16 +22,13 @@
struct tfp410 {
struct drm_bridge bridge;
struct drm_connector connector;
unsigned int connector_type;
u32 bus_format;
struct i2c_adapter *ddc;
struct gpio_desc *hpd;
int hpd_irq;
struct delayed_work hpd_work;
struct gpio_desc *powerdown;
struct drm_bridge_timings timings;
struct drm_bridge *next_bridge;
struct device *dev;
};
@ -56,13 +51,18 @@ static int tfp410_get_modes(struct drm_connector *connector)
struct edid *edid;
int ret;
if (!dvi->ddc)
goto fallback;
edid = drm_bridge_get_edid(dvi->next_bridge, connector);
if (IS_ERR_OR_NULL(edid)) {
if (edid != ERR_PTR(-ENOTSUPP))
DRM_INFO("EDID read failed. Fallback to standard modes\n");
edid = drm_get_edid(connector, dvi->ddc);
if (!edid) {
DRM_INFO("EDID read failed. Fallback to standard modes\n");
goto fallback;
/*
* No EDID, fallback on the XGA standard modes and prefer a mode
* pretty much anything can handle.
*/
ret = drm_add_modes_noedid(connector, 1920, 1200);
drm_set_preferred_mode(connector, 1024, 768);
return ret;
}
drm_connector_update_edid_property(connector, edid);
@ -71,15 +71,6 @@ static int tfp410_get_modes(struct drm_connector *connector)
kfree(edid);
return ret;
fallback:
/* No EDID, fallback on the XGA standard modes */
ret = drm_add_modes_noedid(connector, 1920, 1200);
/* And prefer a mode pretty much anything can handle */
drm_set_preferred_mode(connector, 1024, 768);
return ret;
}
@ -92,21 +83,7 @@ tfp410_connector_detect(struct drm_connector *connector, bool force)
{
struct tfp410 *dvi = drm_connector_to_tfp410(connector);
if (dvi->hpd) {
if (gpiod_get_value_cansleep(dvi->hpd))
return connector_status_connected;
else
return connector_status_disconnected;
}
if (dvi->ddc) {
if (drm_probe_ddc(dvi->ddc))
return connector_status_connected;
else
return connector_status_disconnected;
}
return connector_status_unknown;
return drm_bridge_detect(dvi->next_bridge);
}
static const struct drm_connector_funcs tfp410_con_funcs = {
@ -118,27 +95,60 @@ static const struct drm_connector_funcs tfp410_con_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int tfp410_attach(struct drm_bridge *bridge)
static void tfp410_hpd_work_func(struct work_struct *work)
{
struct tfp410 *dvi;
dvi = container_of(work, struct tfp410, hpd_work.work);
if (dvi->bridge.dev)
drm_helper_hpd_irq_event(dvi->bridge.dev);
}
static void tfp410_hpd_callback(void *arg, enum drm_connector_status status)
{
struct tfp410 *dvi = arg;
mod_delayed_work(system_wq, &dvi->hpd_work,
msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
}
static int tfp410_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
int ret;
ret = drm_bridge_attach(bridge->encoder, dvi->next_bridge, bridge,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret < 0)
return ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
if (!bridge->encoder) {
dev_err(dvi->dev, "Missing encoder\n");
return -ENODEV;
}
if (dvi->hpd_irq >= 0)
if (dvi->next_bridge->ops & DRM_BRIDGE_OP_DETECT)
dvi->connector.polled = DRM_CONNECTOR_POLL_HPD;
else
dvi->connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
if (dvi->next_bridge->ops & DRM_BRIDGE_OP_HPD) {
INIT_DELAYED_WORK(&dvi->hpd_work, tfp410_hpd_work_func);
drm_bridge_hpd_enable(dvi->next_bridge, tfp410_hpd_callback,
dvi);
}
drm_connector_helper_add(&dvi->connector,
&tfp410_con_helper_funcs);
ret = drm_connector_init_with_ddc(bridge->dev, &dvi->connector,
&tfp410_con_funcs,
dvi->connector_type,
dvi->ddc);
dvi->next_bridge->type,
dvi->next_bridge->ddc);
if (ret) {
dev_err(dvi->dev, "drm_connector_init() failed: %d\n", ret);
return ret;
@ -147,12 +157,21 @@ static int tfp410_attach(struct drm_bridge *bridge)
drm_display_info_set_bus_formats(&dvi->connector.display_info,
&dvi->bus_format, 1);
drm_connector_attach_encoder(&dvi->connector,
bridge->encoder);
drm_connector_attach_encoder(&dvi->connector, bridge->encoder);
return 0;
}
static void tfp410_detach(struct drm_bridge *bridge)
{
struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
if (dvi->connector.dev && dvi->next_bridge->ops & DRM_BRIDGE_OP_HPD) {
drm_bridge_hpd_disable(dvi->next_bridge);
cancel_delayed_work_sync(&dvi->hpd_work);
}
}
static void tfp410_enable(struct drm_bridge *bridge)
{
struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
@ -181,31 +200,12 @@ static enum drm_mode_status tfp410_mode_valid(struct drm_bridge *bridge,
static const struct drm_bridge_funcs tfp410_bridge_funcs = {
.attach = tfp410_attach,
.detach = tfp410_detach,
.enable = tfp410_enable,
.disable = tfp410_disable,
.mode_valid = tfp410_mode_valid,
};
static void tfp410_hpd_work_func(struct work_struct *work)
{
struct tfp410 *dvi;
dvi = container_of(work, struct tfp410, hpd_work.work);
if (dvi->bridge.dev)
drm_helper_hpd_irq_event(dvi->bridge.dev);
}
static irqreturn_t tfp410_hpd_irq_thread(int irq, void *arg)
{
struct tfp410 *dvi = arg;
mod_delayed_work(system_wq, &dvi->hpd_work,
msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
return IRQ_HANDLED;
}
static const struct drm_bridge_timings tfp410_default_timings = {
.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE
| DRM_BUS_FLAG_DE_HIGH,
@ -283,51 +283,9 @@ static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c)
return 0;
}
static int tfp410_get_connector_properties(struct tfp410 *dvi)
{
struct device_node *connector_node, *ddc_phandle;
int ret = 0;
/* port@1 is the connector node */
connector_node = of_graph_get_remote_node(dvi->dev->of_node, 1, -1);
if (!connector_node)
return -ENODEV;
if (of_device_is_compatible(connector_node, "hdmi-connector"))
dvi->connector_type = DRM_MODE_CONNECTOR_HDMIA;
else
dvi->connector_type = DRM_MODE_CONNECTOR_DVID;
dvi->hpd = fwnode_gpiod_get_index(&connector_node->fwnode,
"hpd", 0, GPIOD_IN, "hpd");
if (IS_ERR(dvi->hpd)) {
ret = PTR_ERR(dvi->hpd);
dvi->hpd = NULL;
if (ret == -ENOENT)
ret = 0;
else
goto fail;
}
ddc_phandle = of_parse_phandle(connector_node, "ddc-i2c-bus", 0);
if (!ddc_phandle)
goto fail;
dvi->ddc = of_get_i2c_adapter_by_node(ddc_phandle);
if (dvi->ddc)
dev_info(dvi->dev, "Connector's ddc i2c bus found\n");
else
ret = -EPROBE_DEFER;
of_node_put(ddc_phandle);
fail:
of_node_put(connector_node);
return ret;
}
static int tfp410_init(struct device *dev, bool i2c)
{
struct device_node *node;
struct tfp410 *dvi;
int ret;
@ -339,21 +297,31 @@ static int tfp410_init(struct device *dev, bool i2c)
dvi = devm_kzalloc(dev, sizeof(*dvi), GFP_KERNEL);
if (!dvi)
return -ENOMEM;
dvi->dev = dev;
dev_set_drvdata(dev, dvi);
dvi->bridge.funcs = &tfp410_bridge_funcs;
dvi->bridge.of_node = dev->of_node;
dvi->bridge.timings = &dvi->timings;
dvi->dev = dev;
dvi->bridge.type = DRM_MODE_CONNECTOR_DVID;
ret = tfp410_parse_timings(dvi, i2c);
if (ret)
goto fail;
return ret;
ret = tfp410_get_connector_properties(dvi);
if (ret)
goto fail;
/* Get the next bridge, connected to port@1. */
node = of_graph_get_remote_node(dev->of_node, 1, -1);
if (!node)
return -ENODEV;
dvi->next_bridge = of_drm_find_bridge(node);
of_node_put(node);
if (!dvi->next_bridge)
return -EPROBE_DEFER;
/* Get the powerdown GPIO. */
dvi->powerdown = devm_gpiod_get_optional(dev, "powerdown",
GPIOD_OUT_HIGH);
if (IS_ERR(dvi->powerdown)) {
@ -361,48 +329,18 @@ static int tfp410_init(struct device *dev, bool i2c)
return PTR_ERR(dvi->powerdown);
}
if (dvi->hpd)
dvi->hpd_irq = gpiod_to_irq(dvi->hpd);
else
dvi->hpd_irq = -ENXIO;
if (dvi->hpd_irq >= 0) {
INIT_DELAYED_WORK(&dvi->hpd_work, tfp410_hpd_work_func);
ret = devm_request_threaded_irq(dev, dvi->hpd_irq,
NULL, tfp410_hpd_irq_thread, IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"hdmi-hpd", dvi);
if (ret) {
DRM_ERROR("failed to register hpd interrupt\n");
goto fail;
}
}
/* Register the DRM bridge. */
drm_bridge_add(&dvi->bridge);
return 0;
fail:
i2c_put_adapter(dvi->ddc);
if (dvi->hpd)
gpiod_put(dvi->hpd);
return ret;
}
static int tfp410_fini(struct device *dev)
{
struct tfp410 *dvi = dev_get_drvdata(dev);
if (dvi->hpd_irq >= 0)
cancel_delayed_work_sync(&dvi->hpd_work);
drm_bridge_remove(&dvi->bridge);
if (dvi->ddc)
i2c_put_adapter(dvi->ddc);
if (dvi->hpd)
gpiod_put(dvi->hpd);
return 0;
}

View File

@ -0,0 +1,211 @@
// SPDX-License-Identifier: GPL-2.0
/*
* TPD12S015 HDMI ESD protection & level shifter chip driver
*
* Copyright (C) 2019 Texas Instruments Incorporated
*
* Based on the omapdrm-specific encoder-opa362 driver
*
* Copyright (C) 2013 Texas Instruments Incorporated
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <drm/drm_bridge.h>
struct tpd12s015_device {
struct drm_bridge bridge;
struct gpio_desc *ct_cp_hpd_gpio;
struct gpio_desc *ls_oe_gpio;
struct gpio_desc *hpd_gpio;
int hpd_irq;
struct drm_bridge *next_bridge;
};
static inline struct tpd12s015_device *to_tpd12s015(struct drm_bridge *bridge)
{
return container_of(bridge, struct tpd12s015_device, bridge);
}
static int tpd12s015_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct tpd12s015_device *tpd = to_tpd12s015(bridge);
int ret;
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
return -EINVAL;
ret = drm_bridge_attach(bridge->encoder, tpd->next_bridge,
bridge, flags);
if (ret < 0)
return ret;
gpiod_set_value_cansleep(tpd->ls_oe_gpio, 1);
/* DC-DC converter needs at max 300us to get to 90% of 5V. */
usleep_range(300, 1000);
return 0;
}
static void tpd12s015_detach(struct drm_bridge *bridge)
{
struct tpd12s015_device *tpd = to_tpd12s015(bridge);
gpiod_set_value_cansleep(tpd->ls_oe_gpio, 0);
}
static enum drm_connector_status tpd12s015_detect(struct drm_bridge *bridge)
{
struct tpd12s015_device *tpd = to_tpd12s015(bridge);
if (gpiod_get_value_cansleep(tpd->hpd_gpio))
return connector_status_connected;
else
return connector_status_disconnected;
}
static void tpd12s015_hpd_enable(struct drm_bridge *bridge)
{
struct tpd12s015_device *tpd = to_tpd12s015(bridge);
gpiod_set_value_cansleep(tpd->ct_cp_hpd_gpio, 1);
}
static void tpd12s015_hpd_disable(struct drm_bridge *bridge)
{
struct tpd12s015_device *tpd = to_tpd12s015(bridge);
gpiod_set_value_cansleep(tpd->ct_cp_hpd_gpio, 0);
}
static const struct drm_bridge_funcs tpd12s015_bridge_funcs = {
.attach = tpd12s015_attach,
.detach = tpd12s015_detach,
.detect = tpd12s015_detect,
.hpd_enable = tpd12s015_hpd_enable,
.hpd_disable = tpd12s015_hpd_disable,
};
static irqreturn_t tpd12s015_hpd_isr(int irq, void *data)
{
struct tpd12s015_device *tpd = data;
struct drm_bridge *bridge = &tpd->bridge;
drm_bridge_hpd_notify(bridge, tpd12s015_detect(bridge));
return IRQ_HANDLED;
}
static int tpd12s015_probe(struct platform_device *pdev)
{
struct tpd12s015_device *tpd;
struct device_node *node;
struct gpio_desc *gpio;
int ret;
tpd = devm_kzalloc(&pdev->dev, sizeof(*tpd), GFP_KERNEL);
if (!tpd)
return -ENOMEM;
platform_set_drvdata(pdev, tpd);
tpd->bridge.funcs = &tpd12s015_bridge_funcs;
tpd->bridge.of_node = pdev->dev.of_node;
tpd->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
tpd->bridge.ops = DRM_BRIDGE_OP_DETECT;
/* Get the next bridge, connected to port@1. */
node = of_graph_get_remote_node(pdev->dev.of_node, 1, -1);
if (!node)
return -ENODEV;
tpd->next_bridge = of_drm_find_bridge(node);
of_node_put(node);
if (!tpd->next_bridge)
return -EPROBE_DEFER;
/* Get the control and HPD GPIOs. */
gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
GPIOD_OUT_LOW);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
tpd->ct_cp_hpd_gpio = gpio;
gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
GPIOD_OUT_LOW);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
tpd->ls_oe_gpio = gpio;
gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_IN);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
tpd->hpd_gpio = gpio;
/* Register the IRQ if the HPD GPIO is IRQ-capable. */
tpd->hpd_irq = gpiod_to_irq(tpd->hpd_gpio);
if (tpd->hpd_irq) {
ret = devm_request_threaded_irq(&pdev->dev, tpd->hpd_irq, NULL,
tpd12s015_hpd_isr,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"tpd12s015 hpd", tpd);
if (ret)
return ret;
tpd->bridge.ops |= DRM_BRIDGE_OP_HPD;
}
/* Register the DRM bridge. */
drm_bridge_add(&tpd->bridge);
return 0;
}
static int __exit tpd12s015_remove(struct platform_device *pdev)
{
struct tpd12s015_device *tpd = platform_get_drvdata(pdev);
drm_bridge_remove(&tpd->bridge);
return 0;
}
static const struct of_device_id tpd12s015_of_match[] = {
{ .compatible = "ti,tpd12s015", },
{},
};
MODULE_DEVICE_TABLE(of, tpd12s015_of_match);
static struct platform_driver tpd12s015_driver = {
.probe = tpd12s015_probe,
.remove = __exit_p(tpd12s015_remove),
.driver = {
.name = "tpd12s015",
.of_match_table = tpd12s015_of_match,
},
};
module_platform_driver(tpd12s015_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("TPD12S015 HDMI level shifter and ESD protection driver");
MODULE_LICENSE("GPL");

View File

@ -39,26 +39,56 @@
* encoder chain.
*
* A bridge is always attached to a single &drm_encoder at a time, but can be
* either connected to it directly, or through an intermediate bridge::
* either connected to it directly, or through a chain of bridges::
*
* encoder ---> bridge B ---> bridge A
* [ CRTC ---> ] Encoder ---> Bridge A ---> Bridge B
*
* Here, the output of the encoder feeds to bridge B, and that furthers feeds to
* bridge A.
* Here, the output of the encoder feeds to bridge A, and that furthers feeds to
* bridge B. Bridge chains can be arbitrarily long, and shall be fully linear:
* Chaining multiple bridges to the output of a bridge, or the same bridge to
* the output of different bridges, is not supported.
*
* The driver using the bridge is responsible to make the associations between
* the encoder and bridges. Once these links are made, the bridges will
* participate along with encoder functions to perform mode_set/enable/disable
* through the ops provided in &drm_bridge_funcs.
* Display drivers are responsible for linking encoders with the first bridge
* in the chains. This is done by acquiring the appropriate bridge with
* of_drm_find_bridge() or drm_of_find_panel_or_bridge(), or creating it for a
* panel with drm_panel_bridge_add_typed() (or the managed version
* devm_drm_panel_bridge_add_typed()). Once acquired, the bridge shall be
* attached to the encoder with a call to drm_bridge_attach().
*
* drm_bridge, like drm_panel, aren't drm_mode_object entities like planes,
* Bridges are responsible for linking themselves with the next bridge in the
* chain, if any. This is done the same way as for encoders, with the call to
* drm_bridge_attach() occurring in the &drm_bridge_funcs.attach operation.
*
* Once these links are created, the bridges can participate along with encoder
* functions to perform mode validation and fixup (through
* drm_bridge_chain_mode_valid() and drm_atomic_bridge_chain_check()), mode
* setting (through drm_bridge_chain_mode_set()), enable (through
* drm_atomic_bridge_chain_pre_enable() and drm_atomic_bridge_chain_enable())
* and disable (through drm_atomic_bridge_chain_disable() and
* drm_atomic_bridge_chain_post_disable()). Those functions call the
* corresponding operations provided in &drm_bridge_funcs in sequence for all
* bridges in the chain.
*
* For display drivers that use the atomic helpers
* drm_atomic_helper_check_modeset(),
* drm_atomic_helper_commit_modeset_enables() and
* drm_atomic_helper_commit_modeset_disables() (either directly in hand-rolled
* commit check and commit tail handlers, or through the higher-level
* drm_atomic_helper_check() and drm_atomic_helper_commit_tail() or
* drm_atomic_helper_commit_tail_rpm() helpers), this is done transparently and
* requires no intervention from the driver. For other drivers, the relevant
* DRM bridge chain functions shall be called manually.
*
* Bridges also participate in implementing the &drm_connector at the end of
* the bridge chain. Display drivers may use the drm_bridge_connector_init()
* helper to create the &drm_connector, or implement it manually on top of the
* connector-related operations exposed by the bridge (see the overview
* documentation of bridge operations for more details).
*
* &drm_bridge, like &drm_panel, aren't &drm_mode_object entities like planes,
* CRTCs, encoders or connectors and hence are not visible to userspace. They
* just provide additional hooks to get the desired output at the end of the
* encoder chain.
*
* Bridges can also be chained up using the &drm_bridge.chain_node field.
*
* Both legacy CRTC helpers and the new atomic modeset helpers support bridges.
*/
static DEFINE_MUTEX(bridge_lock);
@ -71,6 +101,8 @@ static LIST_HEAD(bridge_list);
*/
void drm_bridge_add(struct drm_bridge *bridge)
{
mutex_init(&bridge->hpd_mutex);
mutex_lock(&bridge_lock);
list_add_tail(&bridge->list, &bridge_list);
mutex_unlock(&bridge_lock);
@ -87,6 +119,8 @@ void drm_bridge_remove(struct drm_bridge *bridge)
mutex_lock(&bridge_lock);
list_del_init(&bridge->list);
mutex_unlock(&bridge_lock);
mutex_destroy(&bridge->hpd_mutex);
}
EXPORT_SYMBOL(drm_bridge_remove);
@ -121,6 +155,7 @@ static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = {
* @encoder: DRM encoder
* @bridge: bridge to attach
* @previous: previous bridge in the chain (optional)
* @flags: DRM_BRIDGE_ATTACH_* flags
*
* Called by a kms driver to link the bridge to an encoder's chain. The previous
* argument specifies the previous bridge in the chain. If NULL, the bridge is
@ -138,7 +173,8 @@ static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = {
* Zero on success, error code on failure
*/
int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
struct drm_bridge *previous)
struct drm_bridge *previous,
enum drm_bridge_attach_flags flags)
{
int ret;
@ -160,7 +196,7 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
list_add(&bridge->chain_node, &encoder->bridge_chain);
if (bridge->funcs->attach) {
ret = bridge->funcs->attach(bridge);
ret = bridge->funcs->attach(bridge, flags);
if (ret < 0)
goto err_reset_bridge;
}
@ -212,14 +248,92 @@ void drm_bridge_detach(struct drm_bridge *bridge)
}
/**
* DOC: bridge callbacks
* DOC: bridge operations
*
* The &drm_bridge_funcs ops are populated by the bridge driver. The DRM
* internals (atomic and CRTC helpers) use the helpers defined in drm_bridge.c
* These helpers call a specific &drm_bridge_funcs op for all the bridges
* during encoder configuration.
* Bridge drivers expose operations through the &drm_bridge_funcs structure.
* The DRM internals (atomic and CRTC helpers) use the helpers defined in
* drm_bridge.c to call bridge operations. Those operations are divided in
* three big categories to support different parts of the bridge usage.
*
* For detailed specification of the bridge callbacks see &drm_bridge_funcs.
* - The encoder-related operations support control of the bridges in the
* chain, and are roughly counterparts to the &drm_encoder_helper_funcs
* operations. They are used by the legacy CRTC and the atomic modeset
* helpers to perform mode validation, fixup and setting, and enable and
* disable the bridge automatically.
*
* The enable and disable operations are split in
* &drm_bridge_funcs.pre_enable, &drm_bridge_funcs.enable,
* &drm_bridge_funcs.disable and &drm_bridge_funcs.post_disable to provide
* finer-grained control.
*
* Bridge drivers may implement the legacy version of those operations, or
* the atomic version (prefixed with atomic\_), in which case they shall also
* implement the atomic state bookkeeping operations
* (&drm_bridge_funcs.atomic_duplicate_state,
* &drm_bridge_funcs.atomic_destroy_state and &drm_bridge_funcs.reset).
* Mixing atomic and non-atomic versions of the operations is not supported.
*
* - The bus format negotiation operations
* &drm_bridge_funcs.atomic_get_output_bus_fmts and
* &drm_bridge_funcs.atomic_get_input_bus_fmts allow bridge drivers to
* negotiate the formats transmitted between bridges in the chain when
* multiple formats are supported. Negotiation for formats is performed
* transparently for display drivers by the atomic modeset helpers. Only
* atomic versions of those operations exist, bridge drivers that need to
* implement them shall thus also implement the atomic version of the
* encoder-related operations. This feature is not supported by the legacy
* CRTC helpers.
*
* - The connector-related operations support implementing a &drm_connector
* based on a chain of bridges. DRM bridges traditionally create a
* &drm_connector for bridges meant to be used at the end of the chain. This
* puts additional burden on bridge drivers, especially for bridges that may
* be used in the middle of a chain or at the end of it. Furthermore, it
* requires all operations of the &drm_connector to be handled by a single
* bridge, which doesn't always match the hardware architecture.
*
* To simplify bridge drivers and make the connector implementation more
* flexible, a new model allows bridges to unconditionally skip creation of
* &drm_connector and instead expose &drm_bridge_funcs operations to support
* an externally-implemented &drm_connector. Those operations are
* &drm_bridge_funcs.detect, &drm_bridge_funcs.get_modes,
* &drm_bridge_funcs.get_edid, &drm_bridge_funcs.hpd_notify,
* &drm_bridge_funcs.hpd_enable and &drm_bridge_funcs.hpd_disable. When
* implemented, display drivers shall create a &drm_connector instance for
* each chain of bridges, and implement those connector instances based on
* the bridge connector operations.
*
* Bridge drivers shall implement the connector-related operations for all
* the features that the bridge hardware support. For instance, if a bridge
* supports reading EDID, the &drm_bridge_funcs.get_edid shall be
* implemented. This however doesn't mean that the DDC lines are wired to the
* bridge on a particular platform, as they could also be connected to an I2C
* controller of the SoC. Support for the connector-related operations on the
* running platform is reported through the &drm_bridge.ops flags. Bridge
* drivers shall detect which operations they can support on the platform
* (usually this information is provided by ACPI or DT), and set the
* &drm_bridge.ops flags for all supported operations. A flag shall only be
* set if the corresponding &drm_bridge_funcs operation is implemented, but
* an implemented operation doesn't necessarily imply that the corresponding
* flag will be set. Display drivers shall use the &drm_bridge.ops flags to
* decide which bridge to delegate a connector operation to. This mechanism
* allows providing a single static const &drm_bridge_funcs instance in
* bridge drivers, improving security by storing function pointers in
* read-only memory.
*
* In order to ease transition, bridge drivers may support both the old and
* new models by making connector creation optional and implementing the
* connected-related bridge operations. Connector creation is then controlled
* by the flags argument to the drm_bridge_attach() function. Display drivers
* that support the new model and create connectors themselves shall set the
* %DRM_BRIDGE_ATTACH_NO_CONNECTOR flag, and bridge drivers shall then skip
* connector creation. For intermediate bridges in the chain, the flag shall
* be passed to the drm_bridge_attach() call for the downstream bridge.
* Bridge drivers that implement the new model only shall return an error
* from their &drm_bridge_funcs.attach handler when the
* %DRM_BRIDGE_ATTACH_NO_CONNECTOR flag is not set. New display drivers
* should use the new model, and convert the bridge drivers they use if
* needed, in order to gradually transition to the new model.
*/
/**
@ -919,6 +1033,164 @@ int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_check);
/**
* drm_bridge_detect - check if anything is attached to the bridge output
* @bridge: bridge control structure
*
* If the bridge supports output detection, as reported by the
* DRM_BRIDGE_OP_DETECT bridge ops flag, call &drm_bridge_funcs.detect for the
* bridge and return the connection status. Otherwise return
* connector_status_unknown.
*
* RETURNS:
* The detection status on success, or connector_status_unknown if the bridge
* doesn't support output detection.
*/
enum drm_connector_status drm_bridge_detect(struct drm_bridge *bridge)
{
if (!(bridge->ops & DRM_BRIDGE_OP_DETECT))
return connector_status_unknown;
return bridge->funcs->detect(bridge);
}
EXPORT_SYMBOL_GPL(drm_bridge_detect);
/**
* drm_bridge_get_modes - fill all modes currently valid for the sink into the
* @connector
* @bridge: bridge control structure
* @connector: the connector to fill with modes
*
* If the bridge supports output modes retrieval, as reported by the
* DRM_BRIDGE_OP_MODES bridge ops flag, call &drm_bridge_funcs.get_modes to
* fill the connector with all valid modes and return the number of modes
* added. Otherwise return 0.
*
* RETURNS:
* The number of modes added to the connector.
*/
int drm_bridge_get_modes(struct drm_bridge *bridge,
struct drm_connector *connector)
{
if (!(bridge->ops & DRM_BRIDGE_OP_MODES))
return 0;
return bridge->funcs->get_modes(bridge, connector);
}
EXPORT_SYMBOL_GPL(drm_bridge_get_modes);
/**
* drm_bridge_get_edid - get the EDID data of the connected display
* @bridge: bridge control structure
* @connector: the connector to read EDID for
*
* If the bridge supports output EDID retrieval, as reported by the
* DRM_BRIDGE_OP_EDID bridge ops flag, call &drm_bridge_funcs.get_edid to
* get the EDID and return it. Otherwise return ERR_PTR(-ENOTSUPP).
*
* RETURNS:
* The retrieved EDID on success, or an error pointer otherwise.
*/
struct edid *drm_bridge_get_edid(struct drm_bridge *bridge,
struct drm_connector *connector)
{
if (!(bridge->ops & DRM_BRIDGE_OP_EDID))
return ERR_PTR(-ENOTSUPP);
return bridge->funcs->get_edid(bridge, connector);
}
EXPORT_SYMBOL_GPL(drm_bridge_get_edid);
/**
* drm_bridge_hpd_enable - enable hot plug detection for the bridge
* @bridge: bridge control structure
* @cb: hot-plug detection callback
* @data: data to be passed to the hot-plug detection callback
*
* Call &drm_bridge_funcs.hpd_enable if implemented and register the given @cb
* and @data as hot plug notification callback. From now on the @cb will be
* called with @data when an output status change is detected by the bridge,
* until hot plug notification gets disabled with drm_bridge_hpd_disable().
*
* Hot plug detection is supported only if the DRM_BRIDGE_OP_HPD flag is set in
* bridge->ops. This function shall not be called when the flag is not set.
*
* Only one hot plug detection callback can be registered at a time, it is an
* error to call this function when hot plug detection is already enabled for
* the bridge.
*/
void drm_bridge_hpd_enable(struct drm_bridge *bridge,
void (*cb)(void *data,
enum drm_connector_status status),
void *data)
{
if (!(bridge->ops & DRM_BRIDGE_OP_HPD))
return;
mutex_lock(&bridge->hpd_mutex);
if (WARN(bridge->hpd_cb, "Hot plug detection already enabled\n"))
goto unlock;
bridge->hpd_cb = cb;
bridge->hpd_data = data;
if (bridge->funcs->hpd_enable)
bridge->funcs->hpd_enable(bridge);
unlock:
mutex_unlock(&bridge->hpd_mutex);
}
EXPORT_SYMBOL_GPL(drm_bridge_hpd_enable);
/**
* drm_bridge_hpd_disable - disable hot plug detection for the bridge
* @bridge: bridge control structure
*
* Call &drm_bridge_funcs.hpd_disable if implemented and unregister the hot
* plug detection callback previously registered with drm_bridge_hpd_enable().
* Once this function returns the callback will not be called by the bridge
* when an output status change occurs.
*
* Hot plug detection is supported only if the DRM_BRIDGE_OP_HPD flag is set in
* bridge->ops. This function shall not be called when the flag is not set.
*/
void drm_bridge_hpd_disable(struct drm_bridge *bridge)
{
if (!(bridge->ops & DRM_BRIDGE_OP_HPD))
return;
mutex_lock(&bridge->hpd_mutex);
if (bridge->funcs->hpd_disable)
bridge->funcs->hpd_disable(bridge);
bridge->hpd_cb = NULL;
bridge->hpd_data = NULL;
mutex_unlock(&bridge->hpd_mutex);
}
EXPORT_SYMBOL_GPL(drm_bridge_hpd_disable);
/**
* drm_bridge_hpd_notify - notify hot plug detection events
* @bridge: bridge control structure
* @status: output connection status
*
* Bridge drivers shall call this function to report hot plug events when they
* detect a change in the output status, when hot plug detection has been
* enabled by drm_bridge_hpd_enable().
*
* This function shall be called in a context that can sleep.
*/
void drm_bridge_hpd_notify(struct drm_bridge *bridge,
enum drm_connector_status status)
{
mutex_lock(&bridge->hpd_mutex);
if (bridge->hpd_cb)
bridge->hpd_cb(bridge->hpd_data, status);
mutex_unlock(&bridge->hpd_mutex);
}
EXPORT_SYMBOL_GPL(drm_bridge_hpd_notify);
#ifdef CONFIG_OF
/**
* of_drm_find_bridge - find the bridge corresponding to the device node in

View File

@ -0,0 +1,379 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_connector.h>
#include <drm/drm_device.h>
#include <drm/drm_edid.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_probe_helper.h>
/**
* DOC: overview
*
* The DRM bridge connector helper object provides a DRM connector
* implementation that wraps a chain of &struct drm_bridge. The connector
* operations are fully implemented based on the operations of the bridges in
* the chain, and don't require any intervention from the display controller
* driver at runtime.
*
* To use the helper, display controller drivers create a bridge connector with
* a call to drm_bridge_connector_init(). This associates the newly created
* connector with the chain of bridges passed to the function and registers it
* with the DRM device. At that point the connector becomes fully usable, no
* further operation is needed.
*
* The DRM bridge connector operations are implemented based on the operations
* provided by the bridges in the chain. Each connector operation is delegated
* to the bridge closest to the connector (at the end of the chain) that
* provides the relevant functionality.
*
* To make use of this helper, all bridges in the chain shall report bridge
* operation flags (&drm_bridge->ops) and bridge output type
* (&drm_bridge->type), as well as the DRM_BRIDGE_ATTACH_NO_CONNECTOR attach
* flag (none of the bridges shall create a DRM connector directly).
*/
/**
* struct drm_bridge_connector - A connector backed by a chain of bridges
*/
struct drm_bridge_connector {
/**
* @base: The base DRM connector
*/
struct drm_connector base;
/**
* @encoder:
*
* The encoder at the start of the bridges chain.
*/
struct drm_encoder *encoder;
/**
* @bridge_edid:
*
* The last bridge in the chain (closest to the connector) that provides
* EDID read support, if any (see &DRM_BRIDGE_OP_EDID).
*/
struct drm_bridge *bridge_edid;
/**
* @bridge_hpd:
*
* The last bridge in the chain (closest to the connector) that provides
* hot-plug detection notification, if any (see &DRM_BRIDGE_OP_HPD).
*/
struct drm_bridge *bridge_hpd;
/**
* @bridge_detect:
*
* The last bridge in the chain (closest to the connector) that provides
* connector detection, if any (see &DRM_BRIDGE_OP_DETECT).
*/
struct drm_bridge *bridge_detect;
/**
* @bridge_modes:
*
* The last bridge in the chain (closest to the connector) that provides
* connector modes detection, if any (see &DRM_BRIDGE_OP_MODES).
*/
struct drm_bridge *bridge_modes;
};
#define to_drm_bridge_connector(x) \
container_of(x, struct drm_bridge_connector, base)
/* -----------------------------------------------------------------------------
* Bridge Connector Hot-Plug Handling
*/
static void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
enum drm_connector_status status)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;
/* Notify all bridges in the pipeline of hotplug events. */
drm_for_each_bridge_in_chain(bridge_connector->encoder, bridge) {
if (bridge->funcs->hpd_notify)
bridge->funcs->hpd_notify(bridge, status);
}
}
static void drm_bridge_connector_hpd_cb(void *cb_data,
enum drm_connector_status status)
{
struct drm_bridge_connector *drm_bridge_connector = cb_data;
struct drm_connector *connector = &drm_bridge_connector->base;
struct drm_device *dev = connector->dev;
enum drm_connector_status old_status;
mutex_lock(&dev->mode_config.mutex);
old_status = connector->status;
connector->status = status;
mutex_unlock(&dev->mode_config.mutex);
if (old_status == status)
return;
drm_bridge_connector_hpd_notify(connector, status);
drm_kms_helper_hotplug_event(dev);
}
/**
* drm_bridge_connector_enable_hpd - Enable hot-plug detection for the connector
* @connector: The DRM bridge connector
*
* This function enables hot-plug detection for the given bridge connector.
* This is typically used by display drivers in their resume handler.
*/
void drm_bridge_connector_enable_hpd(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *hpd = bridge_connector->bridge_hpd;
if (hpd)
drm_bridge_hpd_enable(hpd, drm_bridge_connector_hpd_cb,
bridge_connector);
}
EXPORT_SYMBOL_GPL(drm_bridge_connector_enable_hpd);
/**
* drm_bridge_connector_disable_hpd - Disable hot-plug detection for the
* connector
* @connector: The DRM bridge connector
*
* This function disables hot-plug detection for the given bridge connector.
* This is typically used by display drivers in their suspend handler.
*/
void drm_bridge_connector_disable_hpd(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *hpd = bridge_connector->bridge_hpd;
if (hpd)
drm_bridge_hpd_disable(hpd);
}
EXPORT_SYMBOL_GPL(drm_bridge_connector_disable_hpd);
/* -----------------------------------------------------------------------------
* Bridge Connector Functions
*/
static enum drm_connector_status
drm_bridge_connector_detect(struct drm_connector *connector, bool force)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *detect = bridge_connector->bridge_detect;
enum drm_connector_status status;
if (detect) {
status = detect->funcs->detect(detect);
drm_bridge_connector_hpd_notify(connector, status);
} else {
switch (connector->connector_type) {
case DRM_MODE_CONNECTOR_DPI:
case DRM_MODE_CONNECTOR_LVDS:
case DRM_MODE_CONNECTOR_DSI:
status = connector_status_connected;
break;
default:
status = connector_status_unknown;
break;
}
}
return status;
}
static void drm_bridge_connector_destroy(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
if (bridge_connector->bridge_hpd) {
struct drm_bridge *hpd = bridge_connector->bridge_hpd;
drm_bridge_hpd_disable(hpd);
}
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(bridge_connector);
}
static const struct drm_connector_funcs drm_bridge_connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.detect = drm_bridge_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_bridge_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
/* -----------------------------------------------------------------------------
* Bridge Connector Helper Functions
*/
static int drm_bridge_connector_get_modes_edid(struct drm_connector *connector,
struct drm_bridge *bridge)
{
enum drm_connector_status status;
struct edid *edid;
int n;
status = drm_bridge_connector_detect(connector, false);
if (status != connector_status_connected)
goto no_edid;
edid = bridge->funcs->get_edid(bridge, connector);
if (!edid || !drm_edid_is_valid(edid)) {
kfree(edid);
goto no_edid;
}
drm_connector_update_edid_property(connector, edid);
n = drm_add_edid_modes(connector, edid);
kfree(edid);
return n;
no_edid:
drm_connector_update_edid_property(connector, NULL);
return 0;
}
static int drm_bridge_connector_get_modes(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_bridge *bridge;
/*
* If display exposes EDID, then we parse that in the normal way to
* build table of supported modes.
*/
bridge = bridge_connector->bridge_edid;
if (bridge)
return drm_bridge_connector_get_modes_edid(connector, bridge);
/*
* Otherwise if the display pipeline reports modes (e.g. with a fixed
* resolution panel or an analog TV output), query it.
*/
bridge = bridge_connector->bridge_modes;
if (bridge)
return bridge->funcs->get_modes(bridge, connector);
/*
* We can't retrieve modes, which can happen for instance for a DVI or
* VGA output with the DDC bus unconnected. The KMS core will add the
* default modes.
*/
return 0;
}
static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs = {
.get_modes = drm_bridge_connector_get_modes,
/* No need for .mode_valid(), the bridges are checked by the core. */
};
/* -----------------------------------------------------------------------------
* Bridge Connector Initialisation
*/
/**
* drm_bridge_connector_init - Initialise a connector for a chain of bridges
* @drm: the DRM device
* @encoder: the encoder where the bridge chain starts
*
* Allocate, initialise and register a &drm_bridge_connector with the @drm
* device. The connector is associated with a chain of bridges that starts at
* the @encoder. All bridges in the chain shall report bridge operation flags
* (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of
* them may create a DRM connector directly.
*
* Returns a pointer to the new connector on success, or a negative error
* pointer otherwise.
*/
struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
struct drm_encoder *encoder)
{
struct drm_bridge_connector *bridge_connector;
struct drm_connector *connector;
struct i2c_adapter *ddc = NULL;
struct drm_bridge *bridge;
int connector_type;
bridge_connector = kzalloc(sizeof(*bridge_connector), GFP_KERNEL);
if (!bridge_connector)
return ERR_PTR(-ENOMEM);
bridge_connector->encoder = encoder;
/*
* TODO: Handle doublescan_allowed, stereo_allowed and
* ycbcr_420_allowed.
*/
connector = &bridge_connector->base;
connector->interlace_allowed = true;
/*
* Initialise connector status handling. First locate the furthest
* bridges in the pipeline that support HPD and output detection. Then
* initialise the connector polling mode, using HPD if available and
* falling back to polling if supported. If neither HPD nor output
* detection are available, we don't support hotplug detection at all.
*/
connector_type = DRM_MODE_CONNECTOR_Unknown;
drm_for_each_bridge_in_chain(encoder, bridge) {
if (!bridge->interlace_allowed)
connector->interlace_allowed = false;
if (bridge->ops & DRM_BRIDGE_OP_EDID)
bridge_connector->bridge_edid = bridge;
if (bridge->ops & DRM_BRIDGE_OP_HPD)
bridge_connector->bridge_hpd = bridge;
if (bridge->ops & DRM_BRIDGE_OP_DETECT)
bridge_connector->bridge_detect = bridge;
if (bridge->ops & DRM_BRIDGE_OP_MODES)
bridge_connector->bridge_modes = bridge;
if (!drm_bridge_get_next_bridge(bridge))
connector_type = bridge->type;
if (bridge->ddc)
ddc = bridge->ddc;
}
if (connector_type == DRM_MODE_CONNECTOR_Unknown) {
kfree(bridge_connector);
return ERR_PTR(-EINVAL);
}
drm_connector_init_with_ddc(drm, connector, &drm_bridge_connector_funcs,
connector_type, ddc);
drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
if (bridge_connector->bridge_hpd)
connector->polled = DRM_CONNECTOR_POLL_HPD;
else if (bridge_connector->bridge_detect)
connector->polled = DRM_CONNECTOR_POLL_CONNECT
| DRM_CONNECTOR_POLL_DISCONNECT;
return connector;
}
EXPORT_SYMBOL_GPL(drm_bridge_connector_init);

View File

@ -111,6 +111,21 @@ void drm_connector_ida_destroy(void)
ida_destroy(&drm_connector_enum_list[i].ida);
}
/**
* drm_get_connector_type_name - return a string for connector type
* @type: The connector type (DRM_MODE_CONNECTOR_*)
*
* Returns: the name of the connector type, or NULL if the type is not valid.
*/
const char *drm_get_connector_type_name(unsigned int type)
{
if (type < ARRAY_SIZE(drm_connector_enum_list))
return drm_connector_enum_list[type].name;
return NULL;
}
EXPORT_SYMBOL(drm_get_connector_type_name);
/**
* drm_connector_get_cmdline_mode - reads the user's cmdline mode
* @connector: connector to quwery

View File

@ -4647,6 +4647,9 @@ EXPORT_SYMBOL(drm_av_sync_delay);
*
* Parse the CEA extension according to CEA-861-B.
*
* Drivers that have added the modes parsed from EDID to drm_display_info
* should use &drm_display_info.is_hdmi instead of calling this function.
*
* Return: True if the monitor is HDMI, false if not or unknown.
*/
bool drm_detect_hdmi_monitor(struct edid *edid)
@ -4881,6 +4884,8 @@ drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db)
struct drm_display_info *info = &connector->display_info;
u8 len = cea_db_payload_len(db);
info->is_hdmi = true;
if (len >= 6)
info->dvi_dual = db[6] & 1;
if (len >= 7)
@ -4949,6 +4954,7 @@ drm_reset_display_info(struct drm_connector *connector)
info->cea_rev = 0;
info->max_tmds_clock = 0;
info->dvi_dual = false;
info->is_hdmi = false;
info->has_hdmi_infoframe = false;
info->rgb_quant_range_selectable = false;
memset(&info->hdmi, 0, sizeof(info->hdmi));
@ -5449,14 +5455,11 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
{
enum hdmi_picture_aspect picture_aspect;
u8 vic, hdmi_vic;
int err;
if (!frame || !mode)
return -EINVAL;
err = hdmi_avi_infoframe_init(frame);
if (err < 0)
return err;
hdmi_avi_infoframe_init(frame);
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
frame->pixel_repeat = 1;

View File

@ -229,7 +229,7 @@ static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
struct drm_bridge *bridge)
{
return drm_bridge_attach(&pipe->encoder, bridge, NULL);
return drm_bridge_attach(&pipe->encoder, bridge, NULL, 0);
}
EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);

View File

@ -106,7 +106,8 @@ static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data,
/* Pre-empt DP connector creation if there's a bridge */
if (dp->ptn_bridge) {
ret = drm_bridge_attach(&dp->encoder, dp->ptn_bridge, bridge);
ret = drm_bridge_attach(&dp->encoder, dp->ptn_bridge, bridge,
0);
if (ret) {
DRM_DEV_ERROR(dp->dev,
"Failed to attach bridge to drm\n");

View File

@ -1540,7 +1540,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
out_bridge = of_drm_find_bridge(device->dev.of_node);
if (out_bridge) {
drm_bridge_attach(encoder, out_bridge, NULL);
drm_bridge_attach(encoder, out_bridge, NULL, 0);
dsi->out_bridge = out_bridge;
list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
} else {
@ -1717,7 +1717,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
if (dsi->in_bridge_node) {
in_bridge = of_drm_find_bridge(dsi->in_bridge_node);
if (in_bridge)
drm_bridge_attach(encoder, in_bridge, NULL);
drm_bridge_attach(encoder, in_bridge, NULL, 0);
}
return mipi_dsi_host_register(&dsi->dsi_host);

View File

@ -960,7 +960,7 @@ static int hdmi_create_connector(struct drm_encoder *encoder)
drm_connector_attach_encoder(connector, encoder);
if (hdata->bridge) {
ret = drm_bridge_attach(encoder, hdata->bridge, NULL);
ret = drm_bridge_attach(encoder, hdata->bridge, NULL, 0);
if (ret)
DRM_DEV_ERROR(hdata->dev, "Failed to attach bridge\n");
}

View File

@ -151,5 +151,5 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev)
return fsl_dcu_attach_panel(fsl_dev, panel);
}
return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL);
return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL, 0);
}

View File

@ -40,6 +40,7 @@ struct hibmc_dislay_pll_config {
};
static const struct hibmc_dislay_pll_config hibmc_pll_table[] = {
{640, 480, CRT_PLL1_HS_25MHZ, CRT_PLL2_HS_25MHZ},
{800, 600, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ},
{1024, 768, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ},
{1152, 864, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ},
@ -47,6 +48,8 @@ static const struct hibmc_dislay_pll_config hibmc_pll_table[] = {
{1280, 720, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ},
{1280, 960, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ},
{1280, 1024, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ},
{1440, 900, CRT_PLL1_HS_106MHZ, CRT_PLL2_HS_106MHZ},
{1600, 900, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ},
{1600, 1200, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ},
{1920, 1080, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ},
{1920, 1200, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ},
@ -240,6 +243,25 @@ static void hibmc_crtc_atomic_disable(struct drm_crtc *crtc,
hibmc_set_current_gate(priv, reg);
}
static enum drm_mode_status
hibmc_crtc_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode)
{
int i = 0;
int vrefresh = drm_mode_vrefresh(mode);
if (vrefresh < 59 || vrefresh > 61)
return MODE_NOCLOCK;
for (i = 0; i < ARRAY_SIZE(hibmc_pll_table); i++) {
if (hibmc_pll_table[i].hdisplay == mode->hdisplay &&
hibmc_pll_table[i].vdisplay == mode->vdisplay)
return MODE_OK;
}
return MODE_BAD;
}
static unsigned int format_pll_reg(void)
{
unsigned int pllreg = 0;
@ -508,6 +530,7 @@ static const struct drm_crtc_helper_funcs hibmc_crtc_helper_funcs = {
.atomic_flush = hibmc_crtc_atomic_flush,
.atomic_enable = hibmc_crtc_atomic_enable,
.atomic_disable = hibmc_crtc_atomic_disable,
.mode_valid = hibmc_crtc_mode_valid,
};
int hibmc_de_init(struct hibmc_drm_private *priv)

View File

@ -91,11 +91,11 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv)
priv->dev->mode_config.min_width = 0;
priv->dev->mode_config.min_height = 0;
priv->dev->mode_config.max_width = 1920;
priv->dev->mode_config.max_height = 1440;
priv->dev->mode_config.max_height = 1200;
priv->dev->mode_config.fb_base = priv->fb_base;
priv->dev->mode_config.preferred_depth = 24;
priv->dev->mode_config.prefer_shadow = 0;
priv->dev->mode_config.prefer_shadow = 1;
priv->dev->mode_config.funcs = (void *)&hibmc_mode_funcs;
@ -327,6 +327,11 @@ static int hibmc_pci_probe(struct pci_dev *pdev,
struct drm_device *dev;
int ret;
ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev,
"hibmcdrmfb");
if (ret)
return ret;
dev = drm_dev_alloc(&hibmc_driver, &pdev->dev);
if (IS_ERR(dev)) {
DRM_ERROR("failed to allocate drm_device\n");

View File

@ -179,6 +179,7 @@
#define CRT_PLL1_HS_74MHZ 0x23941dc2
#define CRT_PLL1_HS_80MHZ 0x23941001
#define CRT_PLL1_HS_80MHZ_1152 0x23540fc2
#define CRT_PLL1_HS_106MHZ 0x237C1641
#define CRT_PLL1_HS_108MHZ 0x23b41b01
#define CRT_PLL1_HS_162MHZ 0x23480681
#define CRT_PLL1_HS_148MHZ 0x23541dc2
@ -191,6 +192,7 @@
#define CRT_PLL2_HS_78MHZ 0x50E147AE
#define CRT_PLL2_HS_74MHZ 0x602B6AE7
#define CRT_PLL2_HS_80MHZ 0x70000000
#define CRT_PLL2_HS_106MHZ 0x0075c28f
#define CRT_PLL2_HS_108MHZ 0x80000000
#define CRT_PLL2_HS_162MHZ 0xA0000000
#define CRT_PLL2_HS_148MHZ 0xB0CCCCCD

View File

@ -11,8 +11,10 @@
* Jianhua Li <lijianhua@huawei.com>
*/
#include <drm/drm_gem_vram_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_print.h>
#include "hibmc_drm_drv.h"
@ -20,7 +22,14 @@
static int hibmc_connector_get_modes(struct drm_connector *connector)
{
return drm_add_modes_noedid(connector, 800, 600);
int count;
count = drm_add_modes_noedid(connector,
connector->dev->mode_config.max_width,
connector->dev->mode_config.max_height);
drm_set_preferred_mode(connector, 1024, 768);
return count;
}
static enum drm_mode_status hibmc_connector_mode_valid(struct drm_connector *connector,

View File

@ -777,7 +777,7 @@ static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi)
int ret;
/* associate the bridge to dsi encoder */
ret = drm_bridge_attach(encoder, bridge, NULL);
ret = drm_bridge_attach(encoder, bridge, NULL, 0);
if (ret) {
DRM_ERROR("failed to attach external bridge\n");
return ret;

View File

@ -1356,10 +1356,16 @@ static int tda998x_connector_init(struct tda998x_priv *priv,
/* DRM bridge functions */
static int tda998x_bridge_attach(struct drm_bridge *bridge)
static int tda998x_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge);
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
return tda998x_connector_init(priv, bridge->dev);
}
@ -2022,7 +2028,7 @@ static int tda998x_encoder_init(struct device *dev, struct drm_device *drm)
if (ret)
goto err_encoder;
ret = drm_bridge_attach(&priv->encoder, &priv->bridge, NULL);
ret = drm_bridge_attach(&priv->encoder, &priv->bridge, NULL, 0);
if (ret)
goto err_bridge;

View File

@ -446,7 +446,7 @@ static int imx_ldb_register(struct drm_device *drm,
if (imx_ldb_ch->bridge) {
ret = drm_bridge_attach(&imx_ldb_ch->encoder,
imx_ldb_ch->bridge, NULL);
imx_ldb_ch->bridge, NULL, 0);
if (ret) {
DRM_ERROR("Failed to initialize bridge with drm\n");
return ret;

View File

@ -292,7 +292,7 @@ static int imx_pd_register(struct drm_device *drm,
DRM_MODE_ENCODER_NONE, NULL);
imxpd->bridge.funcs = &imx_pd_bridge_funcs;
drm_bridge_attach(encoder, &imxpd->bridge, NULL);
drm_bridge_attach(encoder, &imxpd->bridge, NULL, 0);
if (!imxpd->next_bridge) {
drm_connector_helper_add(&imxpd->connector,
@ -307,7 +307,7 @@ static int imx_pd_register(struct drm_device *drm,
if (imxpd->next_bridge) {
ret = drm_bridge_attach(encoder, imxpd->next_bridge,
&imxpd->bridge);
&imxpd->bridge, 0);
if (ret < 0) {
dev_err(imxpd->dev, "failed to attach bridge: %d\n",
ret);

View File

@ -737,7 +737,7 @@ static int ingenic_drm_probe(struct platform_device *pdev)
return ret;
}
ret = drm_bridge_attach(&priv->encoder, bridge, NULL);
ret = drm_bridge_attach(&priv->encoder, bridge, NULL, 0);
if (ret) {
dev_err(dev, "Unable to attach bridge");
return ret;

View File

@ -986,7 +986,8 @@ static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
clk_disable_unprepare(d->lp_clk);
}
static int mcde_dsi_bridge_attach(struct drm_bridge *bridge)
static int mcde_dsi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
struct drm_device *drm = bridge->dev;
@ -998,7 +999,7 @@ static int mcde_dsi_bridge_attach(struct drm_bridge *bridge)
}
/* Attach the DSI bridge to the output (panel etc) bridge */
ret = drm_bridge_attach(bridge->encoder, d->bridge_out, bridge);
ret = drm_bridge_attach(bridge->encoder, d->bridge_out, bridge, flags);
if (ret) {
dev_err(d->dev, "failed to attach the DSI bridge\n");
return ret;

View File

@ -607,7 +607,7 @@ static int mtk_dpi_bind(struct device *dev, struct device *master, void *data)
/* Currently DPI0 is fixed to be driven by OVL1 */
dpi->encoder.possible_crtcs = BIT(1);
ret = drm_bridge_attach(&dpi->encoder, dpi->bridge, NULL);
ret = drm_bridge_attach(&dpi->encoder, dpi->bridge, NULL, 0);
if (ret) {
dev_err(dev, "Failed to attach bridge: %d\n", ret);
goto err_cleanup;

View File

@ -904,7 +904,7 @@ static int mtk_dsi_create_conn_enc(struct drm_device *drm, struct mtk_dsi *dsi)
/* If there's a bridge, attach to it and let it create the connector */
if (dsi->bridge) {
ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL);
ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL, 0);
if (ret) {
DRM_ERROR("Failed to attach bridge to drm\n");
goto err_encoder_cleanup;

View File

@ -1297,11 +1297,17 @@ static void mtk_hdmi_hpd_event(bool hpd, struct device *dev)
* Bridge callbacks
*/
static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge)
static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
ret = drm_connector_init_with_ddc(bridge->encoder->dev, &hdmi->conn,
&mtk_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA,
@ -1326,7 +1332,7 @@ static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge)
if (hdmi->next_bridge) {
ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge,
bridge);
bridge, flags);
if (ret) {
dev_err(hdmi->dev,
"Failed to attach external bridge: %d\n", ret);

View File

@ -684,7 +684,7 @@ struct drm_bridge *msm_dsi_manager_bridge_init(u8 id)
bridge = &dsi_bridge->base;
bridge->funcs = &dsi_mgr_bridge_funcs;
ret = drm_bridge_attach(encoder, bridge, NULL);
ret = drm_bridge_attach(encoder, bridge, NULL, 0);
if (ret)
goto fail;
@ -713,7 +713,7 @@ struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id)
encoder = msm_dsi->encoder;
/* link the internal dsi bridge to the external bridge */
drm_bridge_attach(encoder, ext_bridge, int_bridge);
drm_bridge_attach(encoder, ext_bridge, int_bridge, 0);
/*
* we need the drm_connector created by the external bridge

View File

@ -178,7 +178,7 @@ int msm_edp_modeset_init(struct msm_edp *edp, struct drm_device *dev,
goto fail;
}
ret = drm_bridge_attach(encoder, edp->bridge, NULL);
ret = drm_bridge_attach(encoder, edp->bridge, NULL, 0);
if (ret)
goto fail;

View File

@ -97,7 +97,7 @@ struct drm_bridge *msm_edp_bridge_init(struct msm_edp *edp)
bridge = &edp_bridge->base;
bridge->funcs = &edp_bridge_funcs;
ret = drm_bridge_attach(edp->encoder, bridge, NULL);
ret = drm_bridge_attach(edp->encoder, bridge, NULL, 0);
if (ret)
goto fail;

View File

@ -327,7 +327,7 @@ int msm_hdmi_modeset_init(struct hdmi *hdmi,
goto fail;
}
ret = drm_bridge_attach(encoder, hdmi->bridge, NULL);
ret = drm_bridge_attach(encoder, hdmi->bridge, NULL, 0);
if (ret)
goto fail;

View File

@ -287,7 +287,7 @@ struct drm_bridge *msm_hdmi_bridge_init(struct hdmi *hdmi)
bridge = &hdmi_bridge->base;
bridge->funcs = &msm_hdmi_bridge_funcs;
ret = drm_bridge_attach(hdmi->encoder, bridge, NULL);
ret = drm_bridge_attach(hdmi->encoder, bridge, NULL, 0);
if (ret)
goto fail;

View File

@ -1,28 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "OMAPDRM External Display Device Drivers"
config DRM_OMAP_ENCODER_OPA362
tristate "OPA362 external analog amplifier"
help
Driver for OPA362 external analog TV amplifier controlled
through a GPIO.
config DRM_OMAP_ENCODER_TPD12S015
tristate "TPD12S015 HDMI ESD protection and level shifter"
help
Driver for TPD12S015, which offers HDMI ESD protection and level
shifting.
config DRM_OMAP_CONNECTOR_HDMI
tristate "HDMI Connector"
help
Driver for a generic HDMI connector.
config DRM_OMAP_CONNECTOR_ANALOG_TV
tristate "Analog TV Connector"
help
Driver for a generic analog TV connector.
config DRM_OMAP_PANEL_DSI_CM
tristate "Generic DSI Command Mode Panel"
depends on BACKLIGHT_CLASS_DEVICE

View File

@ -1,6 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DRM_OMAP_ENCODER_OPA362) += encoder-opa362.o
obj-$(CONFIG_DRM_OMAP_ENCODER_TPD12S015) += encoder-tpd12s015.o
obj-$(CONFIG_DRM_OMAP_CONNECTOR_HDMI) += connector-hdmi.o
obj-$(CONFIG_DRM_OMAP_CONNECTOR_ANALOG_TV) += connector-analog-tv.o
obj-$(CONFIG_DRM_OMAP_PANEL_DSI_CM) += panel-dsi-cm.o

View File

@ -1,97 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog TV Connector driver
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include "../dss/omapdss.h"
struct panel_drv_data {
struct omap_dss_device dssdev;
struct device *dev;
};
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int tvc_connect(struct omap_dss_device *src,
struct omap_dss_device *dst)
{
return 0;
}
static void tvc_disconnect(struct omap_dss_device *src,
struct omap_dss_device *dst)
{
}
static const struct omap_dss_device_ops tvc_ops = {
.connect = tvc_connect,
.disconnect = tvc_disconnect,
};
static int tvc_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
ddata->dev = &pdev->dev;
dssdev = &ddata->dssdev;
dssdev->ops = &tvc_ops;
dssdev->dev = &pdev->dev;
dssdev->type = OMAP_DISPLAY_TYPE_VENC;
dssdev->display = true;
dssdev->owner = THIS_MODULE;
dssdev->of_ports = BIT(0);
omapdss_display_init(dssdev);
omapdss_device_register(dssdev);
return 0;
}
static int __exit tvc_remove(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
omapdss_device_unregister(&ddata->dssdev);
return 0;
}
static const struct of_device_id tvc_of_match[] = {
{ .compatible = "omapdss,svideo-connector", },
{ .compatible = "omapdss,composite-video-connector", },
{},
};
MODULE_DEVICE_TABLE(of, tvc_of_match);
static struct platform_driver tvc_connector_driver = {
.probe = tvc_probe,
.remove = __exit_p(tvc_remove),
.driver = {
.name = "connector-analog-tv",
.of_match_table = tvc_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(tvc_connector_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("Analog TV Connector driver");
MODULE_LICENSE("GPL");

View File

@ -1,183 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* HDMI Connector driver
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*/
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "../dss/omapdss.h"
struct panel_drv_data {
struct omap_dss_device dssdev;
void (*hpd_cb)(void *cb_data, enum drm_connector_status status);
void *hpd_cb_data;
struct mutex hpd_lock;
struct device *dev;
struct gpio_desc *hpd_gpio;
};
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int hdmic_connect(struct omap_dss_device *src,
struct omap_dss_device *dst)
{
return 0;
}
static void hdmic_disconnect(struct omap_dss_device *src,
struct omap_dss_device *dst)
{
}
static bool hdmic_detect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
return gpiod_get_value_cansleep(ddata->hpd_gpio);
}
static void hdmic_register_hpd_cb(struct omap_dss_device *dssdev,
void (*cb)(void *cb_data,
enum drm_connector_status status),
void *cb_data)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
mutex_lock(&ddata->hpd_lock);
ddata->hpd_cb = cb;
ddata->hpd_cb_data = cb_data;
mutex_unlock(&ddata->hpd_lock);
}
static void hdmic_unregister_hpd_cb(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
mutex_lock(&ddata->hpd_lock);
ddata->hpd_cb = NULL;
ddata->hpd_cb_data = NULL;
mutex_unlock(&ddata->hpd_lock);
}
static const struct omap_dss_device_ops hdmic_ops = {
.connect = hdmic_connect,
.disconnect = hdmic_disconnect,
.detect = hdmic_detect,
.register_hpd_cb = hdmic_register_hpd_cb,
.unregister_hpd_cb = hdmic_unregister_hpd_cb,
};
static irqreturn_t hdmic_hpd_isr(int irq, void *data)
{
struct panel_drv_data *ddata = data;
mutex_lock(&ddata->hpd_lock);
if (ddata->hpd_cb) {
enum drm_connector_status status;
if (hdmic_detect(&ddata->dssdev))
status = connector_status_connected;
else
status = connector_status_disconnected;
ddata->hpd_cb(ddata->hpd_cb_data, status);
}
mutex_unlock(&ddata->hpd_lock);
return IRQ_HANDLED;
}
static int hdmic_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
struct gpio_desc *gpio;
int r;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
ddata->dev = &pdev->dev;
mutex_init(&ddata->hpd_lock);
/* HPD GPIO */
gpio = devm_gpiod_get_optional(&pdev->dev, "hpd", GPIOD_IN);
if (IS_ERR(gpio)) {
dev_err(&pdev->dev, "failed to parse HPD gpio\n");
return PTR_ERR(gpio);
}
ddata->hpd_gpio = gpio;
if (ddata->hpd_gpio) {
r = devm_request_threaded_irq(&pdev->dev,
gpiod_to_irq(ddata->hpd_gpio),
NULL, hdmic_hpd_isr,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"hdmic hpd", ddata);
if (r)
return r;
}
dssdev = &ddata->dssdev;
dssdev->ops = &hdmic_ops;
dssdev->dev = &pdev->dev;
dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
dssdev->display = true;
dssdev->owner = THIS_MODULE;
dssdev->of_ports = BIT(0);
dssdev->ops_flags = ddata->hpd_gpio
? OMAP_DSS_DEVICE_OP_DETECT | OMAP_DSS_DEVICE_OP_HPD
: 0;
omapdss_display_init(dssdev);
omapdss_device_register(dssdev);
return 0;
}
static int __exit hdmic_remove(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
omapdss_device_unregister(&ddata->dssdev);
return 0;
}
static const struct of_device_id hdmic_of_match[] = {
{ .compatible = "omapdss,hdmi-connector", },
{},
};
MODULE_DEVICE_TABLE(of, hdmic_of_match);
static struct platform_driver hdmi_connector_driver = {
.probe = hdmic_probe,
.remove = __exit_p(hdmic_remove),
.driver = {
.name = "connector-hdmi",
.of_match_table = hdmic_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(hdmi_connector_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("HDMI Connector driver");
MODULE_LICENSE("GPL");

View File

@ -1,137 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* OPA362 analog video amplifier with output/power control
*
* Copyright (C) 2014 Golden Delicious Computers
* Author: H. Nikolaus Schaller <hns@goldelico.com>
*
* based on encoder-tfp410
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*/
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "../dss/omapdss.h"
struct panel_drv_data {
struct omap_dss_device dssdev;
struct gpio_desc *enable_gpio;
};
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int opa362_connect(struct omap_dss_device *src,
struct omap_dss_device *dst)
{
return omapdss_device_connect(dst->dss, dst, dst->next);
}
static void opa362_disconnect(struct omap_dss_device *src,
struct omap_dss_device *dst)
{
omapdss_device_disconnect(dst, dst->next);
}
static void opa362_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
if (ddata->enable_gpio)
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
}
static void opa362_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
if (ddata->enable_gpio)
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
}
static const struct omap_dss_device_ops opa362_ops = {
.connect = opa362_connect,
.disconnect = opa362_disconnect,
.enable = opa362_enable,
.disable = opa362_disable,
};
static int opa362_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
struct gpio_desc *gpio;
dev_dbg(&pdev->dev, "probe\n");
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
ddata->enable_gpio = gpio;
dssdev = &ddata->dssdev;
dssdev->ops = &opa362_ops;
dssdev->dev = &pdev->dev;
dssdev->type = OMAP_DISPLAY_TYPE_VENC;
dssdev->owner = THIS_MODULE;
dssdev->of_ports = BIT(1) | BIT(0);
dssdev->next = omapdss_of_find_connected_device(pdev->dev.of_node, 1);
if (IS_ERR(dssdev->next)) {
if (PTR_ERR(dssdev->next) != -EPROBE_DEFER)
dev_err(&pdev->dev, "failed to find video sink\n");
return PTR_ERR(dssdev->next);
}
omapdss_device_register(dssdev);
return 0;
}
static int __exit opa362_remove(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev = &ddata->dssdev;
if (dssdev->next)
omapdss_device_put(dssdev->next);
omapdss_device_unregister(&ddata->dssdev);
opa362_disable(dssdev);
return 0;
}
static const struct of_device_id opa362_of_match[] = {
{ .compatible = "omapdss,ti,opa362", },
{},
};
MODULE_DEVICE_TABLE(of, opa362_of_match);
static struct platform_driver opa362_driver = {
.probe = opa362_probe,
.remove = __exit_p(opa362_remove),
.driver = {
.name = "amplifier-opa362",
.of_match_table = opa362_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(opa362_driver);
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
MODULE_DESCRIPTION("OPA362 analog video amplifier with output/power control");
MODULE_LICENSE("GPL v2");

View File

@ -1,217 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* TPD12S015 HDMI ESD protection & level shifter chip driver
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*/
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/mutex.h>
#include "../dss/omapdss.h"
struct panel_drv_data {
struct omap_dss_device dssdev;
void (*hpd_cb)(void *cb_data, enum drm_connector_status status);
void *hpd_cb_data;
struct mutex hpd_lock;
struct gpio_desc *ct_cp_hpd_gpio;
struct gpio_desc *ls_oe_gpio;
struct gpio_desc *hpd_gpio;
};
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int tpd_connect(struct omap_dss_device *src,
struct omap_dss_device *dst)
{
struct panel_drv_data *ddata = to_panel_data(dst);
int r;
r = omapdss_device_connect(dst->dss, dst, dst->next);
if (r)
return r;
gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
/* DC-DC converter needs at max 300us to get to 90% of 5V */
udelay(300);
return 0;
}
static void tpd_disconnect(struct omap_dss_device *src,
struct omap_dss_device *dst)
{
struct panel_drv_data *ddata = to_panel_data(dst);
gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
omapdss_device_disconnect(dst, dst->next);
}
static bool tpd_detect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
return gpiod_get_value_cansleep(ddata->hpd_gpio);
}
static void tpd_register_hpd_cb(struct omap_dss_device *dssdev,
void (*cb)(void *cb_data,
enum drm_connector_status status),
void *cb_data)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
mutex_lock(&ddata->hpd_lock);
ddata->hpd_cb = cb;
ddata->hpd_cb_data = cb_data;
mutex_unlock(&ddata->hpd_lock);
}
static void tpd_unregister_hpd_cb(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
mutex_lock(&ddata->hpd_lock);
ddata->hpd_cb = NULL;
ddata->hpd_cb_data = NULL;
mutex_unlock(&ddata->hpd_lock);
}
static const struct omap_dss_device_ops tpd_ops = {
.connect = tpd_connect,
.disconnect = tpd_disconnect,
.detect = tpd_detect,
.register_hpd_cb = tpd_register_hpd_cb,
.unregister_hpd_cb = tpd_unregister_hpd_cb,
};
static irqreturn_t tpd_hpd_isr(int irq, void *data)
{
struct panel_drv_data *ddata = data;
mutex_lock(&ddata->hpd_lock);
if (ddata->hpd_cb) {
enum drm_connector_status status;
if (tpd_detect(&ddata->dssdev))
status = connector_status_connected;
else
status = connector_status_disconnected;
ddata->hpd_cb(ddata->hpd_cb_data, status);
}
mutex_unlock(&ddata->hpd_lock);
return IRQ_HANDLED;
}
static int tpd_probe(struct platform_device *pdev)
{
struct omap_dss_device *dssdev;
struct panel_drv_data *ddata;
int r;
struct gpio_desc *gpio;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
GPIOD_OUT_LOW);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
ddata->ct_cp_hpd_gpio = gpio;
gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
GPIOD_OUT_LOW);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
ddata->ls_oe_gpio = gpio;
gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2,
GPIOD_IN);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
ddata->hpd_gpio = gpio;
mutex_init(&ddata->hpd_lock);
r = devm_request_threaded_irq(&pdev->dev, gpiod_to_irq(ddata->hpd_gpio),
NULL, tpd_hpd_isr,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"tpd12s015 hpd", ddata);
if (r)
return r;
dssdev = &ddata->dssdev;
dssdev->ops = &tpd_ops;
dssdev->dev = &pdev->dev;
dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
dssdev->owner = THIS_MODULE;
dssdev->of_ports = BIT(1) | BIT(0);
dssdev->ops_flags = OMAP_DSS_DEVICE_OP_DETECT
| OMAP_DSS_DEVICE_OP_HPD;
dssdev->next = omapdss_of_find_connected_device(pdev->dev.of_node, 1);
if (IS_ERR(dssdev->next)) {
if (PTR_ERR(dssdev->next) != -EPROBE_DEFER)
dev_err(&pdev->dev, "failed to find video sink\n");
return PTR_ERR(dssdev->next);
}
omapdss_device_register(dssdev);
return 0;
}
static int __exit tpd_remove(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev = &ddata->dssdev;
if (dssdev->next)
omapdss_device_put(dssdev->next);
omapdss_device_unregister(&ddata->dssdev);
return 0;
}
static const struct of_device_id tpd_of_match[] = {
{ .compatible = "omapdss,ti,tpd12s015", },
{},
};
MODULE_DEVICE_TABLE(of, tpd_of_match);
static struct platform_driver tpd_driver = {
.probe = tpd_probe,
.remove = __exit_p(tpd_remove),
.driver = {
.name = "tpd12s015",
.of_match_table = tpd_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(tpd_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("TPD12S015 driver");
MODULE_LICENSE("GPL");

View File

@ -1265,7 +1265,7 @@ static int dsicm_probe(struct platform_device *pdev)
dssdev->type = OMAP_DISPLAY_TYPE_DSI;
dssdev->display = true;
dssdev->owner = THIS_MODULE;
dssdev->of_ports = BIT(0);
dssdev->of_port = 0;
dssdev->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |

View File

@ -2,7 +2,7 @@
obj-$(CONFIG_OMAP2_DSS_INIT) += omapdss-boot-init.o
obj-$(CONFIG_OMAP_DSS_BASE) += omapdss-base.o
omapdss-base-y := base.o display.o dss-of.o output.o
omapdss-base-y := base.o display.o output.o
obj-$(CONFIG_OMAP2_DSS) += omapdss.o
# Core DSS files

View File

@ -149,8 +149,7 @@ struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from)
goto done;
}
if (dssdev->id &&
(dssdev->next || dssdev->bridge || dssdev->panel))
if (dssdev->id && (dssdev->next || dssdev->bridge))
goto done;
}
@ -185,11 +184,10 @@ int omapdss_device_connect(struct dss_device *dss,
if (!dst) {
/*
* The destination is NULL when the source is connected to a
* bridge or panel instead of a DSS device. Stop here, we will
* attach the bridge or panel later when we will have a DRM
* encoder.
* bridge instead of a DSS device. Stop here, we will attach
* the bridge later when we will have a DRM encoder.
*/
return src && (src->bridge || src->panel) ? 0 : -EINVAL;
return src && src->bridge ? 0 : -EINVAL;
}
if (omapdss_device_is_connected(dst))
@ -197,10 +195,12 @@ int omapdss_device_connect(struct dss_device *dss,
dst->dss = dss;
ret = dst->ops->connect(src, dst);
if (ret < 0) {
dst->dss = NULL;
return ret;
if (dst->ops && dst->ops->connect) {
ret = dst->ops->connect(src, dst);
if (ret < 0) {
dst->dss = NULL;
return ret;
}
}
return 0;
@ -217,7 +217,7 @@ void omapdss_device_disconnect(struct omap_dss_device *src,
dst ? dev_name(dst->dev) : "NULL");
if (!dst) {
WARN_ON(!src->bridge && !src->panel);
WARN_ON(!src->bridge);
return;
}
@ -228,29 +228,18 @@ void omapdss_device_disconnect(struct omap_dss_device *src,
WARN_ON(dst->state != OMAP_DSS_DISPLAY_DISABLED);
dst->ops->disconnect(src, dst);
if (dst->ops && dst->ops->disconnect)
dst->ops->disconnect(src, dst);
dst->dss = NULL;
}
EXPORT_SYMBOL_GPL(omapdss_device_disconnect);
void omapdss_device_pre_enable(struct omap_dss_device *dssdev)
{
if (!dssdev)
return;
omapdss_device_pre_enable(dssdev->next);
if (dssdev->ops->pre_enable)
dssdev->ops->pre_enable(dssdev);
}
EXPORT_SYMBOL_GPL(omapdss_device_pre_enable);
void omapdss_device_enable(struct omap_dss_device *dssdev)
{
if (!dssdev)
return;
if (dssdev->ops->enable)
if (dssdev->ops && dssdev->ops->enable)
dssdev->ops->enable(dssdev);
omapdss_device_enable(dssdev->next);
@ -266,25 +255,11 @@ void omapdss_device_disable(struct omap_dss_device *dssdev)
omapdss_device_disable(dssdev->next);
if (dssdev->ops->disable)
if (dssdev->ops && dssdev->ops->disable)
dssdev->ops->disable(dssdev);
}
EXPORT_SYMBOL_GPL(omapdss_device_disable);
void omapdss_device_post_disable(struct omap_dss_device *dssdev)
{
if (!dssdev)
return;
if (dssdev->ops->post_disable)
dssdev->ops->post_disable(dssdev);
omapdss_device_post_disable(dssdev->next);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
EXPORT_SYMBOL_GPL(omapdss_device_post_disable);
/* -----------------------------------------------------------------------------
* Components Handling
*/

View File

@ -40,15 +40,6 @@ void omapdss_display_init(struct omap_dss_device *dssdev)
}
EXPORT_SYMBOL_GPL(omapdss_display_init);
struct omap_dss_device *omapdss_display_get(struct omap_dss_device *output)
{
while (output->next)
output = output->next;
return omapdss_device_get(output);
}
EXPORT_SYMBOL_GPL(omapdss_display_get);
int omapdss_display_get_modes(struct drm_connector *connector,
const struct videomode *vm)
{

View File

@ -9,20 +9,22 @@
#define DSS_SUBSYS_NAME "DPI"
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/string.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/sys_soc.h>
#include "omapdss.h"
#include <drm/drm_bridge.h>
#include "dss.h"
#include "omapdss.h"
struct dpi_data {
struct platform_device *pdev;
@ -34,19 +36,19 @@ struct dpi_data {
enum dss_clk_source clk_src;
struct dss_pll *pll;
struct mutex lock;
struct dss_lcd_mgr_config mgr_config;
unsigned long pixelclock;
int data_lines;
struct omap_dss_device output;
struct drm_bridge bridge;
};
static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device *dssdev)
{
return container_of(dssdev, struct dpi_data, output);
}
#define drm_bridge_to_dpi(bridge) container_of(bridge, struct dpi_data, bridge)
/* -----------------------------------------------------------------------------
* Clock Handling and PLL
*/
static enum dss_clk_source dpi_get_clk_src_dra7xx(struct dpi_data *dpi,
enum omap_channel channel)
@ -283,9 +285,7 @@ static bool dpi_dss_clk_calc(struct dpi_data *dpi, unsigned long pck,
static int dpi_set_pll_clk(struct dpi_data *dpi, enum omap_channel channel,
unsigned long pck_req, unsigned long *fck, int *lck_div,
int *pck_div)
static int dpi_set_pll_clk(struct dpi_data *dpi, unsigned long pck_req)
{
struct dpi_clk_calc_ctx ctx;
int r;
@ -299,19 +299,15 @@ static int dpi_set_pll_clk(struct dpi_data *dpi, enum omap_channel channel,
if (r)
return r;
dss_select_lcd_clk_source(dpi->dss, channel, dpi->clk_src);
dss_select_lcd_clk_source(dpi->dss, dpi->output.dispc_channel,
dpi->clk_src);
dpi->mgr_config.clock_info = ctx.dispc_cinfo;
*fck = ctx.pll_cinfo.clkout[ctx.clkout_idx];
*lck_div = ctx.dispc_cinfo.lck_div;
*pck_div = ctx.dispc_cinfo.pck_div;
return 0;
}
static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req,
unsigned long *fck, int *lck_div, int *pck_div)
static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req)
{
struct dpi_clk_calc_ctx ctx;
int r;
@ -327,29 +323,19 @@ static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req,
dpi->mgr_config.clock_info = ctx.dispc_cinfo;
*fck = ctx.fck;
*lck_div = ctx.dispc_cinfo.lck_div;
*pck_div = ctx.dispc_cinfo.pck_div;
return 0;
}
static int dpi_set_mode(struct dpi_data *dpi)
{
int lck_div = 0, pck_div = 0;
unsigned long fck = 0;
int r = 0;
int r;
if (dpi->pll)
r = dpi_set_pll_clk(dpi, dpi->output.dispc_channel,
dpi->pixelclock, &fck, &lck_div, &pck_div);
r = dpi_set_pll_clk(dpi, dpi->pixelclock);
else
r = dpi_set_dispc_clk(dpi, dpi->pixelclock, &fck,
&lck_div, &pck_div);
if (r)
return r;
r = dpi_set_dispc_clk(dpi, dpi->pixelclock);
return 0;
return r;
}
static void dpi_config_lcd_manager(struct dpi_data *dpi)
@ -366,125 +352,19 @@ static void dpi_config_lcd_manager(struct dpi_data *dpi)
dss_mgr_set_lcd_config(&dpi->output, &dpi->mgr_config);
}
static void dpi_display_enable(struct omap_dss_device *dssdev)
static int dpi_clock_update(struct dpi_data *dpi, unsigned long *clock)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
struct omap_dss_device *out = &dpi->output;
int r;
mutex_lock(&dpi->lock);
if (dpi->vdds_dsi_reg) {
r = regulator_enable(dpi->vdds_dsi_reg);
if (r)
goto err_reg_enable;
}
r = dispc_runtime_get(dpi->dss->dispc);
if (r)
goto err_get_dispc;
r = dss_dpi_select_source(dpi->dss, dpi->id, out->dispc_channel);
if (r)
goto err_src_sel;
if (dpi->pll) {
r = dss_pll_enable(dpi->pll);
if (r)
goto err_pll_init;
}
r = dpi_set_mode(dpi);
if (r)
goto err_set_mode;
dpi_config_lcd_manager(dpi);
mdelay(2);
r = dss_mgr_enable(&dpi->output);
if (r)
goto err_mgr_enable;
mutex_unlock(&dpi->lock);
return;
err_mgr_enable:
err_set_mode:
if (dpi->pll)
dss_pll_disable(dpi->pll);
err_pll_init:
err_src_sel:
dispc_runtime_put(dpi->dss->dispc);
err_get_dispc:
if (dpi->vdds_dsi_reg)
regulator_disable(dpi->vdds_dsi_reg);
err_reg_enable:
mutex_unlock(&dpi->lock);
}
static void dpi_display_disable(struct omap_dss_device *dssdev)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
mutex_lock(&dpi->lock);
dss_mgr_disable(&dpi->output);
if (dpi->pll) {
dss_select_lcd_clk_source(dpi->dss, dpi->output.dispc_channel,
DSS_CLK_SRC_FCK);
dss_pll_disable(dpi->pll);
}
dispc_runtime_put(dpi->dss->dispc);
if (dpi->vdds_dsi_reg)
regulator_disable(dpi->vdds_dsi_reg);
mutex_unlock(&dpi->lock);
}
static void dpi_set_timings(struct omap_dss_device *dssdev,
const struct drm_display_mode *mode)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
DSSDBG("dpi_set_timings\n");
mutex_lock(&dpi->lock);
dpi->pixelclock = mode->clock * 1000;
mutex_unlock(&dpi->lock);
}
static int dpi_check_timings(struct omap_dss_device *dssdev,
struct drm_display_mode *mode)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
int lck_div, pck_div;
unsigned long fck;
unsigned long pck;
struct dpi_clk_calc_ctx ctx;
bool ok;
if (mode->hdisplay % 8 != 0)
return -EINVAL;
if (mode->clock == 0)
return -EINVAL;
if (dpi->pll) {
ok = dpi_pll_clk_calc(dpi, mode->clock * 1000, &ctx);
if (!ok)
if (!dpi_pll_clk_calc(dpi, *clock, &ctx))
return -EINVAL;
fck = ctx.pll_cinfo.clkout[ctx.clkout_idx];
} else {
ok = dpi_dss_clk_calc(dpi, mode->clock * 1000, &ctx);
if (!ok)
if (!dpi_dss_clk_calc(dpi, *clock, &ctx))
return -EINVAL;
fck = ctx.fck;
@ -493,9 +373,7 @@ static int dpi_check_timings(struct omap_dss_device *dssdev,
lck_div = ctx.dispc_cinfo.lck_div;
pck_div = ctx.dispc_cinfo.pck_div;
pck = fck / lck_div / pck_div;
mode->clock = pck / 1000;
*clock = fck / lck_div / pck_div;
return 0;
}
@ -536,6 +414,167 @@ static void dpi_init_pll(struct dpi_data *dpi)
dpi->pll = pll;
}
/* -----------------------------------------------------------------------------
* DRM Bridge Operations
*/
static int dpi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct dpi_data *dpi = drm_bridge_to_dpi(bridge);
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
return -EINVAL;
dpi_init_pll(dpi);
return drm_bridge_attach(bridge->encoder, dpi->output.next_bridge,
bridge, flags);
}
static enum drm_mode_status
dpi_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_mode *mode)
{
struct dpi_data *dpi = drm_bridge_to_dpi(bridge);
unsigned long clock = mode->clock * 1000;
int ret;
if (mode->hdisplay % 8 != 0)
return MODE_BAD_WIDTH;
if (mode->clock == 0)
return MODE_NOCLOCK;
ret = dpi_clock_update(dpi, &clock);
if (ret < 0)
return MODE_CLOCK_RANGE;
return MODE_OK;
}
static bool dpi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct dpi_data *dpi = drm_bridge_to_dpi(bridge);
unsigned long clock = mode->clock * 1000;
int ret;
ret = dpi_clock_update(dpi, &clock);
if (ret < 0)
return false;
adjusted_mode->clock = clock / 1000;
return true;
}
static void dpi_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{
struct dpi_data *dpi = drm_bridge_to_dpi(bridge);
dpi->pixelclock = adjusted_mode->clock * 1000;
}
static void dpi_bridge_enable(struct drm_bridge *bridge)
{
struct dpi_data *dpi = drm_bridge_to_dpi(bridge);
int r;
if (dpi->vdds_dsi_reg) {
r = regulator_enable(dpi->vdds_dsi_reg);
if (r)
return;
}
r = dispc_runtime_get(dpi->dss->dispc);
if (r)
goto err_get_dispc;
r = dss_dpi_select_source(dpi->dss, dpi->id, dpi->output.dispc_channel);
if (r)
goto err_src_sel;
if (dpi->pll) {
r = dss_pll_enable(dpi->pll);
if (r)
goto err_pll_init;
}
r = dpi_set_mode(dpi);
if (r)
goto err_set_mode;
dpi_config_lcd_manager(dpi);
mdelay(2);
r = dss_mgr_enable(&dpi->output);
if (r)
goto err_mgr_enable;
return;
err_mgr_enable:
err_set_mode:
if (dpi->pll)
dss_pll_disable(dpi->pll);
err_pll_init:
err_src_sel:
dispc_runtime_put(dpi->dss->dispc);
err_get_dispc:
if (dpi->vdds_dsi_reg)
regulator_disable(dpi->vdds_dsi_reg);
}
static void dpi_bridge_disable(struct drm_bridge *bridge)
{
struct dpi_data *dpi = drm_bridge_to_dpi(bridge);
dss_mgr_disable(&dpi->output);
if (dpi->pll) {
dss_select_lcd_clk_source(dpi->dss, dpi->output.dispc_channel,
DSS_CLK_SRC_FCK);
dss_pll_disable(dpi->pll);
}
dispc_runtime_put(dpi->dss->dispc);
if (dpi->vdds_dsi_reg)
regulator_disable(dpi->vdds_dsi_reg);
}
static const struct drm_bridge_funcs dpi_bridge_funcs = {
.attach = dpi_bridge_attach,
.mode_valid = dpi_bridge_mode_valid,
.mode_fixup = dpi_bridge_mode_fixup,
.mode_set = dpi_bridge_mode_set,
.enable = dpi_bridge_enable,
.disable = dpi_bridge_disable,
};
static void dpi_bridge_init(struct dpi_data *dpi)
{
dpi->bridge.funcs = &dpi_bridge_funcs;
dpi->bridge.of_node = dpi->pdev->dev.of_node;
dpi->bridge.type = DRM_MODE_CONNECTOR_DPI;
drm_bridge_add(&dpi->bridge);
}
static void dpi_bridge_cleanup(struct dpi_data *dpi)
{
drm_bridge_remove(&dpi->bridge);
}
/* -----------------------------------------------------------------------------
* Initialisation and Cleanup
*/
/*
* Return a hardcoded channel for the DPI output. This should work for
* current use cases, but this can be later expanded to either resolve
@ -572,39 +611,14 @@ static enum omap_channel dpi_get_channel(struct dpi_data *dpi)
}
}
static int dpi_connect(struct omap_dss_device *src,
struct omap_dss_device *dst)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dst);
dpi_init_pll(dpi);
return omapdss_device_connect(dst->dss, dst, dst->next);
}
static void dpi_disconnect(struct omap_dss_device *src,
struct omap_dss_device *dst)
{
omapdss_device_disconnect(dst, dst->next);
}
static const struct omap_dss_device_ops dpi_ops = {
.connect = dpi_connect,
.disconnect = dpi_disconnect,
.enable = dpi_display_enable,
.disable = dpi_display_disable,
.check_timings = dpi_check_timings,
.set_timings = dpi_set_timings,
};
static int dpi_init_output_port(struct dpi_data *dpi, struct device_node *port)
{
struct omap_dss_device *out = &dpi->output;
u32 port_num = 0;
int r;
dpi_bridge_init(dpi);
of_property_read_u32(port, "reg", &port_num);
dpi->id = port_num <= 2 ? port_num : 0;
@ -625,13 +639,14 @@ static int dpi_init_output_port(struct dpi_data *dpi, struct device_node *port)
out->id = OMAP_DSS_OUTPUT_DPI;
out->type = OMAP_DISPLAY_TYPE_DPI;
out->dispc_channel = dpi_get_channel(dpi);
out->of_ports = BIT(port_num);
out->ops = &dpi_ops;
out->of_port = port_num;
out->owner = THIS_MODULE;
r = omapdss_device_init_output(out);
if (r < 0)
r = omapdss_device_init_output(out, &dpi->bridge);
if (r < 0) {
dpi_bridge_cleanup(dpi);
return r;
}
omapdss_device_register(out);
@ -645,8 +660,14 @@ static void dpi_uninit_output_port(struct device_node *port)
omapdss_device_unregister(out);
omapdss_device_cleanup_output(out);
dpi_bridge_cleanup(dpi);
}
/* -----------------------------------------------------------------------------
* Initialisation and Cleanup
*/
static const struct soc_device_attribute dpi_soc_devices[] = {
{ .machine = "OMAP3[456]*" },
{ .machine = "[AD]M37*" },
@ -706,8 +727,6 @@ int dpi_init_port(struct dss_device *dss, struct platform_device *pdev,
dpi->dss = dss;
port->data = dpi;
mutex_init(&dpi->lock);
r = dpi_init_regulator(dpi);
if (r)
return r;

View File

@ -5116,12 +5116,12 @@ static int dsi_init_output(struct dsi_data *dsi)
out->dispc_channel = dsi_get_channel(dsi);
out->ops = &dsi_ops;
out->owner = THIS_MODULE;
out->of_ports = BIT(0);
out->of_port = 0;
out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE
| DRM_BUS_FLAG_DE_HIGH
| DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE;
r = omapdss_device_init_output(out);
r = omapdss_device_init_output(out, NULL);
if (r < 0)
return r;

View File

@ -1,28 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*/
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include "omapdss.h"
struct omap_dss_device *
omapdss_of_find_connected_device(struct device_node *node, unsigned int port)
{
struct device_node *remote_node;
struct omap_dss_device *dssdev;
remote_node = of_graph_get_remote_node(node, port, 0);
if (!remote_node)
return NULL;
dssdev = omapdss_find_device_by_node(remote_node);
of_node_put(remote_node);
return dssdev ? dssdev : ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL_GPL(omapdss_of_find_connected_device);

View File

@ -1151,6 +1151,31 @@ static const struct dss_features dra7xx_dss_feats = {
.has_lcd_clk_src = true,
};
static void __dss_uninit_ports(struct dss_device *dss, unsigned int num_ports)
{
struct platform_device *pdev = dss->pdev;
struct device_node *parent = pdev->dev.of_node;
struct device_node *port;
unsigned int i;
for (i = 0; i < num_ports; i++) {
port = of_graph_get_port_by_id(parent, i);
if (!port)
continue;
switch (dss->feat->ports[i]) {
case OMAP_DISPLAY_TYPE_DPI:
dpi_uninit_port(port);
break;
case OMAP_DISPLAY_TYPE_SDI:
sdi_uninit_port(port);
break;
default:
break;
}
}
}
static int dss_init_ports(struct dss_device *dss)
{
struct platform_device *pdev = dss->pdev;
@ -1168,13 +1193,13 @@ static int dss_init_ports(struct dss_device *dss)
case OMAP_DISPLAY_TYPE_DPI:
r = dpi_init_port(dss, pdev, port, dss->feat->model);
if (r)
return r;
goto error;
break;
case OMAP_DISPLAY_TYPE_SDI:
r = sdi_init_port(dss, pdev, port);
if (r)
return r;
goto error;
break;
default:
@ -1183,31 +1208,15 @@ static int dss_init_ports(struct dss_device *dss)
}
return 0;
error:
__dss_uninit_ports(dss, i);
return r;
}
static void dss_uninit_ports(struct dss_device *dss)
{
struct platform_device *pdev = dss->pdev;
struct device_node *parent = pdev->dev.of_node;
struct device_node *port;
int i;
for (i = 0; i < dss->feat->num_ports; i++) {
port = of_graph_get_port_by_id(parent, i);
if (!port)
continue;
switch (dss->feat->ports[i]) {
case OMAP_DISPLAY_TYPE_DPI:
dpi_uninit_port(port);
break;
case OMAP_DISPLAY_TYPE_SDI:
sdi_uninit_port(port);
break;
default:
break;
}
}
__dss_uninit_ports(dss, dss->feat->num_ports);
}
static int dss_video_pll_probe(struct dss_device *dss)
@ -1543,7 +1552,8 @@ static void dss_shutdown(struct platform_device *pdev)
DSSDBG("shutdown\n");
for_each_dss_output(dssdev) {
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE &&
dssdev->ops && dssdev->ops->disable)
dssdev->ops->disable(dssdev);
}
}

View File

@ -14,6 +14,7 @@
#include <linux/hdmi.h>
#include <sound/omap-hdmi-audio.h>
#include <media/cec.h>
#include <drm/drm_bridge.h>
#include "omapdss.h"
#include "dss.h"
@ -364,6 +365,7 @@ struct omap_hdmi {
bool core_enabled;
struct omap_dss_device output;
struct drm_bridge bridge;
struct platform_device *audio_pdev;
void (*audio_abort_cb)(struct device *dev);
@ -378,6 +380,6 @@ struct omap_hdmi {
bool display_enabled;
};
#define dssdev_to_hdmi(dssdev) container_of(dssdev, struct omap_hdmi, output)
#define drm_bridge_to_hdmi(b) container_of(b, struct omap_hdmi, bridge)
#endif

View File

@ -28,6 +28,9 @@
#include <sound/omap-hdmi-audio.h>
#include <media/cec.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_state_helper.h>
#include "omapdss.h"
#include "hdmi4_core.h"
#include "hdmi4_cec.h"
@ -237,20 +240,6 @@ static void hdmi_power_off_full(struct omap_hdmi *hdmi)
hdmi_power_off_core(hdmi);
}
static void hdmi_display_set_timings(struct omap_dss_device *dssdev,
const struct drm_display_mode *mode)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
mutex_lock(&hdmi->lock);
drm_display_mode_to_videomode(mode, &hdmi->cfg.vm);
dispc_set_tv_pclk(hdmi->dss->dispc, mode->clock * 1000);
mutex_unlock(&hdmi->lock);
}
static int hdmi_dump_regs(struct seq_file *s, void *p)
{
struct omap_hdmi *hdmi = s->private;
@ -272,23 +261,6 @@ static int hdmi_dump_regs(struct seq_file *s, void *p)
return 0;
}
static int read_edid(struct omap_hdmi *hdmi, u8 *buf, int len)
{
int r;
mutex_lock(&hdmi->lock);
r = hdmi_runtime_get(hdmi);
BUG_ON(r);
r = hdmi4_read_edid(&hdmi->core, buf, len);
hdmi_runtime_put(hdmi);
mutex_unlock(&hdmi->lock);
return r;
}
static void hdmi_start_audio_stream(struct omap_hdmi *hd)
{
hdmi_wp_audio_enable(&hd->wp, true);
@ -301,62 +273,6 @@ static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
hdmi_wp_audio_enable(&hd->wp, false);
}
static void hdmi_display_enable(struct omap_dss_device *dssdev)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
unsigned long flags;
int r;
DSSDBG("ENTER hdmi_display_enable\n");
mutex_lock(&hdmi->lock);
r = hdmi_power_on_full(hdmi);
if (r) {
DSSERR("failed to power on device\n");
goto done;
}
if (hdmi->audio_configured) {
r = hdmi4_audio_config(&hdmi->core, &hdmi->wp,
&hdmi->audio_config,
hdmi->cfg.vm.pixelclock);
if (r) {
DSSERR("Error restoring audio configuration: %d", r);
hdmi->audio_abort_cb(&hdmi->pdev->dev);
hdmi->audio_configured = false;
}
}
spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
if (hdmi->audio_configured && hdmi->audio_playing)
hdmi_start_audio_stream(hdmi);
hdmi->display_enabled = true;
spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
done:
mutex_unlock(&hdmi->lock);
}
static void hdmi_display_disable(struct omap_dss_device *dssdev)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
unsigned long flags;
DSSDBG("Enter hdmi_display_disable\n");
mutex_lock(&hdmi->lock);
spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
hdmi_stop_audio_stream(hdmi);
hdmi->display_enabled = false;
spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
hdmi_power_off_full(hdmi);
mutex_unlock(&hdmi->lock);
}
int hdmi4_core_enable(struct hdmi_core_data *core)
{
struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core);
@ -393,22 +309,139 @@ void hdmi4_core_disable(struct hdmi_core_data *core)
mutex_unlock(&hdmi->lock);
}
static int hdmi_connect(struct omap_dss_device *src,
struct omap_dss_device *dst)
/* -----------------------------------------------------------------------------
* DRM Bridge Operations
*/
static int hdmi4_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
return omapdss_device_connect(dst->dss, dst, dst->next);
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
return -EINVAL;
return drm_bridge_attach(bridge->encoder, hdmi->output.next_bridge,
bridge, flags);
}
static void hdmi_disconnect(struct omap_dss_device *src,
struct omap_dss_device *dst)
static void hdmi4_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{
omapdss_device_disconnect(dst, dst->next);
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
mutex_lock(&hdmi->lock);
drm_display_mode_to_videomode(adjusted_mode, &hdmi->cfg.vm);
dispc_set_tv_pclk(hdmi->dss->dispc, adjusted_mode->clock * 1000);
mutex_unlock(&hdmi->lock);
}
static int hdmi_read_edid(struct omap_dss_device *dssdev,
u8 *edid, int len)
static void hdmi4_bridge_enable(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
struct drm_atomic_state *state = bridge_state->base.state;
struct drm_connector_state *conn_state;
struct drm_connector *connector;
struct drm_crtc_state *crtc_state;
unsigned long flags;
int ret;
/*
* None of these should fail, as the bridge can't be enabled without a
* valid CRTC to connector path with fully populated new states.
*/
connector = drm_atomic_get_new_connector_for_encoder(state,
bridge->encoder);
if (WARN_ON(!connector))
return;
conn_state = drm_atomic_get_new_connector_state(state, connector);
if (WARN_ON(!conn_state))
return;
crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
if (WARN_ON(!crtc_state))
return;
hdmi->cfg.hdmi_dvi_mode = connector->display_info.is_hdmi
? HDMI_HDMI : HDMI_DVI;
if (connector->display_info.is_hdmi) {
const struct drm_display_mode *mode;
struct hdmi_avi_infoframe avi;
mode = &crtc_state->adjusted_mode;
ret = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector,
mode);
if (ret == 0)
hdmi->cfg.infoframe = avi;
}
mutex_lock(&hdmi->lock);
ret = hdmi_power_on_full(hdmi);
if (ret) {
DSSERR("failed to power on device\n");
goto done;
}
if (hdmi->audio_configured) {
ret = hdmi4_audio_config(&hdmi->core, &hdmi->wp,
&hdmi->audio_config,
hdmi->cfg.vm.pixelclock);
if (ret) {
DSSERR("Error restoring audio configuration: %d", ret);
hdmi->audio_abort_cb(&hdmi->pdev->dev);
hdmi->audio_configured = false;
}
}
spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
if (hdmi->audio_configured && hdmi->audio_playing)
hdmi_start_audio_stream(hdmi);
hdmi->display_enabled = true;
spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
done:
mutex_unlock(&hdmi->lock);
}
static void hdmi4_bridge_disable(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state)
{
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
unsigned long flags;
mutex_lock(&hdmi->lock);
spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
hdmi_stop_audio_stream(hdmi);
hdmi->display_enabled = false;
spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
hdmi_power_off_full(hdmi);
mutex_unlock(&hdmi->lock);
}
static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge,
enum drm_connector_status status)
{
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
if (status == connector_status_disconnected)
hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID);
}
static struct edid *hdmi4_bridge_get_edid(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
struct edid *edid = NULL;
unsigned int cec_addr;
bool need_enable;
int r;
@ -417,64 +450,66 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev,
if (need_enable) {
r = hdmi4_core_enable(&hdmi->core);
if (r)
return r;
return NULL;
}
r = read_edid(hdmi, edid, len);
if (r >= 256)
hdmi4_cec_set_phys_addr(&hdmi->core,
cec_get_edid_phys_addr(edid, r, NULL));
else
hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID);
mutex_lock(&hdmi->lock);
r = hdmi_runtime_get(hdmi);
BUG_ON(r);
r = hdmi4_core_ddc_init(&hdmi->core);
if (r)
goto done;
edid = drm_do_get_edid(connector, hdmi4_core_ddc_read, &hdmi->core);
done:
hdmi_runtime_put(hdmi);
mutex_unlock(&hdmi->lock);
if (edid && edid->extensions) {
unsigned int len = (edid->extensions + 1) * EDID_LENGTH;
cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL);
} else {
cec_addr = CEC_PHYS_ADDR_INVALID;
}
hdmi4_cec_set_phys_addr(&hdmi->core, cec_addr);
if (need_enable)
hdmi4_core_disable(&hdmi->core);
return r;
return edid;
}
static void hdmi_lost_hotplug(struct omap_dss_device *dssdev)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID);
}
static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
const struct hdmi_avi_infoframe *avi)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
hdmi->cfg.infoframe = *avi;
return 0;
}
static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
bool hdmi_mode)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
hdmi->cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
return 0;
}
static const struct omap_dss_device_ops hdmi_ops = {
.connect = hdmi_connect,
.disconnect = hdmi_disconnect,
.enable = hdmi_display_enable,
.disable = hdmi_display_disable,
.set_timings = hdmi_display_set_timings,
.read_edid = hdmi_read_edid,
.hdmi = {
.lost_hotplug = hdmi_lost_hotplug,
.set_infoframe = hdmi_set_infoframe,
.set_hdmi_mode = hdmi_set_hdmi_mode,
},
static const struct drm_bridge_funcs hdmi4_bridge_funcs = {
.attach = hdmi4_bridge_attach,
.mode_set = hdmi4_bridge_mode_set,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_enable = hdmi4_bridge_enable,
.atomic_disable = hdmi4_bridge_disable,
.hpd_notify = hdmi4_bridge_hpd_notify,
.get_edid = hdmi4_bridge_get_edid,
};
static void hdmi4_bridge_init(struct omap_hdmi *hdmi)
{
hdmi->bridge.funcs = &hdmi4_bridge_funcs;
hdmi->bridge.of_node = hdmi->pdev->dev.of_node;
hdmi->bridge.ops = DRM_BRIDGE_OP_EDID;
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
drm_bridge_add(&hdmi->bridge);
}
static void hdmi4_bridge_cleanup(struct omap_hdmi *hdmi)
{
drm_bridge_remove(&hdmi->bridge);
}
/* -----------------------------------------------------------------------------
* Audio Callbacks
*/
@ -666,19 +701,21 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi)
struct omap_dss_device *out = &hdmi->output;
int r;
hdmi4_bridge_init(hdmi);
out->dev = &hdmi->pdev->dev;
out->id = OMAP_DSS_OUTPUT_HDMI;
out->type = OMAP_DISPLAY_TYPE_HDMI;
out->name = "hdmi.0";
out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
out->ops = &hdmi_ops;
out->owner = THIS_MODULE;
out->of_ports = BIT(0);
out->ops_flags = OMAP_DSS_DEVICE_OP_EDID;
out->of_port = 0;
r = omapdss_device_init_output(out);
if (r < 0)
r = omapdss_device_init_output(out, &hdmi->bridge);
if (r < 0) {
hdmi4_bridge_cleanup(hdmi);
return r;
}
omapdss_device_register(out);
@ -691,6 +728,8 @@ static void hdmi4_uninit_output(struct omap_hdmi *hdmi)
omapdss_device_unregister(out);
omapdss_device_cleanup_output(out);
hdmi4_bridge_cleanup(hdmi);
}
static int hdmi4_probe_of(struct omap_hdmi *hdmi)

View File

@ -32,7 +32,7 @@ static inline void __iomem *hdmi_av_base(struct hdmi_core_data *core)
return core->base + HDMI_CORE_AV;
}
static int hdmi_core_ddc_init(struct hdmi_core_data *core)
int hdmi4_core_ddc_init(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
@ -74,13 +74,11 @@ static int hdmi_core_ddc_init(struct hdmi_core_data *core)
return 0;
}
static int hdmi_core_ddc_edid(struct hdmi_core_data *core,
u8 *pedid, int ext)
int hdmi4_core_ddc_read(void *data, u8 *buf, unsigned int block, size_t len)
{
struct hdmi_core_data *core = data;
void __iomem *base = core->base;
u32 i;
char checksum;
u32 offset = 0;
/* HDMI_CORE_DDC_STATUS_IN_PROG */
if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
@ -89,24 +87,21 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core,
return -ETIMEDOUT;
}
if (ext % 2 != 0)
offset = 0x80;
/* Load Segment Address Register */
REG_FLD_MOD(base, HDMI_CORE_DDC_SEGM, ext / 2, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_DDC_SEGM, block / 2, 7, 0);
/* Load Slave Address Register */
REG_FLD_MOD(base, HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1);
/* Load Offset Address Register */
REG_FLD_MOD(base, HDMI_CORE_DDC_OFFSET, offset, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_DDC_OFFSET, block % 2 ? 0x80 : 0, 7, 0);
/* Load Byte Count */
REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT1, 0x80, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT1, len, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT2, 0x0, 1, 0);
/* Set DDC_CMD */
if (ext)
if (block)
REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x4, 3, 0);
else
REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x2, 3, 0);
@ -122,7 +117,7 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core,
return -EIO;
}
for (i = 0; i < 0x80; ++i) {
for (i = 0; i < len; ++i) {
int t;
/* IN_PROG */
@ -141,48 +136,12 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core,
udelay(1);
}
pedid[i] = REG_GET(base, HDMI_CORE_DDC_DATA, 7, 0);
}
checksum = 0;
for (i = 0; i < 0x80; ++i)
checksum += pedid[i];
if (checksum != 0) {
DSSERR("E-EDID checksum failed!!\n");
return -EIO;
buf[i] = REG_GET(base, HDMI_CORE_DDC_DATA, 7, 0);
}
return 0;
}
int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len)
{
int r, l;
if (len < 128)
return -EINVAL;
r = hdmi_core_ddc_init(core);
if (r)
return r;
r = hdmi_core_ddc_edid(core, edid, 0);
if (r)
return r;
l = 128;
if (len >= 128 * 2 && edid[0x7e] > 0) {
r = hdmi_core_ddc_edid(core, edid + 0x80, 1);
if (r)
return r;
l += 128;
}
return l;
}
static void hdmi_core_init(struct hdmi_core_video_config *video_cfg)
{
DSSDBG("Enter hdmi_core_init\n");

View File

@ -249,7 +249,9 @@ struct hdmi_core_packet_enable_repeat {
u32 generic_pkt_repeat;
};
int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len);
int hdmi4_core_ddc_init(struct hdmi_core_data *core);
int hdmi4_core_ddc_read(void *data, u8 *buf, unsigned int block, size_t len);
void hdmi4_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct hdmi_config *cfg);
void hdmi4_core_dump(struct hdmi_core_data *core, struct seq_file *s);

View File

@ -31,6 +31,9 @@
#include <linux/of_graph.h>
#include <sound/omap-hdmi-audio.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_state_helper.h>
#include "omapdss.h"
#include "hdmi5_core.h"
#include "dss.h"
@ -236,20 +239,6 @@ static void hdmi_power_off_full(struct omap_hdmi *hdmi)
hdmi_power_off_core(hdmi);
}
static void hdmi_display_set_timings(struct omap_dss_device *dssdev,
const struct drm_display_mode *mode)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
mutex_lock(&hdmi->lock);
drm_display_mode_to_videomode(mode, &hdmi->cfg.vm);
dispc_set_tv_pclk(hdmi->dss->dispc, mode->clock * 1000);
mutex_unlock(&hdmi->lock);
}
static int hdmi_dump_regs(struct seq_file *s, void *p)
{
struct omap_hdmi *hdmi = s->private;
@ -271,30 +260,6 @@ static int hdmi_dump_regs(struct seq_file *s, void *p)
return 0;
}
static int read_edid(struct omap_hdmi *hdmi, u8 *buf, int len)
{
int r;
int idlemode;
mutex_lock(&hdmi->lock);
r = hdmi_runtime_get(hdmi);
BUG_ON(r);
idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2);
/* No-idle mode */
REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
r = hdmi5_read_edid(&hdmi->core, buf, len);
REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2);
hdmi_runtime_put(hdmi);
mutex_unlock(&hdmi->lock);
return r;
}
static void hdmi_start_audio_stream(struct omap_hdmi *hd)
{
REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
@ -309,62 +274,6 @@ static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2);
}
static void hdmi_display_enable(struct omap_dss_device *dssdev)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
unsigned long flags;
int r;
DSSDBG("ENTER hdmi_display_enable\n");
mutex_lock(&hdmi->lock);
r = hdmi_power_on_full(hdmi);
if (r) {
DSSERR("failed to power on device\n");
goto done;
}
if (hdmi->audio_configured) {
r = hdmi5_audio_config(&hdmi->core, &hdmi->wp,
&hdmi->audio_config,
hdmi->cfg.vm.pixelclock);
if (r) {
DSSERR("Error restoring audio configuration: %d", r);
hdmi->audio_abort_cb(&hdmi->pdev->dev);
hdmi->audio_configured = false;
}
}
spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
if (hdmi->audio_configured && hdmi->audio_playing)
hdmi_start_audio_stream(hdmi);
hdmi->display_enabled = true;
spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
done:
mutex_unlock(&hdmi->lock);
}
static void hdmi_display_disable(struct omap_dss_device *dssdev)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
unsigned long flags;
DSSDBG("Enter hdmi_display_disable\n");
mutex_lock(&hdmi->lock);
spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
hdmi_stop_audio_stream(hdmi);
hdmi->display_enabled = false;
spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
hdmi_power_off_full(hdmi);
mutex_unlock(&hdmi->lock);
}
static int hdmi_core_enable(struct omap_hdmi *hdmi)
{
int r = 0;
@ -398,23 +307,131 @@ static void hdmi_core_disable(struct omap_hdmi *hdmi)
mutex_unlock(&hdmi->lock);
}
static int hdmi_connect(struct omap_dss_device *src,
struct omap_dss_device *dst)
/* -----------------------------------------------------------------------------
* DRM Bridge Operations
*/
static int hdmi5_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
return omapdss_device_connect(dst->dss, dst, dst->next);
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
return -EINVAL;
return drm_bridge_attach(bridge->encoder, hdmi->output.next_bridge,
bridge, flags);
}
static void hdmi_disconnect(struct omap_dss_device *src,
struct omap_dss_device *dst)
static void hdmi5_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{
omapdss_device_disconnect(dst, dst->next);
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
mutex_lock(&hdmi->lock);
drm_display_mode_to_videomode(adjusted_mode, &hdmi->cfg.vm);
dispc_set_tv_pclk(hdmi->dss->dispc, adjusted_mode->clock * 1000);
mutex_unlock(&hdmi->lock);
}
static int hdmi_read_edid(struct omap_dss_device *dssdev,
u8 *edid, int len)
static void hdmi5_bridge_enable(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
struct drm_atomic_state *state = bridge_state->base.state;
struct drm_connector_state *conn_state;
struct drm_connector *connector;
struct drm_crtc_state *crtc_state;
unsigned long flags;
int ret;
/*
* None of these should fail, as the bridge can't be enabled without a
* valid CRTC to connector path with fully populated new states.
*/
connector = drm_atomic_get_new_connector_for_encoder(state,
bridge->encoder);
if (WARN_ON(!connector))
return;
conn_state = drm_atomic_get_new_connector_state(state, connector);
if (WARN_ON(!conn_state))
return;
crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
if (WARN_ON(!crtc_state))
return;
hdmi->cfg.hdmi_dvi_mode = connector->display_info.is_hdmi
? HDMI_HDMI : HDMI_DVI;
if (connector->display_info.is_hdmi) {
const struct drm_display_mode *mode;
struct hdmi_avi_infoframe avi;
mode = &crtc_state->adjusted_mode;
ret = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector,
mode);
if (ret == 0)
hdmi->cfg.infoframe = avi;
}
mutex_lock(&hdmi->lock);
ret = hdmi_power_on_full(hdmi);
if (ret) {
DSSERR("failed to power on device\n");
goto done;
}
if (hdmi->audio_configured) {
ret = hdmi5_audio_config(&hdmi->core, &hdmi->wp,
&hdmi->audio_config,
hdmi->cfg.vm.pixelclock);
if (ret) {
DSSERR("Error restoring audio configuration: %d", ret);
hdmi->audio_abort_cb(&hdmi->pdev->dev);
hdmi->audio_configured = false;
}
}
spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
if (hdmi->audio_configured && hdmi->audio_playing)
hdmi_start_audio_stream(hdmi);
hdmi->display_enabled = true;
spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
done:
mutex_unlock(&hdmi->lock);
}
static void hdmi5_bridge_disable(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state)
{
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
unsigned long flags;
mutex_lock(&hdmi->lock);
spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
hdmi_stop_audio_stream(hdmi);
hdmi->display_enabled = false;
spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
hdmi_power_off_full(hdmi);
mutex_unlock(&hdmi->lock);
}
static struct edid *hdmi5_bridge_get_edid(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
struct edid *edid;
bool need_enable;
int idlemode;
int r;
need_enable = hdmi->core_enabled == false;
@ -422,52 +439,60 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev,
if (need_enable) {
r = hdmi_core_enable(hdmi);
if (r)
return r;
return NULL;
}
r = read_edid(hdmi, edid, len);
mutex_lock(&hdmi->lock);
r = hdmi_runtime_get(hdmi);
BUG_ON(r);
idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2);
/* No-idle mode */
REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
hdmi5_core_ddc_init(&hdmi->core);
edid = drm_do_get_edid(connector, hdmi5_core_ddc_read, &hdmi->core);
hdmi5_core_ddc_uninit(&hdmi->core);
REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2);
hdmi_runtime_put(hdmi);
mutex_unlock(&hdmi->lock);
if (need_enable)
hdmi_core_disable(hdmi);
return r;
return (struct edid *)edid;
}
static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
const struct hdmi_avi_infoframe *avi)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
hdmi->cfg.infoframe = *avi;
return 0;
}
static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
bool hdmi_mode)
{
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
hdmi->cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
return 0;
}
static const struct omap_dss_device_ops hdmi_ops = {
.connect = hdmi_connect,
.disconnect = hdmi_disconnect,
.enable = hdmi_display_enable,
.disable = hdmi_display_disable,
.set_timings = hdmi_display_set_timings,
.read_edid = hdmi_read_edid,
.hdmi = {
.set_infoframe = hdmi_set_infoframe,
.set_hdmi_mode = hdmi_set_hdmi_mode,
},
static const struct drm_bridge_funcs hdmi5_bridge_funcs = {
.attach = hdmi5_bridge_attach,
.mode_set = hdmi5_bridge_mode_set,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_enable = hdmi5_bridge_enable,
.atomic_disable = hdmi5_bridge_disable,
.get_edid = hdmi5_bridge_get_edid,
};
static void hdmi5_bridge_init(struct omap_hdmi *hdmi)
{
hdmi->bridge.funcs = &hdmi5_bridge_funcs;
hdmi->bridge.of_node = hdmi->pdev->dev.of_node;
hdmi->bridge.ops = DRM_BRIDGE_OP_EDID;
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
drm_bridge_add(&hdmi->bridge);
}
static void hdmi5_bridge_cleanup(struct omap_hdmi *hdmi)
{
drm_bridge_remove(&hdmi->bridge);
}
/* -----------------------------------------------------------------------------
* Audio Callbacks
*/
@ -650,19 +675,21 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi)
struct omap_dss_device *out = &hdmi->output;
int r;
hdmi5_bridge_init(hdmi);
out->dev = &hdmi->pdev->dev;
out->id = OMAP_DSS_OUTPUT_HDMI;
out->type = OMAP_DISPLAY_TYPE_HDMI;
out->name = "hdmi.0";
out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
out->ops = &hdmi_ops;
out->owner = THIS_MODULE;
out->of_ports = BIT(0);
out->ops_flags = OMAP_DSS_DEVICE_OP_EDID;
out->of_port = 0;
r = omapdss_device_init_output(out);
if (r < 0)
r = omapdss_device_init_output(out, &hdmi->bridge);
if (r < 0) {
hdmi5_bridge_cleanup(hdmi);
return r;
}
omapdss_device_register(out);
@ -675,6 +702,8 @@ static void hdmi5_uninit_output(struct omap_hdmi *hdmi)
omapdss_device_unregister(out);
omapdss_device_cleanup_output(out);
hdmi5_bridge_cleanup(hdmi);
}
static int hdmi5_probe_of(struct omap_hdmi *hdmi)

View File

@ -23,7 +23,7 @@
#include "hdmi5_core.h"
static void hdmi_core_ddc_init(struct hdmi_core_data *core)
void hdmi5_core_ddc_init(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
const unsigned long long iclk = 266000000; /* DSS L3 ICLK */
@ -102,7 +102,7 @@ static void hdmi_core_ddc_init(struct hdmi_core_data *core)
REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x0, 2, 2);
}
static void hdmi_core_ddc_uninit(struct hdmi_core_data *core)
void hdmi5_core_ddc_uninit(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
@ -112,14 +112,14 @@ static void hdmi_core_ddc_uninit(struct hdmi_core_data *core)
REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 2, 2);
}
static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext)
int hdmi5_core_ddc_read(void *data, u8 *buf, unsigned int block, size_t len)
{
struct hdmi_core_data *core = data;
void __iomem *base = core->base;
u8 cur_addr;
char checksum = 0;
const int retries = 1000;
u8 seg_ptr = ext / 2;
u8 edidbase = ((ext % 2) * 0x80);
u8 seg_ptr = block / 2;
u8 edidbase = ((block % 2) * EDID_LENGTH);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SEGPTR, seg_ptr, 7, 0);
@ -127,7 +127,7 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext)
* TODO: We use polling here, although we probably should use proper
* interrupts.
*/
for (cur_addr = 0; cur_addr < 128; ++cur_addr) {
for (cur_addr = 0; cur_addr < len; ++cur_addr) {
int i;
/* clear ERROR and DONE */
@ -164,45 +164,13 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext)
return -EIO;
}
pedid[cur_addr] = REG_GET(base, HDMI_CORE_I2CM_DATAI, 7, 0);
checksum += pedid[cur_addr];
buf[cur_addr] = REG_GET(base, HDMI_CORE_I2CM_DATAI, 7, 0);
}
return 0;
}
int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len)
{
int r, n, i;
int max_ext_blocks = (len / 128) - 1;
if (len < 128)
return -EINVAL;
hdmi_core_ddc_init(core);
r = hdmi_core_ddc_edid(core, edid, 0);
if (r)
goto out;
n = edid[0x7e];
if (n > max_ext_blocks)
n = max_ext_blocks;
for (i = 1; i <= n; i++) {
r = hdmi_core_ddc_edid(core, edid + i * EDID_LENGTH, i);
if (r)
goto out;
}
out:
hdmi_core_ddc_uninit(core);
return r ? r : len;
}
void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s)
{

View File

@ -281,7 +281,10 @@ struct csc_table {
u16 c1, c2, c3, c4;
};
int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len);
void hdmi5_core_ddc_init(struct hdmi_core_data *core);
int hdmi5_core_ddc_read(void *data, u8 *buf, unsigned int block, size_t len);
void hdmi5_core_ddc_uninit(struct hdmi_core_data *core);
void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s);
int hdmi5_core_handle_irqs(struct hdmi_core_data *core);
void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp,

View File

@ -174,12 +174,7 @@ static const struct of_device_id omapdss_of_match[] __initconst = {
};
static const struct of_device_id omapdss_of_fixups_whitelist[] __initconst = {
{ .compatible = "composite-video-connector" },
{ .compatible = "hdmi-connector" },
{ .compatible = "panel-dsi-cm" },
{ .compatible = "svideo-connector" },
{ .compatible = "ti,opa362" },
{ .compatible = "ti,tpd12s015" },
{},
};

View File

@ -285,13 +285,6 @@ struct omap_dss_writeback_info {
u8 pre_mult_alpha;
};
struct omapdss_hdmi_ops {
void (*lost_hotplug)(struct omap_dss_device *dssdev);
int (*set_hdmi_mode)(struct omap_dss_device *dssdev, bool hdmi_mode);
int (*set_infoframe)(struct omap_dss_device *dssdev,
const struct hdmi_avi_infoframe *avi);
};
struct omapdss_dsi_ops {
void (*disable)(struct omap_dss_device *dssdev, bool disconnect_lanes,
bool enter_ulps);
@ -349,46 +342,23 @@ struct omap_dss_device_ops {
void (*disconnect)(struct omap_dss_device *dssdev,
struct omap_dss_device *dst);
void (*pre_enable)(struct omap_dss_device *dssdev);
void (*enable)(struct omap_dss_device *dssdev);
void (*disable)(struct omap_dss_device *dssdev);
void (*post_disable)(struct omap_dss_device *dssdev);
int (*check_timings)(struct omap_dss_device *dssdev,
struct drm_display_mode *mode);
void (*set_timings)(struct omap_dss_device *dssdev,
const struct drm_display_mode *mode);
bool (*detect)(struct omap_dss_device *dssdev);
void (*register_hpd_cb)(struct omap_dss_device *dssdev,
void (*cb)(void *cb_data,
enum drm_connector_status status),
void *cb_data);
void (*unregister_hpd_cb)(struct omap_dss_device *dssdev);
int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len);
int (*get_modes)(struct omap_dss_device *dssdev,
struct drm_connector *connector);
union {
const struct omapdss_hdmi_ops hdmi;
const struct omapdss_dsi_ops dsi;
};
const struct omapdss_dsi_ops dsi;
};
/**
* enum omap_dss_device_ops_flag - Indicates which device ops are supported
* @OMAP_DSS_DEVICE_OP_DETECT: The device supports output connection detection
* @OMAP_DSS_DEVICE_OP_HPD: The device supports all hot-plug-related operations
* @OMAP_DSS_DEVICE_OP_EDID: The device supports reading EDID
* @OMAP_DSS_DEVICE_OP_MODES: The device supports reading modes
*/
enum omap_dss_device_ops_flag {
OMAP_DSS_DEVICE_OP_DETECT = BIT(0),
OMAP_DSS_DEVICE_OP_HPD = BIT(1),
OMAP_DSS_DEVICE_OP_EDID = BIT(2),
OMAP_DSS_DEVICE_OP_MODES = BIT(3),
};
@ -400,6 +370,7 @@ struct omap_dss_device {
struct dss_device *dss;
struct omap_dss_device *next;
struct drm_bridge *bridge;
struct drm_bridge *next_bridge;
struct drm_panel *panel;
struct list_head list;
@ -436,8 +407,8 @@ struct omap_dss_device {
/* output instance */
enum omap_dss_output_id id;
/* bitmask of port numbers in DT */
unsigned int of_ports;
/* port number in DT */
unsigned int of_port;
};
struct omap_dss_driver {
@ -461,7 +432,6 @@ static inline bool omapdss_is_initialized(void)
}
void omapdss_display_init(struct omap_dss_device *dssdev);
struct omap_dss_device *omapdss_display_get(struct omap_dss_device *output);
int omapdss_display_get_modes(struct drm_connector *connector,
const struct videomode *vm);
@ -475,10 +445,8 @@ int omapdss_device_connect(struct dss_device *dss,
struct omap_dss_device *dst);
void omapdss_device_disconnect(struct omap_dss_device *src,
struct omap_dss_device *dst);
void omapdss_device_pre_enable(struct omap_dss_device *dssdev);
void omapdss_device_enable(struct omap_dss_device *dssdev);
void omapdss_device_disable(struct omap_dss_device *dssdev);
void omapdss_device_post_disable(struct omap_dss_device *dssdev);
int omap_dss_get_num_overlay_managers(void);
@ -487,7 +455,8 @@ int omap_dss_get_num_overlays(void);
#define for_each_dss_output(d) \
while ((d = omapdss_device_next_output(d)) != NULL)
struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from);
int omapdss_device_init_output(struct omap_dss_device *out);
int omapdss_device_init_output(struct omap_dss_device *out,
struct drm_bridge *local_bridge);
void omapdss_device_cleanup_output(struct omap_dss_device *out);
typedef void (*omap_dispc_isr_t) (void *arg, u32 mask);
@ -502,9 +471,6 @@ static inline bool omapdss_device_is_enabled(struct omap_dss_device *dssdev)
return dssdev->state == OMAP_DSS_DISPLAY_ACTIVE;
}
struct omap_dss_device *
omapdss_of_find_connected_device(struct device_node *node, unsigned int port);
enum dss_writeback_channel {
DSS_WB_LCD1_MGR = 0,
DSS_WB_LCD2_MGR = 1,

View File

@ -4,7 +4,6 @@
* Author: Archit Taneja <archit@ti.com>
*/
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@ -18,12 +17,14 @@
#include "dss.h"
#include "omapdss.h"
int omapdss_device_init_output(struct omap_dss_device *out)
int omapdss_device_init_output(struct omap_dss_device *out,
struct drm_bridge *local_bridge)
{
struct device_node *remote_node;
int ret;
remote_node = of_graph_get_remote_node(out->dev->of_node,
ffs(out->of_ports) - 1, 0);
out->of_port, 0);
if (!remote_node) {
dev_dbg(out->dev, "failed to find video sink\n");
return 0;
@ -39,17 +40,55 @@ int omapdss_device_init_output(struct omap_dss_device *out)
if (out->next && out->type != out->next->type) {
dev_err(out->dev, "output type and display type don't match\n");
omapdss_device_put(out->next);
out->next = NULL;
return -EINVAL;
ret = -EINVAL;
goto error;
}
return out->next || out->bridge || out->panel ? 0 : -EPROBE_DEFER;
if (out->panel) {
struct drm_bridge *bridge;
bridge = drm_panel_bridge_add(out->panel);
if (IS_ERR(bridge)) {
dev_err(out->dev,
"unable to create panel bridge (%ld)\n",
PTR_ERR(bridge));
ret = PTR_ERR(bridge);
goto error;
}
out->bridge = bridge;
}
if (local_bridge) {
if (!out->bridge) {
ret = -EPROBE_DEFER;
goto error;
}
out->next_bridge = out->bridge;
out->bridge = local_bridge;
}
if (!out->next && !out->bridge) {
ret = -EPROBE_DEFER;
goto error;
}
return 0;
error:
omapdss_device_cleanup_output(out);
out->next = NULL;
return ret;
}
EXPORT_SYMBOL(omapdss_device_init_output);
void omapdss_device_cleanup_output(struct omap_dss_device *out)
{
if (out->bridge && out->panel)
drm_panel_bridge_remove(out->next_bridge ?
out->next_bridge : out->bridge);
if (out->next)
omapdss_device_put(out->next);
}

View File

@ -6,17 +6,19 @@
#define DSS_SUBSYS_NAME "SDI"
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/regulator/consumer.h>
#include <linux/export.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/string.h>
#include <drm/drm_bridge.h>
#include "omapdss.h"
#include "dss.h"
#include "omapdss.h"
struct sdi_device {
struct platform_device *pdev;
@ -30,9 +32,11 @@ struct sdi_device {
int datapairs;
struct omap_dss_device output;
struct drm_bridge bridge;
};
#define dssdev_to_sdi(dssdev) container_of(dssdev, struct sdi_device, output)
#define drm_bridge_to_sdi(bridge) \
container_of(bridge, struct sdi_device, bridge)
struct sdi_clk_calc_ctx {
struct sdi_device *sdi;
@ -118,9 +122,82 @@ static void sdi_config_lcd_manager(struct sdi_device *sdi)
dss_mgr_set_lcd_config(&sdi->output, &sdi->mgr_config);
}
static void sdi_display_enable(struct omap_dss_device *dssdev)
/* -----------------------------------------------------------------------------
* DRM Bridge Operations
*/
static int sdi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct sdi_device *sdi = dssdev_to_sdi(dssdev);
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
return -EINVAL;
return drm_bridge_attach(bridge->encoder, sdi->output.next_bridge,
bridge, flags);
}
static enum drm_mode_status
sdi_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_mode *mode)
{
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
unsigned long pixelclock = mode->clock * 1000;
struct dispc_clock_info dispc_cinfo;
unsigned long fck;
int ret;
if (pixelclock == 0)
return MODE_NOCLOCK;
ret = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo);
if (ret < 0)
return MODE_CLOCK_RANGE;
return MODE_OK;
}
static bool sdi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
unsigned long pixelclock = mode->clock * 1000;
struct dispc_clock_info dispc_cinfo;
unsigned long fck;
unsigned long pck;
int ret;
ret = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo);
if (ret < 0)
return false;
pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;
if (pck != pixelclock)
dev_dbg(&sdi->pdev->dev,
"pixel clock adjusted from %lu Hz to %lu Hz\n",
pixelclock, pck);
adjusted_mode->clock = pck / 1000;
return true;
}
static void sdi_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
sdi->pixelclock = adjusted_mode->clock * 1000;
}
static void sdi_bridge_enable(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state)
{
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
struct dispc_clock_info dispc_cinfo;
unsigned long fck;
int r;
@ -181,9 +258,10 @@ err_get_dispc:
regulator_disable(sdi->vdds_sdi_reg);
}
static void sdi_display_disable(struct omap_dss_device *dssdev)
static void sdi_bridge_disable(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state)
{
struct sdi_device *sdi = dssdev_to_sdi(dssdev);
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
dss_mgr_disable(&sdi->output);
@ -194,86 +272,56 @@ static void sdi_display_disable(struct omap_dss_device *dssdev)
regulator_disable(sdi->vdds_sdi_reg);
}
static void sdi_set_timings(struct omap_dss_device *dssdev,
const struct drm_display_mode *mode)
{
struct sdi_device *sdi = dssdev_to_sdi(dssdev);
sdi->pixelclock = mode->clock * 1000;
}
static int sdi_check_timings(struct omap_dss_device *dssdev,
struct drm_display_mode *mode)
{
struct sdi_device *sdi = dssdev_to_sdi(dssdev);
struct dispc_clock_info dispc_cinfo;
unsigned long pixelclock = mode->clock * 1000;
unsigned long fck;
unsigned long pck;
int r;
if (pixelclock == 0)
return -EINVAL;
r = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo);
if (r)
return r;
pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;
if (pck != pixelclock) {
DSSWARN("Pixel clock adjusted from %lu Hz to %lu Hz\n",
pixelclock, pck);
mode->clock = pck / 1000;
}
return 0;
}
static int sdi_connect(struct omap_dss_device *src,
struct omap_dss_device *dst)
{
return omapdss_device_connect(dst->dss, dst, dst->next);
}
static void sdi_disconnect(struct omap_dss_device *src,
struct omap_dss_device *dst)
{
omapdss_device_disconnect(dst, dst->next);
}
static const struct omap_dss_device_ops sdi_ops = {
.connect = sdi_connect,
.disconnect = sdi_disconnect,
.enable = sdi_display_enable,
.disable = sdi_display_disable,
.check_timings = sdi_check_timings,
.set_timings = sdi_set_timings,
static const struct drm_bridge_funcs sdi_bridge_funcs = {
.attach = sdi_bridge_attach,
.mode_valid = sdi_bridge_mode_valid,
.mode_fixup = sdi_bridge_mode_fixup,
.mode_set = sdi_bridge_mode_set,
.atomic_enable = sdi_bridge_enable,
.atomic_disable = sdi_bridge_disable,
};
static void sdi_bridge_init(struct sdi_device *sdi)
{
sdi->bridge.funcs = &sdi_bridge_funcs;
sdi->bridge.of_node = sdi->pdev->dev.of_node;
sdi->bridge.type = DRM_MODE_CONNECTOR_LVDS;
drm_bridge_add(&sdi->bridge);
}
static void sdi_bridge_cleanup(struct sdi_device *sdi)
{
drm_bridge_remove(&sdi->bridge);
}
/* -----------------------------------------------------------------------------
* Initialisation and Cleanup
*/
static int sdi_init_output(struct sdi_device *sdi)
{
struct omap_dss_device *out = &sdi->output;
int r;
sdi_bridge_init(sdi);
out->dev = &sdi->pdev->dev;
out->id = OMAP_DSS_OUTPUT_SDI;
out->type = OMAP_DISPLAY_TYPE_SDI;
out->name = "sdi.0";
out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
/* We have SDI only on OMAP3, where it's on port 1 */
out->of_ports = BIT(1);
out->ops = &sdi_ops;
out->of_port = 1;
out->owner = THIS_MODULE;
out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE /* 15.5.9.1.2 */
| DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE;
r = omapdss_device_init_output(out);
if (r < 0)
r = omapdss_device_init_output(out, &sdi->bridge);
if (r < 0) {
sdi_bridge_cleanup(sdi);
return r;
}
omapdss_device_register(out);
@ -284,6 +332,8 @@ static void sdi_uninit_output(struct sdi_device *sdi)
{
omapdss_device_unregister(&sdi->output);
omapdss_device_cleanup_output(&sdi->output);
sdi_bridge_cleanup(sdi);
}
int sdi_init_port(struct dss_device *dss, struct platform_device *pdev,

View File

@ -13,7 +13,6 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/string.h>
@ -26,6 +25,8 @@
#include <linux/component.h>
#include <linux/sys_soc.h>
#include <drm/drm_bridge.h>
#include "omapdss.h"
#include "dss.h"
@ -289,7 +290,6 @@ static const struct drm_display_mode omap_dss_ntsc_mode = {
struct venc_device {
struct platform_device *pdev;
void __iomem *base;
struct mutex venc_lock;
struct regulator *vdda_dac_reg;
struct dss_device *dss;
@ -303,9 +303,10 @@ struct venc_device {
bool requires_tv_dac_clk;
struct omap_dss_device output;
struct drm_bridge bridge;
};
#define dssdev_to_venc(dssdev) container_of(dssdev, struct venc_device, output)
#define drm_bridge_to_venc(b) container_of(b, struct venc_device, bridge)
static inline void venc_write_reg(struct venc_device *venc, int idx, u32 val)
{
@ -477,56 +478,6 @@ static void venc_power_off(struct venc_device *venc)
venc_runtime_put(venc);
}
static void venc_display_enable(struct omap_dss_device *dssdev)
{
struct venc_device *venc = dssdev_to_venc(dssdev);
DSSDBG("venc_display_enable\n");
mutex_lock(&venc->venc_lock);
venc_power_on(venc);
mutex_unlock(&venc->venc_lock);
}
static void venc_display_disable(struct omap_dss_device *dssdev)
{
struct venc_device *venc = dssdev_to_venc(dssdev);
DSSDBG("venc_display_disable\n");
mutex_lock(&venc->venc_lock);
venc_power_off(venc);
mutex_unlock(&venc->venc_lock);
}
static int venc_get_modes(struct omap_dss_device *dssdev,
struct drm_connector *connector)
{
static const struct drm_display_mode *modes[] = {
&omap_dss_pal_mode,
&omap_dss_ntsc_mode,
};
unsigned int i;
for (i = 0; i < ARRAY_SIZE(modes); ++i) {
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, modes[i]);
if (!mode)
return i;
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
}
return ARRAY_SIZE(modes);
}
static enum venc_videomode venc_get_videomode(const struct drm_display_mode *mode)
{
if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
@ -545,57 +496,6 @@ static enum venc_videomode venc_get_videomode(const struct drm_display_mode *mod
return VENC_MODE_UNKNOWN;
}
static void venc_set_timings(struct omap_dss_device *dssdev,
const struct drm_display_mode *mode)
{
struct venc_device *venc = dssdev_to_venc(dssdev);
enum venc_videomode venc_mode = venc_get_videomode(mode);
DSSDBG("venc_set_timings\n");
mutex_lock(&venc->venc_lock);
switch (venc_mode) {
default:
WARN_ON_ONCE(1);
/* Fall-through */
case VENC_MODE_PAL:
venc->config = &venc_config_pal_trm;
break;
case VENC_MODE_NTSC:
venc->config = &venc_config_ntsc_trm;
break;
}
dispc_set_tv_pclk(venc->dss->dispc, 13500000);
mutex_unlock(&venc->venc_lock);
}
static int venc_check_timings(struct omap_dss_device *dssdev,
struct drm_display_mode *mode)
{
DSSDBG("venc_check_timings\n");
switch (venc_get_videomode(mode)) {
case VENC_MODE_PAL:
drm_mode_copy(mode, &omap_dss_pal_mode);
break;
case VENC_MODE_NTSC:
drm_mode_copy(mode, &omap_dss_ntsc_mode);
break;
default:
return -EINVAL;
}
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
drm_mode_set_name(mode);
return 0;
}
static int venc_dump_regs(struct seq_file *s, void *p)
{
struct venc_device *venc = s->private;
@ -673,31 +573,149 @@ static int venc_get_clocks(struct venc_device *venc)
return 0;
}
static int venc_connect(struct omap_dss_device *src,
struct omap_dss_device *dst)
/* -----------------------------------------------------------------------------
* DRM Bridge Operations
*/
static int venc_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
return omapdss_device_connect(dst->dss, dst, dst->next);
struct venc_device *venc = drm_bridge_to_venc(bridge);
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
return -EINVAL;
return drm_bridge_attach(bridge->encoder, venc->output.next_bridge,
bridge, flags);
}
static void venc_disconnect(struct omap_dss_device *src,
struct omap_dss_device *dst)
static enum drm_mode_status
venc_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_mode *mode)
{
omapdss_device_disconnect(dst, dst->next);
switch (venc_get_videomode(mode)) {
case VENC_MODE_PAL:
case VENC_MODE_NTSC:
return MODE_OK;
default:
return MODE_BAD;
}
}
static const struct omap_dss_device_ops venc_ops = {
.connect = venc_connect,
.disconnect = venc_disconnect,
static bool venc_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
const struct drm_display_mode *venc_mode;
.enable = venc_display_enable,
.disable = venc_display_disable,
switch (venc_get_videomode(adjusted_mode)) {
case VENC_MODE_PAL:
venc_mode = &omap_dss_pal_mode;
break;
.check_timings = venc_check_timings,
.set_timings = venc_set_timings,
case VENC_MODE_NTSC:
venc_mode = &omap_dss_ntsc_mode;
break;
.get_modes = venc_get_modes,
default:
return false;
}
drm_mode_copy(adjusted_mode, venc_mode);
drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
drm_mode_set_name(adjusted_mode);
return true;
}
static void venc_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{
struct venc_device *venc = drm_bridge_to_venc(bridge);
enum venc_videomode venc_mode = venc_get_videomode(adjusted_mode);
switch (venc_mode) {
default:
WARN_ON_ONCE(1);
/* Fall-through */
case VENC_MODE_PAL:
venc->config = &venc_config_pal_trm;
break;
case VENC_MODE_NTSC:
venc->config = &venc_config_ntsc_trm;
break;
}
dispc_set_tv_pclk(venc->dss->dispc, 13500000);
}
static void venc_bridge_enable(struct drm_bridge *bridge)
{
struct venc_device *venc = drm_bridge_to_venc(bridge);
venc_power_on(venc);
}
static void venc_bridge_disable(struct drm_bridge *bridge)
{
struct venc_device *venc = drm_bridge_to_venc(bridge);
venc_power_off(venc);
}
static int venc_bridge_get_modes(struct drm_bridge *bridge,
struct drm_connector *connector)
{
static const struct drm_display_mode *modes[] = {
&omap_dss_pal_mode,
&omap_dss_ntsc_mode,
};
unsigned int i;
for (i = 0; i < ARRAY_SIZE(modes); ++i) {
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, modes[i]);
if (!mode)
return i;
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
}
return ARRAY_SIZE(modes);
}
static const struct drm_bridge_funcs venc_bridge_funcs = {
.attach = venc_bridge_attach,
.mode_valid = venc_bridge_mode_valid,
.mode_fixup = venc_bridge_mode_fixup,
.mode_set = venc_bridge_mode_set,
.enable = venc_bridge_enable,
.disable = venc_bridge_disable,
.get_modes = venc_bridge_get_modes,
};
static void venc_bridge_init(struct venc_device *venc)
{
venc->bridge.funcs = &venc_bridge_funcs;
venc->bridge.of_node = venc->pdev->dev.of_node;
venc->bridge.ops = DRM_BRIDGE_OP_MODES;
venc->bridge.type = DRM_MODE_CONNECTOR_SVIDEO;
venc->bridge.interlace_allowed = true;
drm_bridge_add(&venc->bridge);
}
static void venc_bridge_cleanup(struct venc_device *venc)
{
drm_bridge_remove(&venc->bridge);
}
/* -----------------------------------------------------------------------------
* Component Bind & Unbind
*/
@ -747,19 +765,22 @@ static int venc_init_output(struct venc_device *venc)
struct omap_dss_device *out = &venc->output;
int r;
venc_bridge_init(venc);
out->dev = &venc->pdev->dev;
out->id = OMAP_DSS_OUTPUT_VENC;
out->type = OMAP_DISPLAY_TYPE_VENC;
out->name = "venc.0";
out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
out->ops = &venc_ops;
out->owner = THIS_MODULE;
out->of_ports = BIT(0);
out->of_port = 0;
out->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
r = omapdss_device_init_output(out);
if (r < 0)
r = omapdss_device_init_output(out, &venc->bridge);
if (r < 0) {
venc_bridge_cleanup(venc);
return r;
}
omapdss_device_register(out);
@ -770,6 +791,8 @@ static void venc_uninit_output(struct venc_device *venc)
{
omapdss_device_unregister(&venc->output);
omapdss_device_cleanup_output(&venc->output);
venc_bridge_cleanup(venc);
}
static int venc_probe_of(struct venc_device *venc)
@ -839,8 +862,6 @@ static int venc_probe(struct platform_device *pdev)
if (soc_device_match(venc_soc_devices))
venc->requires_tv_dac_clk = true;
mutex_init(&venc->venc_lock);
venc->config = &venc_config_pal_trm;
venc_mem = platform_get_resource(venc->pdev, IORESOURCE_MEM, 0);

View File

@ -6,7 +6,6 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
#include "omap_drv.h"
@ -20,124 +19,12 @@
struct omap_connector {
struct drm_connector base;
struct omap_dss_device *output;
struct omap_dss_device *hpd;
bool hdmi_mode;
};
static void omap_connector_hpd_notify(struct drm_connector *connector,
enum drm_connector_status status)
{
struct omap_connector *omap_connector = to_omap_connector(connector);
struct omap_dss_device *dssdev;
if (status != connector_status_disconnected)
return;
/*
* Notify all devics in the pipeline of disconnection. This is required
* to let the HDMI encoders reset their internal state related to
* connection status, such as the CEC address.
*/
for (dssdev = omap_connector->output; dssdev; dssdev = dssdev->next) {
if (dssdev->ops && dssdev->ops->hdmi.lost_hotplug)
dssdev->ops->hdmi.lost_hotplug(dssdev);
}
}
static void omap_connector_hpd_cb(void *cb_data,
enum drm_connector_status status)
{
struct omap_connector *omap_connector = cb_data;
struct drm_connector *connector = &omap_connector->base;
struct drm_device *dev = connector->dev;
enum drm_connector_status old_status;
mutex_lock(&dev->mode_config.mutex);
old_status = connector->status;
connector->status = status;
mutex_unlock(&dev->mode_config.mutex);
if (old_status == status)
return;
omap_connector_hpd_notify(connector, status);
drm_kms_helper_hotplug_event(dev);
}
void omap_connector_enable_hpd(struct drm_connector *connector)
{
struct omap_connector *omap_connector = to_omap_connector(connector);
struct omap_dss_device *hpd = omap_connector->hpd;
if (hpd)
hpd->ops->register_hpd_cb(hpd, omap_connector_hpd_cb,
omap_connector);
}
void omap_connector_disable_hpd(struct drm_connector *connector)
{
struct omap_connector *omap_connector = to_omap_connector(connector);
struct omap_dss_device *hpd = omap_connector->hpd;
if (hpd)
hpd->ops->unregister_hpd_cb(hpd);
}
bool omap_connector_get_hdmi_mode(struct drm_connector *connector)
{
struct omap_connector *omap_connector = to_omap_connector(connector);
return omap_connector->hdmi_mode;
}
static struct omap_dss_device *
omap_connector_find_device(struct drm_connector *connector,
enum omap_dss_device_ops_flag op)
{
struct omap_connector *omap_connector = to_omap_connector(connector);
struct omap_dss_device *dssdev = NULL;
struct omap_dss_device *d;
for (d = omap_connector->output; d; d = d->next) {
if (d->ops_flags & op)
dssdev = d;
}
return dssdev;
}
static enum drm_connector_status omap_connector_detect(
struct drm_connector *connector, bool force)
{
struct omap_dss_device *dssdev;
enum drm_connector_status status;
dssdev = omap_connector_find_device(connector,
OMAP_DSS_DEVICE_OP_DETECT);
if (dssdev) {
status = dssdev->ops->detect(dssdev)
? connector_status_connected
: connector_status_disconnected;
omap_connector_hpd_notify(connector, status);
} else {
switch (connector->connector_type) {
case DRM_MODE_CONNECTOR_DPI:
case DRM_MODE_CONNECTOR_LVDS:
case DRM_MODE_CONNECTOR_DSI:
status = connector_status_connected;
break;
default:
status = connector_status_unknown;
break;
}
}
VERB("%s: %d (force=%d)", connector->name, status, force);
return status;
return connector_status_connected;
}
static void omap_connector_destroy(struct drm_connector *connector)
@ -146,14 +33,6 @@ static void omap_connector_destroy(struct drm_connector *connector)
DBG("%s", connector->name);
if (omap_connector->hpd) {
struct omap_dss_device *hpd = omap_connector->hpd;
hpd->ops->unregister_hpd_cb(hpd);
omapdss_device_put(hpd);
omap_connector->hpd = NULL;
}
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
@ -162,81 +41,27 @@ static void omap_connector_destroy(struct drm_connector *connector)
kfree(omap_connector);
}
#define MAX_EDID 512
static int omap_connector_get_modes_edid(struct drm_connector *connector,
struct omap_dss_device *dssdev)
{
struct omap_connector *omap_connector = to_omap_connector(connector);
enum drm_connector_status status;
void *edid;
int n;
status = omap_connector_detect(connector, false);
if (status != connector_status_connected)
goto no_edid;
edid = kzalloc(MAX_EDID, GFP_KERNEL);
if (!edid)
goto no_edid;
if (dssdev->ops->read_edid(dssdev, edid, MAX_EDID) <= 0 ||
!drm_edid_is_valid(edid)) {
kfree(edid);
goto no_edid;
}
drm_connector_update_edid_property(connector, edid);
n = drm_add_edid_modes(connector, edid);
omap_connector->hdmi_mode = drm_detect_hdmi_monitor(edid);
kfree(edid);
return n;
no_edid:
drm_connector_update_edid_property(connector, NULL);
return 0;
}
static int omap_connector_get_modes(struct drm_connector *connector)
{
struct omap_connector *omap_connector = to_omap_connector(connector);
struct omap_dss_device *dssdev;
struct omap_dss_device *dssdev = NULL;
struct omap_dss_device *d;
DBG("%s", connector->name);
/*
* If display exposes EDID, then we parse that in the normal way to
* build table of supported modes.
* If the display pipeline reports modes (e.g. with a fixed resolution
* panel or an analog TV output), query it.
*/
dssdev = omap_connector_find_device(connector,
OMAP_DSS_DEVICE_OP_EDID);
if (dssdev)
return omap_connector_get_modes_edid(connector, dssdev);
for (d = omap_connector->output; d; d = d->next) {
if (d->ops_flags & OMAP_DSS_DEVICE_OP_MODES)
dssdev = d;
}
/*
* Otherwise if the display pipeline reports modes (e.g. with a fixed
* resolution panel or an analog TV output), query it.
*/
dssdev = omap_connector_find_device(connector,
OMAP_DSS_DEVICE_OP_MODES);
if (dssdev)
return dssdev->ops->get_modes(dssdev, connector);
/*
* Otherwise if the display pipeline uses a drm_panel, we delegate the
* operation to the panel API.
*/
if (omap_connector->output->panel)
return drm_panel_get_modes(omap_connector->output->panel,
connector);
/*
* We can't retrieve modes, which can happen for instance for a DVI or
* VGA output with the DDC bus unconnected. The KMS core will add the
* default modes.
*/
/* We can't retrieve modes. The KMS core will add the default modes. */
return 0;
}
@ -249,7 +74,7 @@ enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev,
drm_mode_copy(adjusted_mode, mode);
for (; dssdev; dssdev = dssdev->next) {
if (!dssdev->ops->check_timings)
if (!dssdev->ops || !dssdev->ops->check_timings)
continue;
ret = dssdev->ops->check_timings(dssdev, adjusted_mode);
@ -298,35 +123,6 @@ static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
.mode_valid = omap_connector_mode_valid,
};
static int omap_connector_get_type(struct omap_dss_device *output)
{
struct omap_dss_device *display;
enum omap_display_type type;
display = omapdss_display_get(output);
type = display->type;
omapdss_device_put(display);
switch (type) {
case OMAP_DISPLAY_TYPE_HDMI:
return DRM_MODE_CONNECTOR_HDMIA;
case OMAP_DISPLAY_TYPE_DVI:
return DRM_MODE_CONNECTOR_DVID;
case OMAP_DISPLAY_TYPE_DSI:
return DRM_MODE_CONNECTOR_DSI;
case OMAP_DISPLAY_TYPE_DPI:
case OMAP_DISPLAY_TYPE_DBI:
return DRM_MODE_CONNECTOR_DPI;
case OMAP_DISPLAY_TYPE_VENC:
/* TODO: This could also be composite */
return DRM_MODE_CONNECTOR_SVIDEO;
case OMAP_DISPLAY_TYPE_SDI:
return DRM_MODE_CONNECTOR_LVDS;
default:
return DRM_MODE_CONNECTOR_Unknown;
}
}
/* initialize connector */
struct drm_connector *omap_connector_init(struct drm_device *dev,
struct omap_dss_device *output,
@ -334,7 +130,6 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
{
struct drm_connector *connector = NULL;
struct omap_connector *omap_connector;
struct omap_dss_device *dssdev;
DBG("%s", output->name);
@ -349,27 +144,9 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
connector->doublescan_allowed = 0;
drm_connector_init(dev, connector, &omap_connector_funcs,
omap_connector_get_type(output));
DRM_MODE_CONNECTOR_DSI);
drm_connector_helper_add(connector, &omap_connector_helper_funcs);
/*
* Initialize connector status handling. First try to find a device that
* supports hot-plug reporting. If it fails, fall back to a device that
* support polling. If that fails too, we don't support hot-plug
* detection at all.
*/
dssdev = omap_connector_find_device(connector, OMAP_DSS_DEVICE_OP_HPD);
if (dssdev) {
omap_connector->hpd = omapdss_device_get(dssdev);
connector->polled = DRM_CONNECTOR_POLL_HPD;
} else {
dssdev = omap_connector_find_device(connector,
OMAP_DSS_DEVICE_OP_DETECT);
if (dssdev)
connector->polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT;
}
return connector;
fail:

View File

@ -21,9 +21,6 @@ struct omap_dss_device;
struct drm_connector *omap_connector_init(struct drm_device *dev,
struct omap_dss_device *output,
struct drm_encoder *encoder);
bool omap_connector_get_hdmi_mode(struct drm_connector *connector);
void omap_connector_enable_hpd(struct drm_connector *connector);
void omap_connector_disable_hpd(struct drm_connector *connector);
enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);

View File

@ -12,6 +12,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_file.h>
@ -134,9 +135,6 @@ static void omap_disconnect_pipelines(struct drm_device *ddev)
for (i = 0; i < priv->num_pipes; i++) {
struct omap_drm_pipeline *pipe = &priv->pipes[i];
if (pipe->output->panel)
drm_panel_detach(pipe->output->panel);
omapdss_device_disconnect(NULL, pipe->output);
omapdss_device_put(pipe->output);
@ -209,11 +207,12 @@ static int omap_display_id(struct omap_dss_device *output)
struct device_node *node = NULL;
if (output->next) {
struct omap_dss_device *display;
struct omap_dss_device *display = output;
while (display->next)
display = display->next;
display = omapdss_display_get(output);
node = display->dev->of_node;
omapdss_device_put(display);
} else if (output->bridge) {
struct drm_bridge *bridge = output->bridge;
@ -221,8 +220,6 @@ static int omap_display_id(struct omap_dss_device *output)
bridge = drm_bridge_get_next_bridge(bridge);
node = bridge->of_node;
} else if (output->panel) {
node = output->panel->dev->of_node;
}
return node ? of_alias_get_id(node, "display") : -ENODEV;
@ -297,9 +294,14 @@ static int omap_modeset_init(struct drm_device *dev)
if (pipe->output->bridge) {
ret = drm_bridge_attach(pipe->encoder,
pipe->output->bridge, NULL);
if (ret < 0)
pipe->output->bridge, NULL,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret < 0) {
dev_err(priv->dev,
"unable to attach bridge %pOF\n",
pipe->output->bridge->of_node);
return ret;
}
}
id = omap_display_id(pipe->output);
@ -330,22 +332,30 @@ static int omap_modeset_init(struct drm_device *dev)
struct drm_encoder *encoder = pipe->encoder;
struct drm_crtc *crtc;
if (!pipe->output->bridge) {
if (pipe->output->next) {
pipe->connector = omap_connector_init(dev, pipe->output,
encoder);
if (!pipe->connector)
return -ENOMEM;
drm_connector_attach_encoder(pipe->connector, encoder);
if (pipe->output->panel) {
ret = drm_panel_attach(pipe->output->panel,
pipe->connector);
if (ret < 0)
return ret;
} else {
pipe->connector = drm_bridge_connector_init(dev, encoder);
if (IS_ERR(pipe->connector)) {
dev_err(priv->dev,
"unable to create bridge connector for %s\n",
pipe->output->name);
return PTR_ERR(pipe->connector);
}
}
drm_connector_attach_encoder(pipe->connector, encoder);
if (pipe->output->panel) {
ret = drm_panel_attach(pipe->output->panel,
pipe->connector);
if (ret < 0)
return ret;
}
crtc = omap_crtc_init(dev, pipe, priv->planes[i]);
if (IS_ERR(crtc))
return PTR_ERR(crtc);
@ -382,6 +392,23 @@ static int omap_modeset_init(struct drm_device *dev)
return 0;
}
static void omap_modeset_fini(struct drm_device *ddev)
{
struct omap_drm_private *priv = ddev->dev_private;
unsigned int i;
omap_drm_irq_uninstall(ddev);
for (i = 0; i < priv->num_pipes; i++) {
struct omap_drm_pipeline *pipe = &priv->pipes[i];
if (pipe->output->panel)
drm_panel_detach(pipe->output->panel);
}
drm_mode_config_cleanup(ddev);
}
/*
* Enable the HPD in external components if supported
*/
@ -391,8 +418,13 @@ static void omap_modeset_enable_external_hpd(struct drm_device *ddev)
unsigned int i;
for (i = 0; i < priv->num_pipes; i++) {
if (priv->pipes[i].connector)
omap_connector_enable_hpd(priv->pipes[i].connector);
struct drm_connector *connector = priv->pipes[i].connector;
if (!connector)
continue;
if (priv->pipes[i].output->bridge)
drm_bridge_connector_enable_hpd(connector);
}
}
@ -405,8 +437,13 @@ static void omap_modeset_disable_external_hpd(struct drm_device *ddev)
unsigned int i;
for (i = 0; i < priv->num_pipes; i++) {
if (priv->pipes[i].connector)
omap_connector_disable_hpd(priv->pipes[i].connector);
struct drm_connector *connector = priv->pipes[i].connector;
if (!connector)
continue;
if (priv->pipes[i].output->bridge)
drm_bridge_connector_disable_hpd(connector);
}
}
@ -629,8 +666,7 @@ err_cleanup_helpers:
omap_fbdev_fini(ddev);
err_cleanup_modeset:
drm_mode_config_cleanup(ddev);
omap_drm_irq_uninstall(ddev);
omap_modeset_fini(ddev);
err_gem_deinit:
omap_gem_deinit(ddev);
destroy_workqueue(priv->wq);
@ -655,9 +691,7 @@ static void omapdrm_cleanup(struct omap_drm_private *priv)
drm_atomic_helper_shutdown(ddev);
drm_mode_config_cleanup(ddev);
omap_drm_irq_uninstall(ddev);
omap_modeset_fini(ddev);
omap_gem_deinit(ddev);
destroy_workqueue(priv->wq);

View File

@ -10,7 +10,6 @@
#include <drm/drm_crtc.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_edid.h>
#include <drm/drm_panel.h>
#include "omap_drv.h"
@ -70,30 +69,6 @@ static void omap_encoder_update_videomode_flags(struct videomode *vm,
}
}
static void omap_encoder_hdmi_mode_set(struct drm_connector *connector,
struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode)
{
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
struct omap_dss_device *dssdev = omap_encoder->output;
bool hdmi_mode;
hdmi_mode = omap_connector_get_hdmi_mode(connector);
if (dssdev->ops->hdmi.set_hdmi_mode)
dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode);
if (hdmi_mode && dssdev->ops->hdmi.set_infoframe) {
struct hdmi_avi_infoframe avi;
int r;
r = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector,
adjusted_mode);
if (r == 0)
dssdev->ops->hdmi.set_infoframe(dssdev, &avi);
}
}
static void omap_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@ -138,17 +113,8 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
bus_flags = connector->display_info.bus_flags;
omap_encoder_update_videomode_flags(&vm, bus_flags);
/* Set timings for all devices in the display pipeline. */
/* Set timings for the dss manager. */
dss_mgr_set_timings(output, &vm);
for (dssdev = output; dssdev; dssdev = dssdev->next) {
if (dssdev->ops->set_timings)
dssdev->ops->set_timings(dssdev, adjusted_mode);
}
/* Set the HDMI mode and HDMI infoframe if applicable. */
if (output->type == OMAP_DISPLAY_TYPE_HDMI)
omap_encoder_hdmi_mode_set(connector, encoder, adjusted_mode);
}
static void omap_encoder_disable(struct drm_encoder *encoder)
@ -159,33 +125,12 @@ static void omap_encoder_disable(struct drm_encoder *encoder)
dev_dbg(dev->dev, "disable(%s)\n", dssdev->name);
/* Disable the panel if present. */
if (dssdev->panel) {
drm_panel_disable(dssdev->panel);
drm_panel_unprepare(dssdev->panel);
}
/*
* Disable the chain of external devices, starting at the one at the
* internal encoder's output.
* internal encoder's output. This is used for DSI outputs only, as
* dssdev->next is NULL for all other outputs.
*/
omapdss_device_disable(dssdev->next);
/*
* Disable the internal encoder. This will disable the DSS output. The
* DSI is treated as an exception as DSI pipelines still use the legacy
* flow where the pipeline output controls the encoder.
*/
if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) {
dssdev->ops->disable(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
/*
* Perform the post-disable operations on the chain of external devices
* to complete the display pipeline disable.
*/
omapdss_device_post_disable(dssdev->next);
}
static void omap_encoder_enable(struct drm_encoder *encoder)
@ -196,30 +141,12 @@ static void omap_encoder_enable(struct drm_encoder *encoder)
dev_dbg(dev->dev, "enable(%s)\n", dssdev->name);
/* Prepare the chain of external devices for pipeline enable. */
omapdss_device_pre_enable(dssdev->next);
/*
* Enable the internal encoder. This will enable the DSS output. The
* DSI is treated as an exception as DSI pipelines still use the legacy
* flow where the pipeline output controls the encoder.
*/
if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) {
dssdev->ops->enable(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
}
/*
* Enable the chain of external devices, starting at the one at the
* internal encoder's output.
* internal encoder's output. This is used for DSI outputs only, as
* dssdev->next is NULL for all other outputs.
*/
omapdss_device_enable(dssdev->next);
/* Enable the panel if present. */
if (dssdev->panel) {
drm_panel_prepare(dssdev->panel);
drm_panel_enable(dssdev->panel);
}
}
static int omap_encoder_atomic_check(struct drm_encoder *encoder,

View File

@ -373,6 +373,12 @@ static const struct of_device_id ld9040_of_match[] = {
};
MODULE_DEVICE_TABLE(of, ld9040_of_match);
static const struct spi_device_id ld9040_ids[] = {
{ "ld9040", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(spi, ld9040_ids);
static struct spi_driver ld9040_driver = {
.probe = ld9040_probe,
.remove = ld9040_remove,

View File

@ -2580,7 +2580,8 @@ static const struct panel_desc osddisplays_osd070t1718_19ts = {
.height = 91,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE |
DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE,
.connector_type = DRM_MODE_CONNECTOR_DPI,
};

View File

@ -5,6 +5,7 @@
#include <linux/clk.h>
#include <linux/reset.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/regulator/consumer.h>
#include "panfrost_device.h"
@ -87,18 +88,27 @@ static void panfrost_clk_fini(struct panfrost_device *pfdev)
static int panfrost_regulator_init(struct panfrost_device *pfdev)
{
int ret;
int ret, i;
pfdev->regulator = devm_regulator_get(pfdev->dev, "mali");
if (IS_ERR(pfdev->regulator)) {
ret = PTR_ERR(pfdev->regulator);
dev_err(pfdev->dev, "failed to get regulator: %d\n", ret);
if (WARN(pfdev->comp->num_supplies > ARRAY_SIZE(pfdev->regulators),
"Too many supplies in compatible structure.\n"))
return -EINVAL;
for (i = 0; i < pfdev->comp->num_supplies; i++)
pfdev->regulators[i].supply = pfdev->comp->supply_names[i];
ret = devm_regulator_bulk_get(pfdev->dev,
pfdev->comp->num_supplies,
pfdev->regulators);
if (ret < 0) {
dev_err(pfdev->dev, "failed to get regulators: %d\n", ret);
return ret;
}
ret = regulator_enable(pfdev->regulator);
ret = regulator_bulk_enable(pfdev->comp->num_supplies,
pfdev->regulators);
if (ret < 0) {
dev_err(pfdev->dev, "failed to enable regulator: %d\n", ret);
dev_err(pfdev->dev, "failed to enable regulators: %d\n", ret);
return ret;
}
@ -107,7 +117,81 @@ static int panfrost_regulator_init(struct panfrost_device *pfdev)
static void panfrost_regulator_fini(struct panfrost_device *pfdev)
{
regulator_disable(pfdev->regulator);
regulator_bulk_disable(pfdev->comp->num_supplies,
pfdev->regulators);
}
static void panfrost_pm_domain_fini(struct panfrost_device *pfdev)
{
int i;
for (i = 0; i < ARRAY_SIZE(pfdev->pm_domain_devs); i++) {
if (!pfdev->pm_domain_devs[i])
break;
if (pfdev->pm_domain_links[i])
device_link_del(pfdev->pm_domain_links[i]);
dev_pm_domain_detach(pfdev->pm_domain_devs[i], true);
}
}
static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
{
int err;
int i, num_domains;
num_domains = of_count_phandle_with_args(pfdev->dev->of_node,
"power-domains",
"#power-domain-cells");
/*
* Single domain is handled by the core, and, if only a single power
* the power domain is requested, the property is optional.
*/
if (num_domains < 2 && pfdev->comp->num_pm_domains < 2)
return 0;
if (num_domains != pfdev->comp->num_pm_domains) {
dev_err(pfdev->dev,
"Incorrect number of power domains: %d provided, %d needed\n",
num_domains, pfdev->comp->num_pm_domains);
return -EINVAL;
}
if (WARN(num_domains > ARRAY_SIZE(pfdev->pm_domain_devs),
"Too many supplies in compatible structure.\n"))
return -EINVAL;
for (i = 0; i < num_domains; i++) {
pfdev->pm_domain_devs[i] =
dev_pm_domain_attach_by_name(pfdev->dev,
pfdev->comp->pm_domain_names[i]);
if (IS_ERR_OR_NULL(pfdev->pm_domain_devs[i])) {
err = PTR_ERR(pfdev->pm_domain_devs[i]) ? : -ENODATA;
pfdev->pm_domain_devs[i] = NULL;
dev_err(pfdev->dev,
"failed to get pm-domain %s(%d): %d\n",
pfdev->comp->pm_domain_names[i], i, err);
goto err;
}
pfdev->pm_domain_links[i] = device_link_add(pfdev->dev,
pfdev->pm_domain_devs[i], DL_FLAG_PM_RUNTIME |
DL_FLAG_STATELESS | DL_FLAG_RPM_ACTIVE);
if (!pfdev->pm_domain_links[i]) {
dev_err(pfdev->pm_domain_devs[i],
"adding device link failed!\n");
err = -ENODEV;
goto err;
}
}
return 0;
err:
panfrost_pm_domain_fini(pfdev);
return err;
}
int panfrost_device_init(struct panfrost_device *pfdev)
@ -140,37 +224,43 @@ int panfrost_device_init(struct panfrost_device *pfdev)
goto err_out1;
}
err = panfrost_pm_domain_init(pfdev);
if (err)
goto err_out2;
res = platform_get_resource(pfdev->pdev, IORESOURCE_MEM, 0);
pfdev->iomem = devm_ioremap_resource(pfdev->dev, res);
if (IS_ERR(pfdev->iomem)) {
dev_err(pfdev->dev, "failed to ioremap iomem\n");
err = PTR_ERR(pfdev->iomem);
goto err_out2;
goto err_out3;
}
err = panfrost_gpu_init(pfdev);
if (err)
goto err_out2;
goto err_out3;
err = panfrost_mmu_init(pfdev);
if (err)
goto err_out3;
goto err_out4;
err = panfrost_job_init(pfdev);
if (err)
goto err_out4;
goto err_out5;
err = panfrost_perfcnt_init(pfdev);
if (err)
goto err_out5;
goto err_out6;
return 0;
err_out5:
err_out6:
panfrost_job_fini(pfdev);
err_out4:
err_out5:
panfrost_mmu_fini(pfdev);
err_out3:
err_out4:
panfrost_gpu_fini(pfdev);
err_out3:
panfrost_pm_domain_fini(pfdev);
err_out2:
panfrost_reset_fini(pfdev);
err_out1:
@ -186,6 +276,7 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
panfrost_job_fini(pfdev);
panfrost_mmu_fini(pfdev);
panfrost_gpu_fini(pfdev);
panfrost_pm_domain_fini(pfdev);
panfrost_reset_fini(pfdev);
panfrost_regulator_fini(pfdev);
panfrost_clk_fini(pfdev);

Some files were not shown because too many files have changed in this diff Show More