diff --git a/Documentation/devicetree/bindings/panel/avic,tm070ddh03.txt b/Documentation/devicetree/bindings/panel/avic,tm070ddh03.txt new file mode 100644 index 000000000000..b6f2f3e8f44e --- /dev/null +++ b/Documentation/devicetree/bindings/panel/avic,tm070ddh03.txt @@ -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. diff --git a/Documentation/devicetree/bindings/panel/giantplus,gpg482739qs5.txt b/Documentation/devicetree/bindings/panel/giantplus,gpg482739qs5.txt new file mode 100644 index 000000000000..24b0b624434b --- /dev/null +++ b/Documentation/devicetree/bindings/panel/giantplus,gpg482739qs5.txt @@ -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. diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index b1df0ad1306c..c0333a97c47a 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -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 diff --git a/Documentation/devicetree/bindings/video/bridge/ps8622.txt b/Documentation/devicetree/bindings/video/bridge/ps8622.txt new file mode 100644 index 000000000000..c989c3807f2b --- /dev/null +++ b/Documentation/devicetree/bindings/video/bridge/ps8622.txt @@ -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>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt b/Documentation/devicetree/bindings/video/bridge/ptn3460.txt similarity index 65% rename from Documentation/devicetree/bindings/drm/bridge/ptn3460.txt rename to Documentation/devicetree/bindings/video/bridge/ptn3460.txt index 52b93b2c6748..361971ba104d 100644 --- a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt +++ b/Documentation/devicetree/bindings/video/bridge/ptn3460.txt @@ -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>; + }; + }; + }; }; diff --git a/Documentation/devicetree/bindings/video/exynos_dp.txt b/Documentation/devicetree/bindings/video/exynos_dp.txt index 53dbccfa80ca..7a3a9cdb86ab 100644 --- a/Documentation/devicetree/bindings/video/exynos_dp.txt +++ b/Documentation/devicetree/bindings/video/exynos_dp.txt @@ -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>; + }; + }; + }; }; diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index cf0eed8208b5..2c239b99de64 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -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 diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index b70f3c8d4e8a..f38bbcdf929b 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -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. diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index 6ea000504173..cd6a70647e32 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -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 diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index d466696ed5e8..826833e396f0 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -13,20 +13,23 @@ * GNU General Public License for more details. */ +#include +#include +#include #include #include #include -#include -#include -#include +#include -#include "drmP.h" -#include "drm_edid.h" -#include "drm_crtc.h" -#include "drm_crtc_helper.h" +#include #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 "); +MODULE_DESCRIPTION("NXP ptn3460 eDP-LVDS converter driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c new file mode 100644 index 000000000000..d1187e571c6d --- /dev/null +++ b/drivers/gpu/drm/drm_bridge.c @@ -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 +#include + +#include + +#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 "); +MODULE_DESCRIPTION("DRM bridge infrastructure"); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index b459888f6310..6b00173d1be4 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -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); diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index c0644bb865f2..2d5ca8eec13a 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -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; } diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 34d46aa75416..46f149737bc8 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -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, diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h index 164f171168e7..a4e799679669 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.h +++ b/drivers/gpu/drm/exynos/exynos_dp_core.h @@ -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; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 062c68725376..95f7b8d0f3ef 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -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) { diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 43e654f751b7..4d4cad42a776 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -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: diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 6902ad6da710..d6f8d5818e18 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -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; diff --git a/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c index 9d81759d82fc..3cce3ca19601 100644 --- a/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c +++ b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c @@ -19,8 +19,6 @@ #include