drm/panel: Changes for v3.20-rc1

This contains the long-awaited drm_bridge series that makes Chromebooks
 work for people. I had thought this would've been perfect by now, but
 then I go and build test it and the first thing it does is yell about a
 recursive dependency. I fixed that up because I was feeling bad for not
 getting around to look at this earlier.
 
 Biseds that there is new support for two more panels, a couple of fixup
 patches to the Sharp LQ101R1SX01 dual-channel DSI panel driver and a
 potential NULL pointer dereference fix.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABAgAGBQJUyKWaAAoJEN0jrNd/PrOhtEkP/jI1A4UKGTWsMPrWa2Sco6Vm
 afFYHX/7UEARLpXeI86dV/cIY3ec9uLPTJrXhZT8qvi14XXnQjyc3ku1TXlCwvBS
 OinFu6J1ABPnmtLvItJY4lc9/AkVQ8NVKDgdCEwo6bVJX2BEdMVcvT2byzkpdmMQ
 hcJe0aK0UFPiuw35TFl4yfVeTsliPVrHI4eSMzODQNPGpZVZ5098E77B3PjHnVOD
 pW/YIl/4llR3hsHn6lso7eoBOVpUM6dm3cd08UFKEkhq+ICwe2Q9OBOcAQdQVnRb
 V7by53YYnD+5nASE+vz1ZPpiR9CJNwwCOtRt1QpO9pHlxjkr2fJfgdu5Tgg6MbnS
 GI/sXpyKvMATpSPzB5/a1dCbHEcvs9W1Wf9jjyL2OmG5zKboGCIp2BOvOGkmo6re
 jpVPfzClfXJXuT5mVDi9xphFT3fR4HIfAqbm+km3RvH9MMCL0ZqhTqcyANVXskjb
 +brKUI9704MyPFx1AAfYjRHD0NLzKNTKQECDGuD+gUkuViyM+E5tNGgm6Szr53J3
 IuRI3zUKCnMoNbLi3buslebu3/Bmd01SvkTpm8VOTw1ey8bEFEhGephTO5koaFuT
 4GhH5sBNIx+mopOGazLpM9A0h7Utxq1PXoaQY9SRbiQNmyGlQuSEyf4VmrwQySgL
 TZr2yx/3tIVUagkwzf8K
 =5PkY
 -----END PGP SIGNATURE-----

Merge tag 'drm/panel/for-3.20-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next

drm/panel: Changes for v3.20-rc1

This contains the long-awaited drm_bridge series that makes Chromebooks
work for people. I had thought this would've been perfect by now, but
then I go and build test it and the first thing it does is yell about a
recursive dependency. I fixed that up because I was feeling bad for not
getting around to look at this earlier.

Biseds that there is new support for two more panels, a couple of fixup
patches to the Sharp LQ101R1SX01 dual-channel DSI panel driver and a
potential NULL pointer dereference fix.

* tag 'drm/panel/for-3.20-rc1' of git://anongit.freedesktop.org/tegra/linux: (23 commits)
  drm/bridge: dw-hdmi: Adapt to bridge API change
  drm/sti: fixup for bridge interface
  drm/bridge: dw-hdmi: Fix return error path
  drm: Check the right variable when setting formats
  Documentation: bridge: Add documentation for ps8622 DT properties
  Documentation: devicetree: Add vendor prefix for parade
  Documentation: drm: bridge: move to video/bridge
  drm/bridge: ptn3460: use gpiod interface
  drm/bridge: ptn3460: probe connector at the end of bridge attach
  drm/bridge: ptn3460: support drm_panel
  drm/exynos: dp: support drm_bridge
  drm/bridge: ptn3460: Convert to I2C driver model
  drm/bridge: make bridge registration independent of drm flow
  drm/bridge: do not pass drm_bridge_funcs to drm_bridge_init
  drm/bridge: ptn3460: Few trivial cleanups
  drm/panel: simple: Add AVIC TM070DDH03 panel support
  of: Add vendor prefix for Shanghai AVIC Optoelectronics Co., Ltd.
  drm/panel: sharp: lq101r1sx01: Remove unneeded include
  drm/panel: sharp: lq101r1sx01: Respect power timings
  drm/panel: sharp: lq101r1sx01: Add delay after display on
  ...
This commit is contained in:
Dave Airlie 2015-01-29 11:29:35 +10:00
commit 7b83741bf7
25 changed files with 545 additions and 308 deletions

View File

@ -0,0 +1,7 @@
Shanghai AVIC Optoelectronics 7" 1024x600 color TFT-LCD panel
Required properties:
- compatible: should be "avic,tm070ddh03"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.

View File

@ -0,0 +1,7 @@
GiantPlus GPG48273QS5 4.3" (480x272) WQVGA TFT LCD panel
Required properties:
- compatible: should be "giantplus,gpg48273qs5"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.

View File

@ -23,6 +23,7 @@ armadeus ARMadeus Systems SARL
atmel Atmel Corporation
auo AU Optronics Corporation
avago Avago Technologies
avic Shanghai AVIC Optoelectronics Co., Ltd.
bosch Bosch Sensortec GmbH
brcm Broadcom Corporation
buffalo Buffalo, Inc.
@ -62,6 +63,7 @@ fsl Freescale Semiconductor
GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc.
gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
geniatech Geniatech, Inc.
giantplus Giantplus Technology Co., Ltd.
globalscale Globalscale Technologies, Inc.
gmt Global Mixed-mode Technology, Inc.
google Google, Inc.
@ -119,6 +121,7 @@ nxp NXP Semiconductors
onnn ON Semiconductor Corp.
opencores OpenCores.org
panasonic Panasonic Corporation
parade Parade Technologies Inc.
pericom Pericom Technology Inc.
phytec PHYTEC Messtechnik GmbH
picochip Picochip Ltd

View File

@ -0,0 +1,31 @@
ps8622-bridge bindings
Required properties:
- compatible: "parade,ps8622" or "parade,ps8625"
- reg: first i2c address of the bridge
- sleep-gpios: OF device-tree gpio specification for PD_ pin.
- reset-gpios: OF device-tree gpio specification for RST_ pin.
Optional properties:
- lane-count: number of DP lanes to use
- use-external-pwm: backlight will be controlled by an external PWM
- video interfaces: Device node can contain video interface port
nodes for panel according to [1].
[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
Example:
lvds-bridge@48 {
compatible = "parade,ps8622";
reg = <0x48>;
sleep-gpios = <&gpc3 6 1 0 0>;
reset-gpios = <&gpc3 1 1 0 0>;
lane-count = <1>;
ports {
port@0 {
bridge_out: endpoint {
remote-endpoint = <&panel_in>;
};
};
};
};

View File

@ -3,8 +3,8 @@ ptn3460 bridge bindings
Required properties:
- compatible: "nxp,ptn3460"
- reg: i2c address of the bridge
- powerdown-gpio: OF device-tree gpio specification
- reset-gpio: OF device-tree gpio specification
- powerdown-gpio: OF device-tree gpio specification for PD_N pin.
- reset-gpio: OF device-tree gpio specification for RST_N pin.
- edid-emulation: The EDID emulation entry to use
+-------+------------+------------------+
| Value | Resolution | Description |
@ -17,6 +17,11 @@ Required properties:
| 6 | 1600x900 | ChiMei M215HGE |
+-------+------------+------------------+
- video interfaces: Device node can contain video interface port
nodes for panel according to [1].
[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
Example:
lvds-bridge@20 {
compatible = "nxp,ptn3460";
@ -24,4 +29,11 @@ Example:
powerdown-gpio = <&gpy2 5 1 0 0>;
reset-gpio = <&gpx1 5 1 0 0>;
edid-emulation = <5>;
ports {
port@0 {
bridge_out: endpoint {
remote-endpoint = <&panel_in>;
};
};
};
};

View File

@ -66,6 +66,10 @@ Optional properties for dp-controller:
Hotplug detect GPIO.
Indicates which GPIO should be used for hotplug
detection
-video interfaces: Device node can contain video interface port
nodes according to [1].
[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
Example:
@ -105,4 +109,12 @@ Board Specific portion:
vsync-len = <6>;
};
};
ports {
port@0 {
dp_out: endpoint {
remote-endpoint = <&bridge_in>;
};
};
};
};

View File

@ -14,7 +14,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \
drm_info.o drm_debugfs.o drm_encoder_slave.o \
drm_trace_points.o drm_global.o drm_prime.o \
drm_rect.o drm_vma_manager.o drm_flip_work.o \
drm_modeset_lock.o drm_atomic.o
drm_modeset_lock.o drm_atomic.o drm_bridge.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o

View File

@ -1,10 +1,13 @@
config DRM_PTN3460
tristate "PTN3460 DP/LVDS bridge"
depends on DRM
select DRM_KMS_HELPER
---help---
config DRM_DW_HDMI
tristate
depends on DRM
select DRM_KMS_HELPER
config DRM_PTN3460
tristate "PTN3460 DP/LVDS bridge"
depends on DRM
depends on OF
select DRM_KMS_HELPER
select DRM_PANEL
---help---
ptn3460 eDP-LVDS bridge chip driver.

View File

@ -1373,12 +1373,6 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
dw_hdmi_poweron(hdmi);
}
static void dw_hdmi_bridge_destroy(struct drm_bridge *bridge)
{
drm_bridge_cleanup(bridge);
kfree(bridge);
}
static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
{
/* do nothing */
@ -1468,7 +1462,6 @@ struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
.post_disable = dw_hdmi_bridge_nop,
.mode_set = dw_hdmi_bridge_mode_set,
.mode_fixup = dw_hdmi_bridge_mode_fixup,
.destroy = dw_hdmi_bridge_destroy,
};
static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
@ -1531,8 +1524,8 @@ static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi)
hdmi->bridge = bridge;
bridge->driver_private = hdmi;
ret = drm_bridge_init(drm, bridge, &dw_hdmi_bridge_funcs);
bridge->funcs = &dw_hdmi_bridge_funcs;
ret = drm_bridge_attach(drm, bridge);
if (ret) {
DRM_ERROR("Failed to initialize bridge with drm\n");
return -EINVAL;
@ -1649,7 +1642,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
dw_hdmi_irq, IRQF_SHARED,
dev_name(dev), hdmi);
if (ret)
return ret;
goto err_iahb;
/*
* To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator

View File

@ -13,20 +13,23 @@
* GNU General Public License for more details.
*/
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/of_graph.h>
#include "drmP.h"
#include "drm_edid.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include <drm/drm_panel.h>
#include "bridge/ptn3460.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "drm_edid.h"
#include "drmP.h"
#define PTN3460_EDID_ADDR 0x0
#define PTN3460_EDID_EMULATION_ADDR 0x84
#define PTN3460_EDID_ENABLE_EMULATION 0
@ -36,15 +39,27 @@
struct ptn3460_bridge {
struct drm_connector connector;
struct i2c_client *client;
struct drm_encoder *encoder;
struct drm_bridge *bridge;
struct drm_bridge bridge;
struct edid *edid;
int gpio_pd_n;
int gpio_rst_n;
struct drm_panel *panel;
struct gpio_desc *gpio_pd_n;
struct gpio_desc *gpio_rst_n;
u32 edid_emulation;
bool enabled;
};
static inline struct ptn3460_bridge *
bridge_to_ptn3460(struct drm_bridge *bridge)
{
return container_of(bridge, struct ptn3460_bridge, bridge);
}
static inline struct ptn3460_bridge *
connector_to_ptn3460(struct drm_connector *connector)
{
return container_of(connector, struct ptn3460_bridge, connector);
}
static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
u8 *buf, int len)
{
@ -92,7 +107,7 @@ static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
ptn_bridge->edid_emulation);
if (ret) {
DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
DRM_ERROR("Failed to transfer EDID to sram, ret=%d\n", ret);
return ret;
}
@ -102,7 +117,7 @@ static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
if (ret) {
DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
DRM_ERROR("Failed to write EDID value, ret=%d\n", ret);
return ret;
}
@ -111,19 +126,21 @@ static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
static void ptn3460_pre_enable(struct drm_bridge *bridge)
{
struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
int ret;
if (ptn_bridge->enabled)
return;
if (gpio_is_valid(ptn_bridge->gpio_pd_n))
gpio_set_value(ptn_bridge->gpio_pd_n, 1);
gpiod_set_value(ptn_bridge->gpio_pd_n, 1);
if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
gpio_set_value(ptn_bridge->gpio_rst_n, 0);
udelay(10);
gpio_set_value(ptn_bridge->gpio_rst_n, 1);
gpiod_set_value(ptn_bridge->gpio_rst_n, 0);
usleep_range(10, 20);
gpiod_set_value(ptn_bridge->gpio_rst_n, 1);
if (drm_panel_prepare(ptn_bridge->panel)) {
DRM_ERROR("failed to prepare panel\n");
return;
}
/*
@ -135,73 +152,67 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
ret = ptn3460_select_edid(ptn_bridge);
if (ret)
DRM_ERROR("Select edid failed ret=%d\n", ret);
DRM_ERROR("Select EDID failed ret=%d\n", ret);
ptn_bridge->enabled = true;
}
static void ptn3460_enable(struct drm_bridge *bridge)
{
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
if (drm_panel_enable(ptn_bridge->panel)) {
DRM_ERROR("failed to enable panel\n");
return;
}
}
static void ptn3460_disable(struct drm_bridge *bridge)
{
struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
if (!ptn_bridge->enabled)
return;
ptn_bridge->enabled = false;
if (gpio_is_valid(ptn_bridge->gpio_rst_n))
gpio_set_value(ptn_bridge->gpio_rst_n, 1);
if (drm_panel_disable(ptn_bridge->panel)) {
DRM_ERROR("failed to disable panel\n");
return;
}
if (gpio_is_valid(ptn_bridge->gpio_pd_n))
gpio_set_value(ptn_bridge->gpio_pd_n, 0);
gpiod_set_value(ptn_bridge->gpio_rst_n, 1);
gpiod_set_value(ptn_bridge->gpio_pd_n, 0);
}
static void ptn3460_post_disable(struct drm_bridge *bridge)
{
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
if (drm_panel_unprepare(ptn_bridge->panel)) {
DRM_ERROR("failed to unprepare panel\n");
return;
}
}
void ptn3460_bridge_destroy(struct drm_bridge *bridge)
{
struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
drm_bridge_cleanup(bridge);
if (gpio_is_valid(ptn_bridge->gpio_pd_n))
gpio_free(ptn_bridge->gpio_pd_n);
if (gpio_is_valid(ptn_bridge->gpio_rst_n))
gpio_free(ptn_bridge->gpio_rst_n);
/* Nothing else to free, we've got devm allocated memory */
}
struct drm_bridge_funcs ptn3460_bridge_funcs = {
.pre_enable = ptn3460_pre_enable,
.enable = ptn3460_enable,
.disable = ptn3460_disable,
.post_disable = ptn3460_post_disable,
.destroy = ptn3460_bridge_destroy,
};
int ptn3460_get_modes(struct drm_connector *connector)
static int ptn3460_get_modes(struct drm_connector *connector)
{
struct ptn3460_bridge *ptn_bridge;
u8 *edid;
int ret, num_modes;
int ret, num_modes = 0;
bool power_off;
ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
ptn_bridge = connector_to_ptn3460(connector);
if (ptn_bridge->edid)
return drm_add_edid_modes(connector, ptn_bridge->edid);
power_off = !ptn_bridge->enabled;
ptn3460_pre_enable(ptn_bridge->bridge);
ptn3460_pre_enable(&ptn_bridge->bridge);
edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
if (!edid) {
DRM_ERROR("Failed to allocate edid\n");
DRM_ERROR("Failed to allocate EDID\n");
return 0;
}
@ -209,7 +220,6 @@ int ptn3460_get_modes(struct drm_connector *connector)
EDID_LENGTH);
if (ret) {
kfree(edid);
num_modes = 0;
goto out;
}
@ -220,124 +230,188 @@ int ptn3460_get_modes(struct drm_connector *connector)
out:
if (power_off)
ptn3460_disable(ptn_bridge->bridge);
ptn3460_disable(&ptn_bridge->bridge);
return num_modes;
}
struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
static struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
{
struct ptn3460_bridge *ptn_bridge;
struct ptn3460_bridge *ptn_bridge = connector_to_ptn3460(connector);
ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
return ptn_bridge->encoder;
return ptn_bridge->bridge.encoder;
}
struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
static struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
.get_modes = ptn3460_get_modes,
.best_encoder = ptn3460_best_encoder,
};
enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
static enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
bool force)
{
return connector_status_connected;
}
void ptn3460_connector_destroy(struct drm_connector *connector)
static void ptn3460_connector_destroy(struct drm_connector *connector)
{
drm_connector_cleanup(connector);
}
struct drm_connector_funcs ptn3460_connector_funcs = {
static struct drm_connector_funcs ptn3460_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = ptn3460_detect,
.destroy = ptn3460_connector_destroy,
};
int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
struct i2c_client *client, struct device_node *node)
int ptn3460_bridge_attach(struct drm_bridge *bridge)
{
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
int ret;
struct drm_bridge *bridge;
struct ptn3460_bridge *ptn_bridge;
bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
if (!bridge) {
DRM_ERROR("Failed to allocate drm bridge\n");
return -ENOMEM;
if (!bridge->encoder) {
DRM_ERROR("Parent encoder object not found");
return -ENODEV;
}
ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
if (!ptn_bridge) {
DRM_ERROR("Failed to allocate ptn bridge\n");
return -ENOMEM;
}
ptn_bridge->client = client;
ptn_bridge->encoder = encoder;
ptn_bridge->bridge = bridge;
ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);
if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
ret = gpio_request_one(ptn_bridge->gpio_pd_n,
GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
if (ret) {
DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
return ret;
}
}
ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
/*
* Request the reset pin low to avoid the bridge being
* initialized prematurely
*/
ret = gpio_request_one(ptn_bridge->gpio_rst_n,
GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
if (ret) {
DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
gpio_free(ptn_bridge->gpio_pd_n);
return ret;
}
}
ret = of_property_read_u32(node, "edid-emulation",
&ptn_bridge->edid_emulation);
if (ret) {
DRM_ERROR("Can't read edid emulation value\n");
goto err;
}
ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
if (ret) {
DRM_ERROR("Failed to initialize bridge with drm\n");
goto err;
}
bridge->driver_private = ptn_bridge;
encoder->bridge = bridge;
ret = drm_connector_init(dev, &ptn_bridge->connector,
ptn_bridge->connector.polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(bridge->dev, &ptn_bridge->connector,
&ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
if (ret) {
DRM_ERROR("Failed to initialize connector with drm\n");
goto err;
return ret;
}
drm_connector_helper_add(&ptn_bridge->connector,
&ptn3460_connector_helper_funcs);
&ptn3460_connector_helper_funcs);
drm_connector_register(&ptn_bridge->connector);
drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
drm_mode_connector_attach_encoder(&ptn_bridge->connector,
bridge->encoder);
return 0;
if (ptn_bridge->panel)
drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector);
drm_helper_hpd_irq_event(ptn_bridge->connector.dev);
err:
if (gpio_is_valid(ptn_bridge->gpio_pd_n))
gpio_free(ptn_bridge->gpio_pd_n);
if (gpio_is_valid(ptn_bridge->gpio_rst_n))
gpio_free(ptn_bridge->gpio_rst_n);
return ret;
}
EXPORT_SYMBOL(ptn3460_init);
static struct drm_bridge_funcs ptn3460_bridge_funcs = {
.pre_enable = ptn3460_pre_enable,
.enable = ptn3460_enable,
.disable = ptn3460_disable,
.post_disable = ptn3460_post_disable,
.attach = ptn3460_bridge_attach,
};
static int ptn3460_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct ptn3460_bridge *ptn_bridge;
struct device_node *endpoint, *panel_node;
int ret;
ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL);
if (!ptn_bridge) {
return -ENOMEM;
}
endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
if (endpoint) {
panel_node = of_graph_get_remote_port_parent(endpoint);
if (panel_node) {
ptn_bridge->panel = of_drm_find_panel(panel_node);
of_node_put(panel_node);
if (!ptn_bridge->panel)
return -EPROBE_DEFER;
}
}
ptn_bridge->client = client;
ptn_bridge->gpio_pd_n = devm_gpiod_get(&client->dev, "powerdown");
if (IS_ERR(ptn_bridge->gpio_pd_n)) {
ret = PTR_ERR(ptn_bridge->gpio_pd_n);
dev_err(dev, "cannot get gpio_pd_n %d\n", ret);
return ret;
}
ret = gpiod_direction_output(ptn_bridge->gpio_pd_n, 1);
if (ret) {
DRM_ERROR("cannot configure gpio_pd_n\n");
return ret;
}
ptn_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset");
if (IS_ERR(ptn_bridge->gpio_rst_n)) {
ret = PTR_ERR(ptn_bridge->gpio_rst_n);
DRM_ERROR("cannot get gpio_rst_n %d\n", ret);
return ret;
}
/*
* Request the reset pin low to avoid the bridge being
* initialized prematurely
*/
ret = gpiod_direction_output(ptn_bridge->gpio_rst_n, 0);
if (ret) {
DRM_ERROR("cannot configure gpio_rst_n\n");
return ret;
}
ret = of_property_read_u32(dev->of_node, "edid-emulation",
&ptn_bridge->edid_emulation);
if (ret) {
dev_err(dev, "Can't read EDID emulation value\n");
return ret;
}
ptn_bridge->bridge.funcs = &ptn3460_bridge_funcs;
ptn_bridge->bridge.of_node = dev->of_node;
ret = drm_bridge_add(&ptn_bridge->bridge);
if (ret) {
DRM_ERROR("Failed to add bridge\n");
return ret;
}
i2c_set_clientdata(client, ptn_bridge);
return 0;
}
static int ptn3460_remove(struct i2c_client *client)
{
struct ptn3460_bridge *ptn_bridge = i2c_get_clientdata(client);
drm_bridge_remove(&ptn_bridge->bridge);
return 0;
}
static const struct i2c_device_id ptn3460_i2c_table[] = {
{"nxp,ptn3460", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, ptn3460_i2c_table);
static const struct of_device_id ptn3460_match[] = {
{ .compatible = "nxp,ptn3460" },
{},
};
MODULE_DEVICE_TABLE(of, ptn3460_match);
static struct i2c_driver ptn3460_driver = {
.id_table = ptn3460_i2c_table,
.probe = ptn3460_probe,
.remove = ptn3460_remove,
.driver = {
.name = "nxp,ptn3460",
.owner = THIS_MODULE,
.of_match_table = ptn3460_match,
},
};
module_i2c_driver(ptn3460_driver);
MODULE_AUTHOR("Sean Paul <seanpaul@chromium.org>");
MODULE_DESCRIPTION("NXP ptn3460 eDP-LVDS converter driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sub license,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <linux/err.h>
#include <linux/module.h>
#include <drm/drm_crtc.h>
#include "drm/drmP.h"
static DEFINE_MUTEX(bridge_lock);
static LIST_HEAD(bridge_list);
int drm_bridge_add(struct drm_bridge *bridge)
{
mutex_lock(&bridge_lock);
list_add_tail(&bridge->list, &bridge_list);
mutex_unlock(&bridge_lock);
return 0;
}
EXPORT_SYMBOL(drm_bridge_add);
void drm_bridge_remove(struct drm_bridge *bridge)
{
mutex_lock(&bridge_lock);
list_del_init(&bridge->list);
mutex_unlock(&bridge_lock);
}
EXPORT_SYMBOL(drm_bridge_remove);
extern int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge)
{
if (!dev || !bridge)
return -EINVAL;
if (bridge->dev)
return -EBUSY;
bridge->dev = dev;
if (bridge->funcs->attach)
return bridge->funcs->attach(bridge);
return 0;
}
EXPORT_SYMBOL(drm_bridge_attach);
#ifdef CONFIG_OF
struct drm_bridge *of_drm_find_bridge(struct device_node *np)
{
struct drm_bridge *bridge;
mutex_lock(&bridge_lock);
list_for_each_entry(bridge, &bridge_list, list) {
if (bridge->of_node == np) {
mutex_unlock(&bridge_lock);
return bridge;
}
}
mutex_unlock(&bridge_lock);
return NULL;
}
EXPORT_SYMBOL(of_drm_find_bridge);
#endif
MODULE_AUTHOR("Ajay Kumar <ajaykumar.rs@samsung.com>");
MODULE_DESCRIPTION("DRM bridge infrastructure");
MODULE_LICENSE("GPL and additional rights");

View File

@ -787,7 +787,7 @@ int drm_display_info_set_bus_formats(struct drm_display_info *info,
if (formats && num_formats) {
fmts = kmemdup(formats, sizeof(*formats) * num_formats,
GFP_KERNEL);
if (!formats)
if (!fmts)
return -ENOMEM;
}
@ -1065,61 +1065,6 @@ void drm_connector_unplug_all(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_connector_unplug_all);
/**
* drm_bridge_init - initialize a drm transcoder/bridge
* @dev: drm device
* @bridge: transcoder/bridge to set up
* @funcs: bridge function table
*
* Initialises a preallocated bridge. Bridges should be
* subclassed as part of driver connector objects.
*
* Returns:
* Zero on success, error code on failure.
*/
int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge,
const struct drm_bridge_funcs *funcs)
{
int ret;
drm_modeset_lock_all(dev);
ret = drm_mode_object_get(dev, &bridge->base, DRM_MODE_OBJECT_BRIDGE);
if (ret)
goto out;
bridge->dev = dev;
bridge->funcs = funcs;
list_add_tail(&bridge->head, &dev->mode_config.bridge_list);
dev->mode_config.num_bridge++;
out:
drm_modeset_unlock_all(dev);
return ret;
}
EXPORT_SYMBOL(drm_bridge_init);
/**
* drm_bridge_cleanup - cleans up an initialised bridge
* @bridge: bridge to cleanup
*
* Cleans up the bridge but doesn't free the object.
*/
void drm_bridge_cleanup(struct drm_bridge *bridge)
{
struct drm_device *dev = bridge->dev;
drm_modeset_lock_all(dev);
drm_mode_object_put(dev, &bridge->base);
list_del(&bridge->head);
dev->mode_config.num_bridge--;
drm_modeset_unlock_all(dev);
memset(bridge, 0, sizeof(*bridge));
}
EXPORT_SYMBOL(drm_bridge_cleanup);
/**
* drm_encoder_init - Init a preallocated encoder
* @dev: drm device
@ -1715,7 +1660,6 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr
total_objects += dev->mode_config.num_crtc;
total_objects += dev->mode_config.num_connector;
total_objects += dev->mode_config.num_encoder;
total_objects += dev->mode_config.num_bridge;
group->id_list = kcalloc(total_objects, sizeof(uint32_t), GFP_KERNEL);
if (!group->id_list)
@ -1724,7 +1668,6 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr
group->num_crtcs = 0;
group->num_connectors = 0;
group->num_encoders = 0;
group->num_bridges = 0;
return 0;
}
@ -1744,7 +1687,6 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
struct drm_crtc *crtc;
struct drm_encoder *encoder;
struct drm_connector *connector;
struct drm_bridge *bridge;
int ret;
ret = drm_mode_group_init(dev, group);
@ -1762,11 +1704,6 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
group->id_list[group->num_crtcs + group->num_encoders +
group->num_connectors++] = connector->base.id;
list_for_each_entry(bridge, &dev->mode_config.bridge_list, head)
group->id_list[group->num_crtcs + group->num_encoders +
group->num_connectors + group->num_bridges++] =
bridge->base.id;
return 0;
}
EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
@ -5443,7 +5380,6 @@ void drm_mode_config_init(struct drm_device *dev)
INIT_LIST_HEAD(&dev->mode_config.fb_list);
INIT_LIST_HEAD(&dev->mode_config.crtc_list);
INIT_LIST_HEAD(&dev->mode_config.connector_list);
INIT_LIST_HEAD(&dev->mode_config.bridge_list);
INIT_LIST_HEAD(&dev->mode_config.encoder_list);
INIT_LIST_HEAD(&dev->mode_config.property_list);
INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
@ -5483,7 +5419,6 @@ void drm_mode_config_cleanup(struct drm_device *dev)
struct drm_connector *connector, *ot;
struct drm_crtc *crtc, *ct;
struct drm_encoder *encoder, *enct;
struct drm_bridge *bridge, *brt;
struct drm_framebuffer *fb, *fbt;
struct drm_property *property, *pt;
struct drm_property_blob *blob, *bt;
@ -5494,11 +5429,6 @@ void drm_mode_config_cleanup(struct drm_device *dev)
encoder->funcs->destroy(encoder);
}
list_for_each_entry_safe(bridge, brt,
&dev->mode_config.bridge_list, head) {
bridge->funcs->destroy(bridge);
}
list_for_each_entry_safe(connector, ot,
&dev->mode_config.connector_list, head) {
connector->funcs->destroy(connector);

View File

@ -323,8 +323,6 @@ EXPORT_SYMBOL(mipi_dsi_packet_format_is_long);
int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
const struct mipi_dsi_msg *msg)
{
const u8 *tx = msg->tx_buf;
if (!packet || !msg)
return -EINVAL;
@ -353,8 +351,10 @@ int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
packet->header[2] = (msg->tx_len >> 8) & 0xff;
packet->payload_length = msg->tx_len;
packet->payload = tx;
packet->payload = msg->tx_buf;
} else {
const u8 *tx = msg->tx_buf;
packet->header[1] = (msg->tx_len > 0) ? tx[0] : 0;
packet->header[2] = (msg->tx_len > 1) ? tx[1] : 0;
}

View File

@ -18,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_graph.h>
#include <linux/gpio.h>
#include <linux/component.h>
#include <linux/phy/phy.h>
@ -993,32 +994,20 @@ static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = {
.best_encoder = exynos_dp_best_encoder,
};
static bool find_bridge(const char *compat, struct bridge_init *bridge)
{
bridge->client = NULL;
bridge->node = of_find_compatible_node(NULL, NULL, compat);
if (!bridge->node)
return false;
bridge->client = of_find_i2c_device_by_node(bridge->node);
if (!bridge->client)
return false;
return true;
}
/* returns the number of bridges attached */
static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp,
struct drm_encoder *encoder)
{
struct bridge_init bridge;
int ret;
if (find_bridge("nxp,ptn3460", &bridge)) {
ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
if (!ret)
return 1;
encoder->bridge = dp->bridge;
dp->bridge->encoder = encoder;
ret = drm_bridge_attach(encoder->dev, dp->bridge);
if (ret) {
DRM_ERROR("Failed to attach bridge to drm\n");
return ret;
}
return 0;
}
@ -1032,9 +1021,11 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
dp->encoder = encoder;
/* Pre-empt DP connector creation if there's a bridge */
ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder);
if (ret)
return 0;
if (dp->bridge) {
ret = exynos_drm_attach_lcd_bridge(dp, encoder);
if (!ret)
return 0;
}
connector->polled = DRM_CONNECTOR_POLL_HPD;
@ -1241,7 +1232,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
}
}
if (!dp->panel) {
if (!dp->panel && !dp->bridge) {
ret = exynos_dp_dt_parse_panel(dp);
if (ret)
return ret;
@ -1325,7 +1316,7 @@ static const struct component_ops exynos_dp_ops = {
static int exynos_dp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *panel_node;
struct device_node *panel_node, *bridge_node, *endpoint;
struct exynos_dp_device *dp;
int ret;
@ -1351,6 +1342,18 @@ static int exynos_dp_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
}
endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
if (endpoint) {
bridge_node = of_graph_get_remote_port_parent(endpoint);
if (bridge_node) {
dp->bridge = of_drm_find_bridge(bridge_node);
of_node_put(bridge_node);
if (!dp->bridge)
return -EPROBE_DEFER;
} else
return -EPROBE_DEFER;
}
ret = component_add(&pdev->dev, &exynos_dp_ops);
if (ret)
exynos_drm_component_del(&pdev->dev,

View File

@ -153,6 +153,7 @@ struct exynos_dp_device {
struct drm_connector connector;
struct drm_encoder *encoder;
struct drm_panel *panel;
struct drm_bridge *bridge;
struct clk *clock;
unsigned int irq;
void __iomem *reg_base;

View File

@ -247,9 +247,9 @@ int hdmi_modeset_init(struct hdmi *hdmi,
return 0;
fail:
/* bridge/connector are normally destroyed by drm: */
/* bridge is normally destroyed by drm: */
if (hdmi->bridge) {
hdmi->bridge->funcs->destroy(hdmi->bridge);
hdmi_bridge_destroy(hdmi->bridge);
hdmi->bridge = NULL;
}
if (hdmi->connector) {

View File

@ -146,6 +146,7 @@ void hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate);
*/
struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi);
void hdmi_bridge_destroy(struct drm_bridge *bridge);
/*
* hdmi connector:

View File

@ -23,10 +23,9 @@ struct hdmi_bridge {
};
#define to_hdmi_bridge(x) container_of(x, struct hdmi_bridge, base)
static void hdmi_bridge_destroy(struct drm_bridge *bridge)
void hdmi_bridge_destroy(struct drm_bridge *bridge)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
drm_bridge_cleanup(bridge);
kfree(hdmi_bridge);
}
@ -200,7 +199,6 @@ static const struct drm_bridge_funcs hdmi_bridge_funcs = {
.disable = hdmi_bridge_disable,
.post_disable = hdmi_bridge_post_disable,
.mode_set = hdmi_bridge_mode_set,
.destroy = hdmi_bridge_destroy,
};
@ -220,8 +218,9 @@ struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi)
hdmi_bridge->hdmi = hdmi;
bridge = &hdmi_bridge->base;
bridge->funcs = &hdmi_bridge_funcs;
drm_bridge_init(hdmi->dev, bridge, &hdmi_bridge_funcs);
drm_bridge_attach(hdmi->dev, bridge);
return bridge;

View File

@ -19,8 +19,6 @@
#include <video/mipi_display.h>
#include <linux/host1x.h>
struct sharp_panel {
struct drm_panel base;
/* the datasheet refers to them as DSI-LINK1 and DSI-LINK2 */
@ -41,6 +39,16 @@ static inline struct sharp_panel *to_sharp_panel(struct drm_panel *panel)
return container_of(panel, struct sharp_panel, base);
}
static void sharp_wait_frames(struct sharp_panel *sharp, unsigned int frames)
{
unsigned int refresh = drm_mode_vrefresh(sharp->mode);
if (WARN_ON(frames > refresh))
return;
msleep(1000 / (refresh / frames));
}
static int sharp_panel_write(struct sharp_panel *sharp, u16 offset, u8 value)
{
u8 payload[3] = { offset >> 8, offset & 0xff, value };
@ -106,6 +114,8 @@ static int sharp_panel_unprepare(struct drm_panel *panel)
if (!sharp->prepared)
return 0;
sharp_wait_frames(sharp, 4);
err = mipi_dsi_dcs_set_display_off(sharp->link1);
if (err < 0)
dev_err(panel->dev, "failed to set display off: %d\n", err);
@ -170,15 +180,13 @@ static int sharp_panel_prepare(struct drm_panel *panel)
if (err < 0)
return err;
usleep_range(10000, 20000);
err = mipi_dsi_dcs_soft_reset(sharp->link1);
if (err < 0) {
dev_err(panel->dev, "soft reset failed: %d\n", err);
goto poweroff;
}
msleep(120);
/*
* According to the datasheet, the panel needs around 10 ms to fully
* power up. At least another 120 ms is required before exiting sleep
* mode to make sure the panel is ready. Throw in another 20 ms for
* good measure.
*/
msleep(150);
err = mipi_dsi_dcs_exit_sleep_mode(sharp->link1);
if (err < 0) {
@ -238,6 +246,9 @@ static int sharp_panel_prepare(struct drm_panel *panel)
sharp->prepared = true;
/* wait for 6 frames before continuing */
sharp_wait_frames(sharp, 6);
return 0;
poweroff:

View File

@ -448,6 +448,34 @@ static const struct panel_desc auo_b133htn01 = {
},
};
static const struct drm_display_mode avic_tm070ddh03_mode = {
.clock = 51200,
.hdisplay = 1024,
.hsync_start = 1024 + 160,
.hsync_end = 1024 + 160 + 4,
.htotal = 1024 + 160 + 4 + 156,
.vdisplay = 600,
.vsync_start = 600 + 17,
.vsync_end = 600 + 17 + 1,
.vtotal = 600 + 17 + 1 + 17,
.vrefresh = 60,
};
static const struct panel_desc avic_tm070ddh03 = {
.modes = &avic_tm070ddh03_mode,
.num_modes = 1,
.bpc = 8,
.size = {
.width = 154,
.height = 90,
},
.delay = {
.prepare = 20,
.enable = 200,
.disable = 200,
},
};
static const struct drm_display_mode chunghwa_claa101wa01a_mode = {
.clock = 72070,
.hdisplay = 1366,
@ -566,6 +594,29 @@ static const struct panel_desc foxlink_fl500wvr00_a0t = {
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
static const struct drm_display_mode giantplus_gpg482739qs5_mode = {
.clock = 9000,
.hdisplay = 480,
.hsync_start = 480 + 5,
.hsync_end = 480 + 5 + 1,
.htotal = 480 + 5 + 1 + 40,
.vdisplay = 272,
.vsync_start = 272 + 8,
.vsync_end = 272 + 8 + 1,
.vtotal = 272 + 8 + 1 + 8,
.vrefresh = 60,
};
static const struct panel_desc giantplus_gpg482739qs5 = {
.modes = &giantplus_gpg482739qs5_mode,
.num_modes = 1,
.bpc = 8,
.size = {
.width = 95,
.height = 54,
},
};
static const struct drm_display_mode hannstar_hsd070pww1_mode = {
.clock = 71100,
.hdisplay = 1280,
@ -744,6 +795,9 @@ static const struct of_device_id platform_of_match[] = {
}, {
.compatible = "auo,b133xtn01",
.data = &auo_b133xtn01,
}, {
.compatible = "avic,tm070ddh03",
.data = &avic_tm070ddh03,
}, {
.compatible = "chunghwa,claa101wa01a",
.data = &chunghwa_claa101wa01a
@ -762,6 +816,9 @@ static const struct of_device_id platform_of_match[] = {
}, {
.compatible = "foxlink,fl500wvr00-a0t",
.data = &foxlink_fl500wvr00_a0t,
}, {
.compatible = "giantplus,gpg482739qs5",
.data = &giantplus_gpg482739qs5
}, {
.compatible = "hannstar,hsd070pww1",
.data = &hannstar_hsd070pww1,

View File

@ -91,6 +91,7 @@ struct sti_dvo {
struct dvo_config *config;
bool enabled;
struct drm_encoder *encoder;
struct drm_bridge *bridge;
};
struct sti_dvo_connector {
@ -272,19 +273,12 @@ static void sti_dvo_bridge_nope(struct drm_bridge *bridge)
/* do nothing */
}
static void sti_dvo_brigde_destroy(struct drm_bridge *bridge)
{
drm_bridge_cleanup(bridge);
kfree(bridge);
}
static const struct drm_bridge_funcs sti_dvo_bridge_funcs = {
.pre_enable = sti_dvo_pre_enable,
.enable = sti_dvo_bridge_nope,
.disable = sti_dvo_disable,
.post_disable = sti_dvo_bridge_nope,
.mode_set = sti_dvo_set_mode,
.destroy = sti_dvo_brigde_destroy,
};
static int sti_dvo_connector_get_modes(struct drm_connector *connector)
@ -416,8 +410,21 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data)
return -ENOMEM;
bridge->driver_private = dvo;
drm_bridge_init(drm_dev, bridge, &sti_dvo_bridge_funcs);
bridge->funcs = &sti_dvo_bridge_funcs;
bridge->of_node = dvo->dev.of_node;
err = drm_bridge_add(bridge);
if (err) {
DRM_ERROR("Failed to add bridge\n");
return err;
}
err = drm_bridge_attach(drm_dev, bridge);
if (err) {
DRM_ERROR("Failed to attach bridge\n");
return err;
}
dvo->bridge = bridge;
encoder->bridge = bridge;
connector->encoder = encoder;
dvo->encoder = encoder;
@ -446,7 +453,7 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data)
err_sysfs:
drm_connector_unregister(drm_connector);
err_connector:
drm_bridge_cleanup(bridge);
drm_bridge_remove(bridge);
drm_connector_cleanup(drm_connector);
return -EINVAL;
}
@ -454,7 +461,9 @@ err_connector:
static void sti_dvo_unbind(struct device *dev,
struct device *master, void *data)
{
/* do nothing */
struct sti_dvo *dvo = dev_get_drvdata(dev);
drm_bridge_remove(dvo->bridge);
}
static const struct component_ops sti_dvo_ops = {

View File

@ -508,19 +508,12 @@ static void sti_hda_bridge_nope(struct drm_bridge *bridge)
/* do nothing */
}
static void sti_hda_brigde_destroy(struct drm_bridge *bridge)
{
drm_bridge_cleanup(bridge);
kfree(bridge);
}
static const struct drm_bridge_funcs sti_hda_bridge_funcs = {
.pre_enable = sti_hda_pre_enable,
.enable = sti_hda_bridge_nope,
.disable = sti_hda_disable,
.post_disable = sti_hda_bridge_nope,
.mode_set = sti_hda_set_mode,
.destroy = sti_hda_brigde_destroy,
};
static int sti_hda_connector_get_modes(struct drm_connector *connector)
@ -664,7 +657,8 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data)
return -ENOMEM;
bridge->driver_private = hda;
drm_bridge_init(drm_dev, bridge, &sti_hda_bridge_funcs);
bridge->funcs = &sti_hda_bridge_funcs;
drm_bridge_attach(drm_dev, bridge);
encoder->bridge = bridge;
connector->encoder = encoder;
@ -693,7 +687,6 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data)
err_sysfs:
drm_connector_unregister(drm_connector);
err_connector:
drm_bridge_cleanup(bridge);
drm_connector_cleanup(drm_connector);
return -EINVAL;
}

View File

@ -463,19 +463,12 @@ static void sti_hdmi_bridge_nope(struct drm_bridge *bridge)
/* do nothing */
}
static void sti_hdmi_brigde_destroy(struct drm_bridge *bridge)
{
drm_bridge_cleanup(bridge);
kfree(bridge);
}
static const struct drm_bridge_funcs sti_hdmi_bridge_funcs = {
.pre_enable = sti_hdmi_pre_enable,
.enable = sti_hdmi_bridge_nope,
.disable = sti_hdmi_disable,
.post_disable = sti_hdmi_bridge_nope,
.mode_set = sti_hdmi_set_mode,
.destroy = sti_hdmi_brigde_destroy,
};
static int sti_hdmi_connector_get_modes(struct drm_connector *connector)
@ -635,7 +628,8 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
goto err_adapt;
bridge->driver_private = hdmi;
drm_bridge_init(drm_dev, bridge, &sti_hdmi_bridge_funcs);
bridge->funcs = &sti_hdmi_bridge_funcs;
drm_bridge_attach(drm_dev, bridge);
encoder->bridge = bridge;
connector->encoder = encoder;
@ -667,7 +661,6 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
err_sysfs:
drm_connector_unregister(drm_connector);
err_connector:
drm_bridge_cleanup(bridge);
drm_connector_cleanup(drm_connector);
err_adapt:
put_device(&hdmi->ddc_adapt->dev);

View File

@ -15,6 +15,7 @@
#define _DRM_BRIDGE_PTN3460_H_
struct drm_device;
struct drm_bridge;
struct drm_encoder;
struct i2c_client;
struct device_node;
@ -23,6 +24,9 @@ struct device_node;
int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
struct i2c_client *client, struct device_node *node);
void ptn3460_destroy(struct drm_bridge *bridge);
#else
static inline int ptn3460_init(struct drm_device *dev,
@ -32,6 +36,10 @@ static inline int ptn3460_init(struct drm_device *dev,
return 0;
}
static inline void ptn3460_destroy(struct drm_bridge *bridge)
{
}
#endif
#endif

View File

@ -868,15 +868,16 @@ struct drm_plane {
/**
* struct drm_bridge_funcs - drm_bridge control functions
* @attach: Called during drm_bridge_attach
* @mode_fixup: Try to fixup (or reject entirely) proposed mode for this bridge
* @disable: Called right before encoder prepare, disables the bridge
* @post_disable: Called right after encoder prepare, for lockstepped disable
* @mode_set: Set this mode to the bridge
* @pre_enable: Called right before encoder commit, for lockstepped commit
* @enable: Called right after encoder commit, enables the bridge
* @destroy: make object go away
*/
struct drm_bridge_funcs {
int (*attach)(struct drm_bridge *bridge);
bool (*mode_fixup)(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
@ -887,22 +888,24 @@ struct drm_bridge_funcs {
struct drm_display_mode *adjusted_mode);
void (*pre_enable)(struct drm_bridge *bridge);
void (*enable)(struct drm_bridge *bridge);
void (*destroy)(struct drm_bridge *bridge);
};
/**
* struct drm_bridge - central DRM bridge control structure
* @dev: DRM device this bridge belongs to
* @head: list management
* @of_node: device node pointer to the bridge
* @list: to keep track of all added bridges
* @base: base mode object
* @funcs: control functions
* @driver_private: pointer to the bridge driver's internal context
*/
struct drm_bridge {
struct drm_device *dev;
struct list_head head;
struct drm_mode_object base;
struct drm_encoder *encoder;
#ifdef CONFIG_OF
struct device_node *of_node;
#endif
struct list_head list;
const struct drm_bridge_funcs *funcs;
void *driver_private;
@ -1007,7 +1010,6 @@ struct drm_mode_group {
uint32_t num_crtcs;
uint32_t num_encoders;
uint32_t num_connectors;
uint32_t num_bridges;
/* list of object IDs for this group */
uint32_t *id_list;
@ -1026,8 +1028,6 @@ struct drm_mode_group {
* @fb_list: list of framebuffers available
* @num_connector: number of connectors on this device
* @connector_list: list of connector objects
* @num_bridge: number of bridges on this device
* @bridge_list: list of bridge objects
* @num_encoder: number of encoders on this device
* @encoder_list: list of encoder objects
* @num_overlay_plane: number of overlay planes on this device
@ -1072,8 +1072,6 @@ struct drm_mode_config {
int num_connector;
struct list_head connector_list;
int num_bridge;
struct list_head bridge_list;
int num_encoder;
struct list_head encoder_list;
@ -1222,9 +1220,10 @@ extern unsigned int drm_connector_index(struct drm_connector *connector);
/* helper to unplug all connectors from sysfs for device */
extern void drm_connector_unplug_all(struct drm_device *dev);
extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge,
const struct drm_bridge_funcs *funcs);
extern void drm_bridge_cleanup(struct drm_bridge *bridge);
extern int drm_bridge_add(struct drm_bridge *bridge);
extern void drm_bridge_remove(struct drm_bridge *bridge);
extern struct drm_bridge *of_drm_find_bridge(struct device_node *np);
extern int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge);
extern int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,