Merge tag 'drm-misc-next-2017-06-15' of git://anongit.freedesktop.org/git/drm-misc into drm-next
Cross-subsystem Changes: - dt-bindings: add vendor prefix for NLT Technologies, Ltd. (Lucas) - dt-bindings: Add support for samsung s6e3hf2 panel (Hoegeun) Core Changes: - Add drm_panel_bridge to avoid connector boilerplate in drivers (Eric) - Trival fixes for dupe forward decl and reduce scope of variable (Dawid) Driver Changes: - dw-hdmi: Use mode_valid hook on bridge instead of connector (Jose) - vc4,atmel-hlcdc: Use drm_panel_bridge where appropriate (Eric) - panel: Add Innolux P079ZCA panel driver (Chris) - panel-simple: Add NL12880B20-05, NL192108AC18-02D, P320HVN03 panels (Lucas) - panel-samsung-s6e3ha2: Add s6e3hf2 panel support (Hoegeun) - zte,vc4,pl111,panel,mxsfb: Miscellaneous fixes Cc: Jose Abreu <Jose.Abreu@synopsys.com> Cc: Eric Anholt <eric@anholt.net> Cc: Chris Zhong <zyw@rock-chips.com> Cc: Lucas Stach <l.stach@pengutronix.de> Cc: Hoegeun Kwon <hoegeun.kwon@samsung.com> Cc: Dawid Kurek <dawikur@gmail.com> * tag 'drm-misc-next-2017-06-15' of git://anongit.freedesktop.org/git/drm-misc: (26 commits) drm: Reduce scope of 'state' variable drm: mxsfb_crtc: Reset the eLCDIF controller drm: Remove duplicate forward declaration drm/panel: s6e3ha2: Add support for s6e3hf2 panel on TM2e board dt-bindings: Add support for samsung s6e3hf2 panel drm/panel: add backlight dependency for sitronix-st7789v drm/panel: S6E3HA2 needs backlight code drm/panel: simple: add support for AUO P320HVN03 drm/panel: simple: add support for NLT NL192108AC18-02D dt-bindings: add vendor prefix for NLT Technologies, Ltd. drm/panel: simple: add support for NEC NL12880B20-05 drm/panel: add Innolux P079ZCA panel driver dt-bindings: Add INNOLUX P079ZCA panel bindings drm/vc4: Fix resource leak in 'vc4_get_hang_state_ioctl()' in error handling path drm/vc4/vc4_bo.c: always set bo->resv drm: Add const to name field declaration in struct drm_prop_enum_list drm/pl111: Fix offset calculation for the primary plane. drm/atmel-hlcdc: Fix panel registration drm/bridge: Build the panel wrapper in drm_kms_helper drm/atmel-hlcdc: Replace the panel usage with drm_panel_bridge. ...
This commit is contained in:
commit
bfda9aa153
@ -0,0 +1,8 @@
|
||||
AU Optronics Corporation 31.5" FHD (1920x1080) TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "auo,p320hvn03"
|
||||
- power-supply: as specified in the base binding
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,23 @@
|
||||
Innolux P079ZCA 7.85" 768x1024 TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "innolux,p079zca"
|
||||
- reg: DSI virtual channel of the peripheral
|
||||
- power-supply: phandle of the regulator that provides the supply voltage
|
||||
- enable-gpios: panel enable gpio
|
||||
|
||||
Optional properties:
|
||||
- backlight: phandle of the backlight device attached to the panel
|
||||
|
||||
Example:
|
||||
|
||||
&mipi_dsi {
|
||||
panel {
|
||||
compatible = "innolux,p079zca";
|
||||
reg = <0>;
|
||||
power-supply = <...>;
|
||||
backlight = <&backlight>;
|
||||
enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
|
||||
status = "okay";
|
||||
};
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
NEC LCD Technologies, Ltd. 12.1" WXGA (1280x800) LVDS TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "nec,nl12880bc20-05"
|
||||
- power-supply: as specified in the base binding
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,8 @@
|
||||
NLT Technologies, Ltd. 15.6" FHD (1920x1080) LVDS TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "nlt,nl192108ac18-02d"
|
||||
- power-supply: as specified in the base binding
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -1,7 +1,10 @@
|
||||
Samsung S6E3HA2 5.7" 1440x2560 AMOLED panel
|
||||
Samsung S6E3HF2 5.65" 1600x2560 AMOLED panel
|
||||
|
||||
Required properties:
|
||||
- compatible: "samsung,s6e3ha2"
|
||||
- compatible: should be one of:
|
||||
"samsung,s6e3ha2",
|
||||
"samsung,s6e3hf2".
|
||||
- reg: the virtual channel number of a DSI peripheral
|
||||
- vdd3-supply: I/O voltage supply
|
||||
- vci-supply: voltage supply for analog circuits
|
||||
|
@ -219,6 +219,7 @@ nexbox Nexbox
|
||||
newhaven Newhaven Display International
|
||||
ni National Instruments
|
||||
nintendo Nintendo
|
||||
nlt NLT Technologies, Ltd.
|
||||
nokia Nokia
|
||||
nordic Nordic Semiconductor
|
||||
nuvoton Nuvoton Technology Corporation
|
||||
|
@ -143,6 +143,12 @@ Bridge Helper Reference
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
|
||||
:export:
|
||||
|
||||
Panel-Bridge Helper Reference
|
||||
-----------------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/bridge/panel.c
|
||||
:export:
|
||||
|
||||
.. _drm_panel_helper:
|
||||
|
||||
Panel Helper Reference
|
||||
|
@ -35,6 +35,7 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
|
||||
drm_simple_kms_helper.o drm_modeset_helper.o \
|
||||
drm_scdc_helper.o
|
||||
|
||||
drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
|
||||
drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
|
||||
drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
|
||||
drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
|
||||
|
@ -23,139 +23,17 @@
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
|
||||
#include "atmel_hlcdc_dc.h"
|
||||
|
||||
/**
|
||||
* Atmel HLCDC RGB connector structure
|
||||
*
|
||||
* This structure stores RGB slave device information.
|
||||
*
|
||||
* @connector: DRM connector
|
||||
* @encoder: DRM encoder
|
||||
* @dc: pointer to the atmel_hlcdc_dc structure
|
||||
* @panel: panel connected on the RGB output
|
||||
*/
|
||||
struct atmel_hlcdc_rgb_output {
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder encoder;
|
||||
struct atmel_hlcdc_dc *dc;
|
||||
struct drm_panel *panel;
|
||||
};
|
||||
|
||||
static inline struct atmel_hlcdc_rgb_output *
|
||||
drm_connector_to_atmel_hlcdc_rgb_output(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct atmel_hlcdc_rgb_output,
|
||||
connector);
|
||||
}
|
||||
|
||||
static inline struct atmel_hlcdc_rgb_output *
|
||||
drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder)
|
||||
{
|
||||
return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_rgb_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct atmel_hlcdc_rgb_output *rgb =
|
||||
drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
|
||||
|
||||
if (rgb->panel) {
|
||||
drm_panel_prepare(rgb->panel);
|
||||
drm_panel_enable(rgb->panel);
|
||||
}
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_rgb_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct atmel_hlcdc_rgb_output *rgb =
|
||||
drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
|
||||
|
||||
if (rgb->panel) {
|
||||
drm_panel_disable(rgb->panel);
|
||||
drm_panel_unprepare(rgb->panel);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = {
|
||||
.disable = atmel_hlcdc_rgb_encoder_disable,
|
||||
.enable = atmel_hlcdc_rgb_encoder_enable,
|
||||
};
|
||||
|
||||
static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
drm_encoder_cleanup(encoder);
|
||||
memset(encoder, 0, sizeof(*encoder));
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = {
|
||||
.destroy = atmel_hlcdc_rgb_encoder_destroy,
|
||||
};
|
||||
|
||||
static int atmel_hlcdc_panel_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct atmel_hlcdc_rgb_output *rgb =
|
||||
drm_connector_to_atmel_hlcdc_rgb_output(connector);
|
||||
|
||||
if (rgb->panel)
|
||||
return rgb->panel->funcs->get_modes(rgb->panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct atmel_hlcdc_rgb_output *rgb =
|
||||
drm_connector_to_atmel_hlcdc_rgb_output(connector);
|
||||
|
||||
return atmel_hlcdc_dc_mode_valid(rgb->dc, mode);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helper_funcs = {
|
||||
.get_modes = atmel_hlcdc_panel_get_modes,
|
||||
.mode_valid = atmel_hlcdc_rgb_mode_valid,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
atmel_hlcdc_panel_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct atmel_hlcdc_rgb_output *rgb =
|
||||
drm_connector_to_atmel_hlcdc_rgb_output(connector);
|
||||
|
||||
if (rgb->panel)
|
||||
return connector_status_connected;
|
||||
|
||||
return connector_status_disconnected;
|
||||
}
|
||||
|
||||
static void
|
||||
atmel_hlcdc_panel_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct atmel_hlcdc_rgb_output *rgb =
|
||||
drm_connector_to_atmel_hlcdc_rgb_output(connector);
|
||||
|
||||
if (rgb->panel)
|
||||
drm_panel_detach(rgb->panel);
|
||||
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs atmel_hlcdc_panel_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.detect = atmel_hlcdc_panel_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = atmel_hlcdc_panel_connector_destroy,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
|
||||
{
|
||||
struct atmel_hlcdc_dc *dc = dev->dev_private;
|
||||
struct atmel_hlcdc_rgb_output *output;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
int ret;
|
||||
@ -165,55 +43,34 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL);
|
||||
if (!output)
|
||||
encoder = devm_kzalloc(dev->dev, sizeof(*encoder), GFP_KERNEL);
|
||||
if (!encoder)
|
||||
return -EINVAL;
|
||||
|
||||
output->dc = dc;
|
||||
|
||||
drm_encoder_helper_add(&output->encoder,
|
||||
&atmel_hlcdc_panel_encoder_helper_funcs);
|
||||
ret = drm_encoder_init(dev, &output->encoder,
|
||||
ret = drm_encoder_init(dev, encoder,
|
||||
&atmel_hlcdc_panel_encoder_funcs,
|
||||
DRM_MODE_ENCODER_NONE, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
output->encoder.possible_crtcs = 0x1;
|
||||
encoder->possible_crtcs = 0x1;
|
||||
|
||||
if (panel) {
|
||||
output->connector.dpms = DRM_MODE_DPMS_OFF;
|
||||
output->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
|
||||
drm_connector_helper_add(&output->connector,
|
||||
&atmel_hlcdc_panel_connector_helper_funcs);
|
||||
ret = drm_connector_init(dev, &output->connector,
|
||||
&atmel_hlcdc_panel_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_Unknown);
|
||||
if (ret)
|
||||
goto err_encoder_cleanup;
|
||||
|
||||
drm_mode_connector_attach_encoder(&output->connector,
|
||||
&output->encoder);
|
||||
|
||||
ret = drm_panel_attach(panel, &output->connector);
|
||||
if (ret) {
|
||||
drm_connector_cleanup(&output->connector);
|
||||
goto err_encoder_cleanup;
|
||||
}
|
||||
|
||||
output->panel = panel;
|
||||
|
||||
return 0;
|
||||
bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown);
|
||||
if (IS_ERR(bridge))
|
||||
return PTR_ERR(bridge);
|
||||
}
|
||||
|
||||
if (bridge) {
|
||||
ret = drm_bridge_attach(&output->encoder, bridge, NULL);
|
||||
ret = drm_bridge_attach(encoder, bridge, NULL);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
if (panel)
|
||||
drm_panel_bridge_remove(bridge);
|
||||
}
|
||||
|
||||
err_encoder_cleanup:
|
||||
drm_encoder_cleanup(&output->encoder);
|
||||
drm_encoder_cleanup(encoder);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -4,6 +4,14 @@ config DRM_BRIDGE
|
||||
help
|
||||
Bridge registration and lookup framework.
|
||||
|
||||
config DRM_PANEL_BRIDGE
|
||||
def_bool y
|
||||
depends on DRM_BRIDGE
|
||||
depends on DRM_KMS_HELPER
|
||||
select DRM_PANEL
|
||||
help
|
||||
DRM bridge wrapper of DRM panels
|
||||
|
||||
menu "Display Interface Bridges"
|
||||
depends on DRM && DRM_BRIDGE
|
||||
|
||||
@ -27,8 +35,7 @@ config DRM_DUMB_VGA_DAC
|
||||
config DRM_LVDS_ENCODER
|
||||
tristate "Transparent parallel to LVDS encoder support"
|
||||
depends on OF
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_PANEL
|
||||
select DRM_PANEL_BRIDGE
|
||||
help
|
||||
Support for transparent parallel to LVDS encoders that don't require
|
||||
any configuration.
|
||||
|
@ -8,144 +8,18 @@
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_modeset_helper_vtables.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
struct lvds_encoder {
|
||||
struct device *dev;
|
||||
|
||||
struct drm_bridge bridge;
|
||||
struct drm_connector connector;
|
||||
struct drm_panel *panel;
|
||||
};
|
||||
|
||||
static inline struct lvds_encoder *
|
||||
drm_bridge_to_lvds_encoder(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct lvds_encoder, bridge);
|
||||
}
|
||||
|
||||
static inline struct lvds_encoder *
|
||||
drm_connector_to_lvds_encoder(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct lvds_encoder, connector);
|
||||
}
|
||||
|
||||
static int lvds_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct lvds_encoder *lvds = drm_connector_to_lvds_encoder(connector);
|
||||
|
||||
return drm_panel_get_modes(lvds->panel);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs lvds_connector_helper_funcs = {
|
||||
.get_modes = lvds_connector_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs lvds_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static int lvds_encoder_attach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
|
||||
struct drm_connector *connector = &lvds->connector;
|
||||
int ret;
|
||||
|
||||
if (!bridge->encoder) {
|
||||
DRM_ERROR("Missing encoder\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
drm_connector_helper_add(connector, &lvds_connector_helper_funcs);
|
||||
|
||||
ret = drm_connector_init(bridge->dev, connector, &lvds_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to initialize connector\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_mode_connector_attach_encoder(&lvds->connector, bridge->encoder);
|
||||
|
||||
ret = drm_panel_attach(lvds->panel, &lvds->connector);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lvds_encoder_detach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
|
||||
|
||||
drm_panel_detach(lvds->panel);
|
||||
}
|
||||
|
||||
static void lvds_encoder_pre_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
|
||||
|
||||
drm_panel_prepare(lvds->panel);
|
||||
}
|
||||
|
||||
static void lvds_encoder_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
|
||||
|
||||
drm_panel_enable(lvds->panel);
|
||||
}
|
||||
|
||||
static void lvds_encoder_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
|
||||
|
||||
drm_panel_disable(lvds->panel);
|
||||
}
|
||||
|
||||
static void lvds_encoder_post_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
|
||||
|
||||
drm_panel_unprepare(lvds->panel);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs lvds_encoder_bridge_funcs = {
|
||||
.attach = lvds_encoder_attach,
|
||||
.detach = lvds_encoder_detach,
|
||||
.pre_enable = lvds_encoder_pre_enable,
|
||||
.enable = lvds_encoder_enable,
|
||||
.disable = lvds_encoder_disable,
|
||||
.post_disable = lvds_encoder_post_disable,
|
||||
};
|
||||
|
||||
static int lvds_encoder_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lvds_encoder *lvds;
|
||||
struct device_node *port;
|
||||
struct device_node *endpoint;
|
||||
struct device_node *panel;
|
||||
|
||||
lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
|
||||
if (!lvds)
|
||||
return -ENOMEM;
|
||||
|
||||
lvds->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, lvds);
|
||||
|
||||
lvds->bridge.funcs = &lvds_encoder_bridge_funcs;
|
||||
lvds->bridge.of_node = pdev->dev.of_node;
|
||||
struct device_node *panel_node;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
/* Locate the panel DT node. */
|
||||
port = of_graph_get_port_by_id(pdev->dev.of_node, 1);
|
||||
@ -161,29 +35,34 @@ static int lvds_encoder_probe(struct platform_device *pdev)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
panel = of_graph_get_remote_port_parent(endpoint);
|
||||
panel_node = of_graph_get_remote_port_parent(endpoint);
|
||||
of_node_put(endpoint);
|
||||
if (!panel) {
|
||||
if (!panel_node) {
|
||||
dev_dbg(&pdev->dev, "no remote endpoint for port 1\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
lvds->panel = of_drm_find_panel(panel);
|
||||
of_node_put(panel);
|
||||
if (!lvds->panel) {
|
||||
panel = of_drm_find_panel(panel_node);
|
||||
of_node_put(panel_node);
|
||||
if (!panel) {
|
||||
dev_dbg(&pdev->dev, "panel not found, deferring probe\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
/* Register the bridge. */
|
||||
return drm_bridge_add(&lvds->bridge);
|
||||
bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_LVDS);
|
||||
if (IS_ERR(bridge))
|
||||
return PTR_ERR(bridge);
|
||||
|
||||
platform_set_drvdata(pdev, bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lvds_encoder_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct lvds_encoder *encoder = platform_get_drvdata(pdev);
|
||||
struct drm_bridge *bridge = platform_get_drvdata(pdev);
|
||||
|
||||
drm_bridge_remove(&encoder->bridge);
|
||||
drm_bridge_remove(bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
200
drivers/gpu/drm/bridge/panel.c
Normal file
200
drivers/gpu/drm/bridge/panel.c
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
* Copyright (C) 2017 Broadcom
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_modeset_helper_vtables.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
struct panel_bridge {
|
||||
struct drm_bridge bridge;
|
||||
struct drm_connector connector;
|
||||
struct drm_panel *panel;
|
||||
u32 connector_type;
|
||||
};
|
||||
|
||||
static inline struct panel_bridge *
|
||||
drm_bridge_to_panel_bridge(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct panel_bridge, bridge);
|
||||
}
|
||||
|
||||
static inline struct panel_bridge *
|
||||
drm_connector_to_panel_bridge(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct panel_bridge, connector);
|
||||
}
|
||||
|
||||
static int panel_bridge_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct panel_bridge *panel_bridge =
|
||||
drm_connector_to_panel_bridge(connector);
|
||||
|
||||
return drm_panel_get_modes(panel_bridge->panel);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs
|
||||
panel_bridge_connector_helper_funcs = {
|
||||
.get_modes = panel_bridge_connector_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs panel_bridge_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static int panel_bridge_attach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
|
||||
struct drm_connector *connector = &panel_bridge->connector;
|
||||
int ret;
|
||||
|
||||
if (!bridge->encoder) {
|
||||
DRM_ERROR("Missing encoder\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
drm_connector_helper_add(connector,
|
||||
&panel_bridge_connector_helper_funcs);
|
||||
|
||||
ret = drm_connector_init(bridge->dev, connector,
|
||||
&panel_bridge_connector_funcs,
|
||||
panel_bridge->connector_type);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to initialize connector\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_mode_connector_attach_encoder(&panel_bridge->connector,
|
||||
bridge->encoder);
|
||||
|
||||
ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void panel_bridge_detach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
|
||||
|
||||
drm_panel_detach(panel_bridge->panel);
|
||||
}
|
||||
|
||||
static void panel_bridge_pre_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
|
||||
|
||||
drm_panel_prepare(panel_bridge->panel);
|
||||
}
|
||||
|
||||
static void panel_bridge_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
|
||||
|
||||
drm_panel_enable(panel_bridge->panel);
|
||||
}
|
||||
|
||||
static void panel_bridge_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
|
||||
|
||||
drm_panel_disable(panel_bridge->panel);
|
||||
}
|
||||
|
||||
static void panel_bridge_post_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
|
||||
|
||||
drm_panel_unprepare(panel_bridge->panel);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
|
||||
.attach = panel_bridge_attach,
|
||||
.detach = panel_bridge_detach,
|
||||
.pre_enable = panel_bridge_pre_enable,
|
||||
.enable = panel_bridge_enable,
|
||||
.disable = panel_bridge_disable,
|
||||
.post_disable = panel_bridge_post_disable,
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_panel_bridge_add - Creates a drm_bridge and drm_connector that
|
||||
* just calls the appropriate functions from drm_panel.
|
||||
*
|
||||
* @panel: The drm_panel being wrapped. Must be non-NULL.
|
||||
* @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be
|
||||
* created.
|
||||
*
|
||||
* For drivers converting from directly using drm_panel: The expected
|
||||
* usage pattern is that during either encoder module probe or DSI
|
||||
* host attach, a drm_panel will be looked up through
|
||||
* drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to
|
||||
* wrap that panel in the new bridge, and the result can then be
|
||||
* passed to drm_bridge_attach(). The drm_panel_prepare() and related
|
||||
* functions can be dropped from the encoder driver (they're now
|
||||
* called by the KMS helpers before calling into the encoder), along
|
||||
* with connector creation. When done with the bridge,
|
||||
* drm_bridge_detach() should be called as normal, then
|
||||
* drm_panel_bridge_remove() to free it.
|
||||
*/
|
||||
struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
|
||||
u32 connector_type)
|
||||
{
|
||||
struct panel_bridge *panel_bridge;
|
||||
int ret;
|
||||
|
||||
if (!panel)
|
||||
return ERR_PTR(EINVAL);
|
||||
|
||||
panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
|
||||
GFP_KERNEL);
|
||||
if (!panel_bridge)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
panel_bridge->connector_type = connector_type;
|
||||
panel_bridge->panel = panel;
|
||||
|
||||
panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs;
|
||||
#ifdef CONFIG_OF
|
||||
panel_bridge->bridge.of_node = panel->dev->of_node;
|
||||
#endif
|
||||
|
||||
ret = drm_bridge_add(&panel_bridge->bridge);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return &panel_bridge->bridge;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_panel_bridge_add);
|
||||
|
||||
/**
|
||||
* drm_panel_bridge_remove - Unregisters and frees a drm_bridge
|
||||
* created by drm_panel_bridge_add().
|
||||
*
|
||||
* @bridge: The drm_bridge being freed.
|
||||
*/
|
||||
void drm_panel_bridge_remove(struct drm_bridge *bridge)
|
||||
{
|
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
|
||||
|
||||
drm_bridge_remove(bridge);
|
||||
devm_kfree(panel_bridge->panel->dev, bridge);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_panel_bridge_remove);
|
@ -1907,24 +1907,6 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
dw_hdmi_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct dw_hdmi *hdmi = container_of(connector,
|
||||
struct dw_hdmi, connector);
|
||||
enum drm_mode_status mode_status = MODE_OK;
|
||||
|
||||
/* We don't support double-clocked modes */
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
||||
return MODE_BAD;
|
||||
|
||||
if (hdmi->plat_data->mode_valid)
|
||||
mode_status = hdmi->plat_data->mode_valid(connector, mode);
|
||||
|
||||
return mode_status;
|
||||
}
|
||||
|
||||
static void dw_hdmi_connector_force(struct drm_connector *connector)
|
||||
{
|
||||
struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
|
||||
@ -1950,7 +1932,6 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
|
||||
|
||||
static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
|
||||
.get_modes = dw_hdmi_connector_get_modes,
|
||||
.mode_valid = dw_hdmi_connector_mode_valid,
|
||||
.best_encoder = drm_atomic_helper_best_encoder,
|
||||
};
|
||||
|
||||
@ -1973,18 +1954,22 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
|
||||
const struct drm_display_mode *orig_mode,
|
||||
struct drm_display_mode *mode)
|
||||
static enum drm_mode_status
|
||||
dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct dw_hdmi *hdmi = bridge->driver_private;
|
||||
struct drm_connector *connector = &hdmi->connector;
|
||||
enum drm_mode_status status;
|
||||
enum drm_mode_status mode_status = MODE_OK;
|
||||
|
||||
status = dw_hdmi_connector_mode_valid(connector, mode);
|
||||
if (status != MODE_OK)
|
||||
return false;
|
||||
return true;
|
||||
/* We don't support double-clocked modes */
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
||||
return MODE_BAD;
|
||||
|
||||
if (hdmi->plat_data->mode_valid)
|
||||
mode_status = hdmi->plat_data->mode_valid(connector, mode);
|
||||
|
||||
return mode_status;
|
||||
}
|
||||
|
||||
static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
|
||||
@ -2028,7 +2013,7 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
|
||||
.enable = dw_hdmi_bridge_enable,
|
||||
.disable = dw_hdmi_bridge_disable,
|
||||
.mode_set = dw_hdmi_bridge_mode_set,
|
||||
.mode_fixup = dw_hdmi_bridge_mode_fixup,
|
||||
.mode_valid = dw_hdmi_bridge_mode_valid,
|
||||
};
|
||||
|
||||
static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)
|
||||
|
@ -109,9 +109,10 @@ struct drm_atomic_state *
|
||||
drm_atomic_state_alloc(struct drm_device *dev)
|
||||
{
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
struct drm_atomic_state *state;
|
||||
|
||||
if (!config->funcs->atomic_state_alloc) {
|
||||
struct drm_atomic_state *state;
|
||||
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (!state)
|
||||
return NULL;
|
||||
|
@ -147,8 +147,9 @@ static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con,
|
||||
struct drm_display_mode *mode)
|
||||
static enum drm_mode_status
|
||||
imx6q_hdmi_mode_valid(struct drm_connector *con,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->clock < 13500)
|
||||
return MODE_CLOCK_LOW;
|
||||
@ -159,8 +160,9 @@ static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con,
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static enum drm_mode_status imx6dl_hdmi_mode_valid(struct drm_connector *con,
|
||||
struct drm_display_mode *mode)
|
||||
static enum drm_mode_status
|
||||
imx6dl_hdmi_mode_valid(struct drm_connector *con,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->clock < 13500)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
@ -536,8 +536,9 @@ static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id)
|
||||
}
|
||||
|
||||
/* TOFIX Enable support for non-vic modes */
|
||||
static enum drm_mode_status dw_hdmi_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
static enum drm_mode_status
|
||||
dw_hdmi_mode_valid(struct drm_connector *connector,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
unsigned int vclk_freq;
|
||||
unsigned int venc_freq;
|
||||
|
@ -35,6 +35,13 @@
|
||||
#include "mxsfb_drv.h"
|
||||
#include "mxsfb_regs.h"
|
||||
|
||||
#define MXS_SET_ADDR 0x4
|
||||
#define MXS_CLR_ADDR 0x8
|
||||
#define MODULE_CLKGATE BIT(30)
|
||||
#define MODULE_SFTRST BIT(31)
|
||||
/* 1 second delay should be plenty of time for block reset */
|
||||
#define RESET_TIMEOUT 1000000
|
||||
|
||||
static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
|
||||
{
|
||||
return (val & mxsfb->devdata->hs_wdth_mask) <<
|
||||
@ -159,6 +166,36 @@ static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
|
||||
clk_disable_unprepare(mxsfb->clk_disp_axi);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the bit and poll it cleared. This is usually called with
|
||||
* a reset address and mask being either SFTRST(bit 31) or CLKGATE
|
||||
* (bit 30).
|
||||
*/
|
||||
static int clear_poll_bit(void __iomem *addr, u32 mask)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
writel(mask, addr + MXS_CLR_ADDR);
|
||||
return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int mxsfb_reset_block(void __iomem *reset_addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR);
|
||||
|
||||
ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return clear_poll_bit(reset_addr, MODULE_CLKGATE);
|
||||
}
|
||||
|
||||
static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode;
|
||||
@ -173,6 +210,11 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
|
||||
*/
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
|
||||
/* Mandatory eLCDIF reset as per the Reference Manual */
|
||||
err = mxsfb_reset_block(mxsfb->base);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
/* Clear the FIFOs */
|
||||
writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
|
||||
|
||||
|
@ -28,6 +28,17 @@ config DRM_PANEL_SIMPLE
|
||||
that it can be automatically turned off when the panel goes into a
|
||||
low power state.
|
||||
|
||||
config DRM_PANEL_INNOLUX_P079ZCA
|
||||
tristate "Innolux P079ZCA panel"
|
||||
depends on OF
|
||||
depends on DRM_MIPI_DSI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
Say Y here if you want to enable support for Innolux P079ZCA
|
||||
TFT-LCD modules. The panel has a 1024x768 resolution and uses
|
||||
24 bit RGB per pixel. It provides a MIPI DSI interface to
|
||||
the host and has a built-in LED backlight.
|
||||
|
||||
config DRM_PANEL_JDI_LT070ME05000
|
||||
tristate "JDI LT070ME05000 WUXGA DSI panel"
|
||||
depends on OF
|
||||
@ -66,6 +77,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA2
|
||||
tristate "Samsung S6E3HA2 DSI video mode panel"
|
||||
depends on OF
|
||||
depends on DRM_MIPI_DSI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
select VIDEOMODE_HELPERS
|
||||
|
||||
config DRM_PANEL_SAMSUNG_S6E8AA0
|
||||
@ -100,6 +112,7 @@ config DRM_PANEL_SHARP_LS043T1LE01
|
||||
config DRM_PANEL_SITRONIX_ST7789V
|
||||
tristate "Sitronix ST7789V panel"
|
||||
depends on OF && SPI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
Say Y here if you want to enable support for the Sitronix
|
||||
ST7789V controller for 240x320 LCD panels
|
||||
|
@ -1,5 +1,6 @@
|
||||
obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
|
||||
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
|
||||
obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
|
||||
obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
|
||||
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
|
||||
obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
|
||||
|
340
drivers/gpu/drm/panel/panel-innolux-p079zca.c
Normal file
340
drivers/gpu/drm/panel/panel-innolux-p079zca.c
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
struct innolux_panel {
|
||||
struct drm_panel base;
|
||||
struct mipi_dsi_device *link;
|
||||
|
||||
struct backlight_device *backlight;
|
||||
struct regulator *supply;
|
||||
struct gpio_desc *enable_gpio;
|
||||
|
||||
bool prepared;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct innolux_panel, base);
|
||||
}
|
||||
|
||||
static int innolux_panel_disable(struct drm_panel *panel)
|
||||
{
|
||||
struct innolux_panel *innolux = to_innolux_panel(panel);
|
||||
int err;
|
||||
|
||||
if (!innolux->enabled)
|
||||
return 0;
|
||||
|
||||
innolux->backlight->props.power = FB_BLANK_POWERDOWN;
|
||||
backlight_update_status(innolux->backlight);
|
||||
|
||||
err = mipi_dsi_dcs_set_display_off(innolux->link);
|
||||
if (err < 0)
|
||||
DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
|
||||
err);
|
||||
|
||||
innolux->enabled = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int innolux_panel_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct innolux_panel *innolux = to_innolux_panel(panel);
|
||||
int err;
|
||||
|
||||
if (!innolux->prepared)
|
||||
return 0;
|
||||
|
||||
err = mipi_dsi_dcs_enter_sleep_mode(innolux->link);
|
||||
if (err < 0) {
|
||||
DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
gpiod_set_value_cansleep(innolux->enable_gpio, 0);
|
||||
|
||||
/* T8: 80ms - 1000ms */
|
||||
msleep(80);
|
||||
|
||||
err = regulator_disable(innolux->supply);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
innolux->prepared = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int innolux_panel_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct innolux_panel *innolux = to_innolux_panel(panel);
|
||||
int err, regulator_err;
|
||||
|
||||
if (innolux->prepared)
|
||||
return 0;
|
||||
|
||||
gpiod_set_value_cansleep(innolux->enable_gpio, 0);
|
||||
|
||||
err = regulator_enable(innolux->supply);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* T2: 15ms - 1000ms */
|
||||
usleep_range(15000, 16000);
|
||||
|
||||
gpiod_set_value_cansleep(innolux->enable_gpio, 1);
|
||||
|
||||
/* T4: 15ms - 1000ms */
|
||||
usleep_range(15000, 16000);
|
||||
|
||||
err = mipi_dsi_dcs_exit_sleep_mode(innolux->link);
|
||||
if (err < 0) {
|
||||
DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n",
|
||||
err);
|
||||
goto poweroff;
|
||||
}
|
||||
|
||||
/* T6: 120ms - 1000ms*/
|
||||
msleep(120);
|
||||
|
||||
err = mipi_dsi_dcs_set_display_on(innolux->link);
|
||||
if (err < 0) {
|
||||
DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n",
|
||||
err);
|
||||
goto poweroff;
|
||||
}
|
||||
|
||||
/* T7: 5ms */
|
||||
usleep_range(5000, 6000);
|
||||
|
||||
innolux->prepared = true;
|
||||
|
||||
return 0;
|
||||
|
||||
poweroff:
|
||||
regulator_err = regulator_disable(innolux->supply);
|
||||
if (regulator_err)
|
||||
DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n",
|
||||
regulator_err);
|
||||
|
||||
gpiod_set_value_cansleep(innolux->enable_gpio, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int innolux_panel_enable(struct drm_panel *panel)
|
||||
{
|
||||
struct innolux_panel *innolux = to_innolux_panel(panel);
|
||||
int ret;
|
||||
|
||||
if (innolux->enabled)
|
||||
return 0;
|
||||
|
||||
innolux->backlight->props.power = FB_BLANK_UNBLANK;
|
||||
ret = backlight_update_status(innolux->backlight);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(panel->drm->dev,
|
||||
"Failed to enable backlight %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
innolux->enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_display_mode default_mode = {
|
||||
.clock = 56900,
|
||||
.hdisplay = 768,
|
||||
.hsync_start = 768 + 40,
|
||||
.hsync_end = 768 + 40 + 40,
|
||||
.htotal = 768 + 40 + 40 + 40,
|
||||
.vdisplay = 1024,
|
||||
.vsync_start = 1024 + 20,
|
||||
.vsync_end = 1024 + 20 + 4,
|
||||
.vtotal = 1024 + 20 + 4 + 20,
|
||||
.vrefresh = 60,
|
||||
};
|
||||
|
||||
static int innolux_panel_get_modes(struct drm_panel *panel)
|
||||
{
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_duplicate(panel->drm, &default_mode);
|
||||
if (!mode) {
|
||||
DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
|
||||
default_mode.hdisplay, default_mode.vdisplay,
|
||||
default_mode.vrefresh);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
drm_mode_set_name(mode);
|
||||
|
||||
drm_mode_probed_add(panel->connector, mode);
|
||||
|
||||
panel->connector->display_info.width_mm = 120;
|
||||
panel->connector->display_info.height_mm = 160;
|
||||
panel->connector->display_info.bpc = 8;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs innolux_panel_funcs = {
|
||||
.disable = innolux_panel_disable,
|
||||
.unprepare = innolux_panel_unprepare,
|
||||
.prepare = innolux_panel_prepare,
|
||||
.enable = innolux_panel_enable,
|
||||
.get_modes = innolux_panel_get_modes,
|
||||
};
|
||||
|
||||
static const struct of_device_id innolux_of_match[] = {
|
||||
{ .compatible = "innolux,p079zca", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, innolux_of_match);
|
||||
|
||||
static int innolux_panel_add(struct innolux_panel *innolux)
|
||||
{
|
||||
struct device *dev = &innolux->link->dev;
|
||||
struct device_node *np;
|
||||
int err;
|
||||
|
||||
innolux->supply = devm_regulator_get(dev, "power");
|
||||
if (IS_ERR(innolux->supply))
|
||||
return PTR_ERR(innolux->supply);
|
||||
|
||||
innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(innolux->enable_gpio)) {
|
||||
err = PTR_ERR(innolux->enable_gpio);
|
||||
dev_dbg(dev, "failed to get enable gpio: %d\n", err);
|
||||
innolux->enable_gpio = NULL;
|
||||
}
|
||||
|
||||
np = of_parse_phandle(dev->of_node, "backlight", 0);
|
||||
if (np) {
|
||||
innolux->backlight = of_find_backlight_by_node(np);
|
||||
of_node_put(np);
|
||||
|
||||
if (!innolux->backlight)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
drm_panel_init(&innolux->base);
|
||||
innolux->base.funcs = &innolux_panel_funcs;
|
||||
innolux->base.dev = &innolux->link->dev;
|
||||
|
||||
err = drm_panel_add(&innolux->base);
|
||||
if (err < 0)
|
||||
goto put_backlight;
|
||||
|
||||
return 0;
|
||||
|
||||
put_backlight:
|
||||
put_device(&innolux->backlight->dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void innolux_panel_del(struct innolux_panel *innolux)
|
||||
{
|
||||
if (innolux->base.dev)
|
||||
drm_panel_remove(&innolux->base);
|
||||
|
||||
put_device(&innolux->backlight->dev);
|
||||
}
|
||||
|
||||
static int innolux_panel_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct innolux_panel *innolux;
|
||||
int err;
|
||||
|
||||
dsi->lanes = 4;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
|
||||
MIPI_DSI_MODE_LPM;
|
||||
|
||||
innolux = devm_kzalloc(&dsi->dev, sizeof(*innolux), GFP_KERNEL);
|
||||
if (!innolux)
|
||||
return -ENOMEM;
|
||||
|
||||
mipi_dsi_set_drvdata(dsi, innolux);
|
||||
|
||||
innolux->link = dsi;
|
||||
|
||||
err = innolux_panel_add(innolux);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = mipi_dsi_attach(dsi);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int innolux_panel_remove(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
|
||||
int err;
|
||||
|
||||
err = innolux_panel_unprepare(&innolux->base);
|
||||
if (err < 0)
|
||||
DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n",
|
||||
err);
|
||||
|
||||
err = innolux_panel_disable(&innolux->base);
|
||||
if (err < 0)
|
||||
DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err);
|
||||
|
||||
err = mipi_dsi_detach(dsi);
|
||||
if (err < 0)
|
||||
DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n",
|
||||
err);
|
||||
|
||||
drm_panel_detach(&innolux->base);
|
||||
innolux_panel_del(innolux);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void innolux_panel_shutdown(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
|
||||
|
||||
innolux_panel_unprepare(&innolux->base);
|
||||
innolux_panel_disable(&innolux->base);
|
||||
}
|
||||
|
||||
static struct mipi_dsi_driver innolux_panel_driver = {
|
||||
.driver = {
|
||||
.name = "panel-innolux-p079zca",
|
||||
.of_match_table = innolux_of_match,
|
||||
},
|
||||
.probe = innolux_panel_probe,
|
||||
.remove = innolux_panel_remove,
|
||||
.shutdown = innolux_panel_shutdown,
|
||||
};
|
||||
module_mipi_dsi_driver(innolux_panel_driver);
|
||||
|
||||
MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("Innolux P079ZCA panel driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -16,6 +16,7 @@
|
||||
#include <drm/drm_panel.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define S6E3HA2_MIN_BRIGHTNESS 0
|
||||
@ -218,6 +219,16 @@ unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = {
|
||||
0x1d, 0x1e, 0x1f, 0x20, 0x21
|
||||
};
|
||||
|
||||
enum s6e3ha2_type {
|
||||
HA2_TYPE,
|
||||
HF2_TYPE,
|
||||
};
|
||||
|
||||
struct s6e3ha2_panel_desc {
|
||||
const struct drm_display_mode *mode;
|
||||
enum s6e3ha2_type type;
|
||||
};
|
||||
|
||||
struct s6e3ha2 {
|
||||
struct device *dev;
|
||||
struct drm_panel panel;
|
||||
@ -226,6 +237,8 @@ struct s6e3ha2 {
|
||||
struct regulator_bulk_data supplies[2];
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct gpio_desc *enable_gpio;
|
||||
|
||||
const struct s6e3ha2_panel_desc *desc;
|
||||
};
|
||||
|
||||
static int s6e3ha2_dcs_write(struct s6e3ha2 *ctx, const void *data, size_t len)
|
||||
@ -283,11 +296,21 @@ static int s6e3ha2_single_dsi_set(struct s6e3ha2 *ctx)
|
||||
static int s6e3ha2_freq_calibration(struct s6e3ha2 *ctx)
|
||||
{
|
||||
s6e3ha2_dcs_write_seq_static(ctx, 0xfd, 0x1c);
|
||||
if (ctx->desc->type == HF2_TYPE)
|
||||
s6e3ha2_dcs_write_seq_static(ctx, 0xf2, 0x67, 0x40, 0xc5);
|
||||
s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20, 0x39);
|
||||
s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0);
|
||||
s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20);
|
||||
s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62, 0x40,
|
||||
0x80, 0xc0, 0x28, 0x28, 0x28, 0x28, 0x39, 0xc5);
|
||||
|
||||
if (ctx->desc->type == HA2_TYPE)
|
||||
s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62,
|
||||
0x40, 0x80, 0xc0, 0x28, 0x28,
|
||||
0x28, 0x28, 0x39, 0xc5);
|
||||
else
|
||||
s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x14, 0x6d,
|
||||
0x40, 0x80, 0xc0, 0x28, 0x28,
|
||||
0x28, 0x28, 0x39, 0xc5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -583,7 +606,7 @@ static int s6e3ha2_enable(struct drm_panel *panel)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_display_mode default_mode = {
|
||||
static const struct drm_display_mode s6e3ha2_mode = {
|
||||
.clock = 222372,
|
||||
.hdisplay = 1440,
|
||||
.hsync_start = 1440 + 1,
|
||||
@ -597,16 +620,41 @@ static const struct drm_display_mode default_mode = {
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static const struct s6e3ha2_panel_desc samsung_s6e3ha2 = {
|
||||
.mode = &s6e3ha2_mode,
|
||||
.type = HA2_TYPE,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode s6e3hf2_mode = {
|
||||
.clock = 247856,
|
||||
.hdisplay = 1600,
|
||||
.hsync_start = 1600 + 1,
|
||||
.hsync_end = 1600 + 1 + 1,
|
||||
.htotal = 1600 + 1 + 1 + 1,
|
||||
.vdisplay = 2560,
|
||||
.vsync_start = 2560 + 1,
|
||||
.vsync_end = 2560 + 1 + 1,
|
||||
.vtotal = 2560 + 1 + 1 + 15,
|
||||
.vrefresh = 60,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static const struct s6e3ha2_panel_desc samsung_s6e3hf2 = {
|
||||
.mode = &s6e3hf2_mode,
|
||||
.type = HF2_TYPE,
|
||||
};
|
||||
|
||||
static int s6e3ha2_get_modes(struct drm_panel *panel)
|
||||
{
|
||||
struct drm_connector *connector = panel->connector;
|
||||
struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_duplicate(panel->drm, &default_mode);
|
||||
mode = drm_mode_duplicate(panel->drm, ctx->desc->mode);
|
||||
if (!mode) {
|
||||
DRM_ERROR("failed to add mode %ux%ux@%u\n",
|
||||
default_mode.hdisplay, default_mode.vdisplay,
|
||||
default_mode.vrefresh);
|
||||
ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
|
||||
ctx->desc->mode->vrefresh);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -642,6 +690,7 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
|
||||
mipi_dsi_set_drvdata(dsi, ctx);
|
||||
|
||||
ctx->dev = dev;
|
||||
ctx->desc = of_device_get_match_data(dev);
|
||||
|
||||
dsi->lanes = 4;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
@ -717,7 +766,8 @@ static int s6e3ha2_remove(struct mipi_dsi_device *dsi)
|
||||
}
|
||||
|
||||
static const struct of_device_id s6e3ha2_of_match[] = {
|
||||
{ .compatible = "samsung,s6e3ha2" },
|
||||
{ .compatible = "samsung,s6e3ha2", .data = &samsung_s6e3ha2 },
|
||||
{ .compatible = "samsung,s6e3hf2", .data = &samsung_s6e3hf2 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, s6e3ha2_of_match);
|
||||
|
@ -638,6 +638,34 @@ static const struct panel_desc auo_g185han01 = {
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
|
||||
};
|
||||
|
||||
static const struct display_timing auo_p320hvn03_timings = {
|
||||
.pixelclock = { 106000000, 148500000, 164000000 },
|
||||
.hactive = { 1920, 1920, 1920 },
|
||||
.hfront_porch = { 25, 50, 130 },
|
||||
.hback_porch = { 25, 50, 130 },
|
||||
.hsync_len = { 20, 40, 105 },
|
||||
.vactive = { 1080, 1080, 1080 },
|
||||
.vfront_porch = { 8, 17, 150 },
|
||||
.vback_porch = { 8, 17, 150 },
|
||||
.vsync_len = { 4, 11, 100 },
|
||||
};
|
||||
|
||||
static const struct panel_desc auo_p320hvn03 = {
|
||||
.timings = &auo_p320hvn03_timings,
|
||||
.num_timings = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 698,
|
||||
.height = 393,
|
||||
},
|
||||
.delay = {
|
||||
.prepare = 1,
|
||||
.enable = 450,
|
||||
.unprepare = 500,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode auo_t215hvn01_mode = {
|
||||
.clock = 148800,
|
||||
.hdisplay = 1920,
|
||||
@ -1322,6 +1350,33 @@ static const struct panel_desc lg_lp129qe = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct display_timing nec_nl12880bc20_05_timing = {
|
||||
.pixelclock = { 67000000, 71000000, 75000000 },
|
||||
.hactive = { 1280, 1280, 1280 },
|
||||
.hfront_porch = { 2, 30, 30 },
|
||||
.hback_porch = { 6, 100, 100 },
|
||||
.hsync_len = { 2, 30, 30 },
|
||||
.vactive = { 800, 800, 800 },
|
||||
.vfront_porch = { 5, 5, 5 },
|
||||
.vback_porch = { 11, 11, 11 },
|
||||
.vsync_len = { 7, 7, 7 },
|
||||
};
|
||||
|
||||
static const struct panel_desc nec_nl12880bc20_05 = {
|
||||
.timings = &nec_nl12880bc20_05_timing,
|
||||
.num_timings = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 261,
|
||||
.height = 163,
|
||||
},
|
||||
.delay = {
|
||||
.enable = 50,
|
||||
.disable = 50,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode nec_nl4827hc19_05b_mode = {
|
||||
.clock = 10870,
|
||||
.hdisplay = 480,
|
||||
@ -1371,6 +1426,32 @@ static const struct panel_desc netron_dy_e231732 = {
|
||||
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
|
||||
};
|
||||
|
||||
static const struct display_timing nlt_nl192108ac18_02d_timing = {
|
||||
.pixelclock = { 130000000, 148350000, 163000000 },
|
||||
.hactive = { 1920, 1920, 1920 },
|
||||
.hfront_porch = { 80, 100, 100 },
|
||||
.hback_porch = { 100, 120, 120 },
|
||||
.hsync_len = { 50, 60, 60 },
|
||||
.vactive = { 1080, 1080, 1080 },
|
||||
.vfront_porch = { 12, 30, 30 },
|
||||
.vback_porch = { 4, 10, 10 },
|
||||
.vsync_len = { 4, 5, 5 },
|
||||
};
|
||||
|
||||
static const struct panel_desc nlt_nl192108ac18_02d = {
|
||||
.timings = &nlt_nl192108ac18_02d_timing,
|
||||
.num_timings = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 344,
|
||||
.height = 194,
|
||||
},
|
||||
.delay = {
|
||||
.unprepare = 500,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode nvd_9128_mode = {
|
||||
.clock = 29500,
|
||||
.hdisplay = 800,
|
||||
@ -1887,6 +1968,9 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "auo,g185han01",
|
||||
.data = &auo_g185han01,
|
||||
}, {
|
||||
.compatible = "auo,p320hvn03",
|
||||
.data = &auo_p320hvn03,
|
||||
}, {
|
||||
.compatible = "auo,t215hvn01",
|
||||
.data = &auo_t215hvn01,
|
||||
@ -1971,12 +2055,18 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "lg,lp129qe",
|
||||
.data = &lg_lp129qe,
|
||||
}, {
|
||||
.compatible = "nec,nl12880bc20-05",
|
||||
.data = &nec_nl12880bc20_05,
|
||||
}, {
|
||||
.compatible = "nec,nl4827hc19-05b",
|
||||
.data = &nec_nl4827hc19_05b,
|
||||
}, {
|
||||
.compatible = "netron-dy,e231732",
|
||||
.data = &netron_dy_e231732,
|
||||
}, {
|
||||
.compatible = "nlt,nl192108ac18-02d",
|
||||
.data = &nlt_nl192108ac18_02d,
|
||||
}, {
|
||||
.compatible = "nvd,9128",
|
||||
.data = &nvd_9128,
|
||||
|
@ -50,17 +50,6 @@ irqreturn_t pl111_irq(int irq, void *data)
|
||||
return status;
|
||||
}
|
||||
|
||||
static u32 pl111_get_fb_offset(struct drm_plane_state *pstate)
|
||||
{
|
||||
struct drm_framebuffer *fb = pstate->fb;
|
||||
struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
|
||||
return (obj->paddr +
|
||||
fb->offsets[0] +
|
||||
fb->format->cpp[0] * pstate->src_x +
|
||||
fb->pitches[0] * pstate->src_y);
|
||||
}
|
||||
|
||||
static int pl111_display_check(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *pstate,
|
||||
struct drm_crtc_state *cstate)
|
||||
@ -73,7 +62,7 @@ static int pl111_display_check(struct drm_simple_display_pipe *pipe,
|
||||
return -EINVAL;
|
||||
|
||||
if (fb) {
|
||||
u32 offset = pl111_get_fb_offset(pstate);
|
||||
u32 offset = drm_fb_cma_get_gem_addr(fb, pstate, 0);
|
||||
|
||||
/* FB base address must be dword aligned. */
|
||||
if (offset & 3)
|
||||
@ -249,7 +238,7 @@ static void pl111_display_update(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_framebuffer *fb = pstate->fb;
|
||||
|
||||
if (fb) {
|
||||
u32 addr = pl111_get_fb_offset(pstate);
|
||||
u32 addr = drm_fb_cma_get_gem_addr(fb, pstate, 0);
|
||||
|
||||
writel(addr, priv->regs + CLCD_UBAS);
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||||
|
||||
static enum drm_mode_status
|
||||
dw_hdmi_rockchip_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
|
||||
int pclk = mode->clock * 1000;
|
||||
|
@ -7,7 +7,7 @@ config DRM_VC4
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select DRM_GEM_CMA_HELPER
|
||||
select DRM_PANEL
|
||||
select DRM_PANEL_BRIDGE
|
||||
select SND_PCM
|
||||
select SND_PCM_ELD
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
|
@ -91,8 +91,7 @@ static void vc4_bo_destroy(struct vc4_bo *bo)
|
||||
vc4->bo_stats.num_allocated--;
|
||||
vc4->bo_stats.size_allocated -= obj->size;
|
||||
|
||||
if (bo->resv == &bo->_resv)
|
||||
reservation_object_fini(bo->resv);
|
||||
reservation_object_fini(&bo->_resv);
|
||||
|
||||
drm_gem_cma_free_object(obj);
|
||||
}
|
||||
@ -212,6 +211,8 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size)
|
||||
vc4->bo_stats.num_allocated++;
|
||||
vc4->bo_stats.size_allocated += size;
|
||||
mutex_unlock(&vc4->bo_lock);
|
||||
bo->resv = &bo->_resv;
|
||||
reservation_object_init(bo->resv);
|
||||
|
||||
return &bo->base.base;
|
||||
}
|
||||
@ -250,12 +251,7 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
bo = to_vc4_bo(&cma_obj->base);
|
||||
|
||||
bo->resv = &bo->_resv;
|
||||
reservation_object_init(bo->resv);
|
||||
|
||||
return bo;
|
||||
return to_vc4_bo(&cma_obj->base);
|
||||
}
|
||||
|
||||
int vc4_dumb_create(struct drm_file *file_priv,
|
||||
|
@ -23,8 +23,10 @@
|
||||
*/
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
@ -95,7 +97,8 @@ struct vc4_dpi {
|
||||
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_connector *connector;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
bool is_panel_bridge;
|
||||
|
||||
void __iomem *regs;
|
||||
|
||||
@ -118,24 +121,6 @@ to_vc4_dpi_encoder(struct drm_encoder *encoder)
|
||||
return container_of(encoder, struct vc4_dpi_encoder, base.base);
|
||||
}
|
||||
|
||||
/* VC4 DPI connector KMS struct */
|
||||
struct vc4_dpi_connector {
|
||||
struct drm_connector base;
|
||||
struct vc4_dpi *dpi;
|
||||
|
||||
/* Since the connector is attached to just the one encoder,
|
||||
* this is the reference to it so we can do the best_encoder()
|
||||
* hook.
|
||||
*/
|
||||
struct drm_encoder *encoder;
|
||||
};
|
||||
|
||||
static inline struct vc4_dpi_connector *
|
||||
to_vc4_dpi_connector(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct vc4_dpi_connector, base);
|
||||
}
|
||||
|
||||
#define DPI_REG(reg) { reg, #reg }
|
||||
static const struct {
|
||||
u32 reg;
|
||||
@ -167,80 +152,6 @@ int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused)
|
||||
}
|
||||
#endif
|
||||
|
||||
static enum drm_connector_status
|
||||
vc4_dpi_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct vc4_dpi_connector *vc4_connector =
|
||||
to_vc4_dpi_connector(connector);
|
||||
struct vc4_dpi *dpi = vc4_connector->dpi;
|
||||
|
||||
if (dpi->panel)
|
||||
return connector_status_connected;
|
||||
else
|
||||
return connector_status_disconnected;
|
||||
}
|
||||
|
||||
static void vc4_dpi_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static int vc4_dpi_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct vc4_dpi_connector *vc4_connector =
|
||||
to_vc4_dpi_connector(connector);
|
||||
struct vc4_dpi *dpi = vc4_connector->dpi;
|
||||
|
||||
if (dpi->panel)
|
||||
return drm_panel_get_modes(dpi->panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs vc4_dpi_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.detect = vc4_dpi_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = vc4_dpi_connector_destroy,
|
||||
.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 const struct drm_connector_helper_funcs vc4_dpi_connector_helper_funcs = {
|
||||
.get_modes = vc4_dpi_connector_get_modes,
|
||||
};
|
||||
|
||||
static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev,
|
||||
struct vc4_dpi *dpi)
|
||||
{
|
||||
struct drm_connector *connector = NULL;
|
||||
struct vc4_dpi_connector *dpi_connector;
|
||||
|
||||
dpi_connector = devm_kzalloc(dev->dev, sizeof(*dpi_connector),
|
||||
GFP_KERNEL);
|
||||
if (!dpi_connector)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
connector = &dpi_connector->base;
|
||||
|
||||
dpi_connector->encoder = dpi->encoder;
|
||||
dpi_connector->dpi = dpi;
|
||||
|
||||
drm_connector_init(dev, connector, &vc4_dpi_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DPI);
|
||||
drm_connector_helper_add(connector, &vc4_dpi_connector_helper_funcs);
|
||||
|
||||
connector->polled = 0;
|
||||
connector->interlace_allowed = 0;
|
||||
connector->doublescan_allowed = 0;
|
||||
|
||||
drm_mode_connector_attach_encoder(connector, dpi->encoder);
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
@ -250,11 +161,7 @@ static void vc4_dpi_encoder_disable(struct drm_encoder *encoder)
|
||||
struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder);
|
||||
struct vc4_dpi *dpi = vc4_encoder->dpi;
|
||||
|
||||
drm_panel_disable(dpi->panel);
|
||||
|
||||
clk_disable_unprepare(dpi->pixel_clock);
|
||||
|
||||
drm_panel_unprepare(dpi->panel);
|
||||
}
|
||||
|
||||
static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
|
||||
@ -265,12 +172,6 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
|
||||
u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE;
|
||||
int ret;
|
||||
|
||||
ret = drm_panel_prepare(dpi->panel);
|
||||
if (ret) {
|
||||
DRM_ERROR("Panel failed to prepare\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dpi->connector->display_info.num_bus_formats) {
|
||||
u32 bus_format = dpi->connector->display_info.bus_formats[0];
|
||||
|
||||
@ -321,13 +222,6 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
|
||||
ret = clk_prepare_enable(dpi->pixel_clock);
|
||||
if (ret)
|
||||
DRM_ERROR("Failed to set clock rate: %d\n", ret);
|
||||
|
||||
ret = drm_panel_enable(dpi->panel);
|
||||
if (ret) {
|
||||
DRM_ERROR("Panel failed to enable\n");
|
||||
drm_panel_unprepare(dpi->panel);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static bool vc4_dpi_encoder_mode_fixup(struct drm_encoder *encoder,
|
||||
@ -351,24 +245,34 @@ static const struct of_device_id vc4_dpi_dt_match[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
/* Walks the OF graph to find the panel node and then asks DRM to look
|
||||
* up the panel.
|
||||
/* Sets up the next link in the display chain, whether it's a panel or
|
||||
* a bridge.
|
||||
*/
|
||||
static struct drm_panel *vc4_dpi_get_panel(struct device *dev)
|
||||
static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
|
||||
{
|
||||
struct device_node *panel_node;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device *dev = &dpi->pdev->dev;
|
||||
struct drm_panel *panel;
|
||||
int ret;
|
||||
|
||||
/* don't proceed if we have an endpoint but no panel_node tied to it */
|
||||
panel_node = of_graph_get_remote_node(np, 0, 0);
|
||||
if (!panel_node)
|
||||
return NULL;
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
|
||||
&panel, &dpi->bridge);
|
||||
if (ret) {
|
||||
/* If nothing was connected in the DT, that's not an
|
||||
* error.
|
||||
*/
|
||||
if (ret == -ENODEV)
|
||||
return 0;
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
panel = of_drm_find_panel(panel_node);
|
||||
of_node_put(panel_node);
|
||||
if (panel) {
|
||||
dpi->bridge = drm_panel_bridge_add(panel,
|
||||
DRM_MODE_CONNECTOR_DPI);
|
||||
dpi->is_panel_bridge = true;
|
||||
}
|
||||
|
||||
return panel;
|
||||
return drm_bridge_attach(dpi->encoder, dpi->bridge, NULL);
|
||||
}
|
||||
|
||||
static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
|
||||
@ -422,20 +326,13 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
|
||||
if (ret)
|
||||
DRM_ERROR("Failed to turn on core clock: %d\n", ret);
|
||||
|
||||
dpi->panel = vc4_dpi_get_panel(dev);
|
||||
|
||||
drm_encoder_init(drm, dpi->encoder, &vc4_dpi_encoder_funcs,
|
||||
DRM_MODE_ENCODER_DPI, NULL);
|
||||
drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs);
|
||||
|
||||
dpi->connector = vc4_dpi_connector_init(drm, dpi);
|
||||
if (IS_ERR(dpi->connector)) {
|
||||
ret = PTR_ERR(dpi->connector);
|
||||
ret = vc4_dpi_init_bridge(dpi);
|
||||
if (ret)
|
||||
goto err_destroy_encoder;
|
||||
}
|
||||
|
||||
if (dpi->panel)
|
||||
drm_panel_attach(dpi->panel, dpi->connector);
|
||||
|
||||
dev_set_drvdata(dev, dpi);
|
||||
|
||||
@ -456,10 +353,9 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master,
|
||||
struct vc4_dev *vc4 = to_vc4_dev(drm);
|
||||
struct vc4_dpi *dpi = dev_get_drvdata(dev);
|
||||
|
||||
if (dpi->panel)
|
||||
drm_panel_detach(dpi->panel);
|
||||
if (dpi->is_panel_bridge)
|
||||
drm_panel_bridge_remove(dpi->bridge);
|
||||
|
||||
vc4_dpi_connector_destroy(dpi->connector);
|
||||
drm_encoder_cleanup(dpi->encoder);
|
||||
|
||||
clk_disable_unprepare(dpi->core_clock);
|
||||
|
@ -503,8 +503,8 @@ struct vc4_dsi {
|
||||
|
||||
struct mipi_dsi_host dsi_host;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_connector *connector;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
bool is_panel_bridge;
|
||||
|
||||
void __iomem *regs;
|
||||
|
||||
@ -605,18 +605,6 @@ to_vc4_dsi_encoder(struct drm_encoder *encoder)
|
||||
return container_of(encoder, struct vc4_dsi_encoder, base.base);
|
||||
}
|
||||
|
||||
/* VC4 DSI connector KMS struct */
|
||||
struct vc4_dsi_connector {
|
||||
struct drm_connector base;
|
||||
struct vc4_dsi *dsi;
|
||||
};
|
||||
|
||||
static inline struct vc4_dsi_connector *
|
||||
to_vc4_dsi_connector(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct vc4_dsi_connector, base);
|
||||
}
|
||||
|
||||
#define DSI_REG(reg) { reg, #reg }
|
||||
static const struct {
|
||||
u32 reg;
|
||||
@ -724,79 +712,6 @@ int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused)
|
||||
}
|
||||
#endif
|
||||
|
||||
static enum drm_connector_status
|
||||
vc4_dsi_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct vc4_dsi_connector *vc4_connector =
|
||||
to_vc4_dsi_connector(connector);
|
||||
struct vc4_dsi *dsi = vc4_connector->dsi;
|
||||
|
||||
if (dsi->panel)
|
||||
return connector_status_connected;
|
||||
else
|
||||
return connector_status_disconnected;
|
||||
}
|
||||
|
||||
static void vc4_dsi_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static int vc4_dsi_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct vc4_dsi_connector *vc4_connector =
|
||||
to_vc4_dsi_connector(connector);
|
||||
struct vc4_dsi *dsi = vc4_connector->dsi;
|
||||
|
||||
if (dsi->panel)
|
||||
return drm_panel_get_modes(dsi->panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs vc4_dsi_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.detect = vc4_dsi_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = vc4_dsi_connector_destroy,
|
||||
.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 const struct drm_connector_helper_funcs vc4_dsi_connector_helper_funcs = {
|
||||
.get_modes = vc4_dsi_connector_get_modes,
|
||||
};
|
||||
|
||||
static struct drm_connector *vc4_dsi_connector_init(struct drm_device *dev,
|
||||
struct vc4_dsi *dsi)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
struct vc4_dsi_connector *dsi_connector;
|
||||
|
||||
dsi_connector = devm_kzalloc(dev->dev, sizeof(*dsi_connector),
|
||||
GFP_KERNEL);
|
||||
if (!dsi_connector)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
connector = &dsi_connector->base;
|
||||
|
||||
dsi_connector->dsi = dsi;
|
||||
|
||||
drm_connector_init(dev, connector, &vc4_dsi_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DSI);
|
||||
drm_connector_helper_add(connector, &vc4_dsi_connector_helper_funcs);
|
||||
|
||||
connector->polled = 0;
|
||||
connector->interlace_allowed = 0;
|
||||
connector->doublescan_allowed = 0;
|
||||
|
||||
drm_mode_connector_attach_encoder(connector, dsi->encoder);
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
||||
static void vc4_dsi_encoder_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
drm_encoder_cleanup(encoder);
|
||||
@ -894,12 +809,8 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
|
||||
struct vc4_dsi *dsi = vc4_encoder->dsi;
|
||||
struct device *dev = &dsi->pdev->dev;
|
||||
|
||||
drm_panel_disable(dsi->panel);
|
||||
|
||||
vc4_dsi_ulps(dsi, true);
|
||||
|
||||
drm_panel_unprepare(dsi->panel);
|
||||
|
||||
clk_disable_unprepare(dsi->pll_phy_clock);
|
||||
clk_disable_unprepare(dsi->escape_clock);
|
||||
clk_disable_unprepare(dsi->pixel_clock);
|
||||
@ -984,12 +895,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
|
||||
return;
|
||||
}
|
||||
|
||||
ret = drm_panel_prepare(dsi->panel);
|
||||
if (ret) {
|
||||
DRM_ERROR("Panel failed to prepare\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (debug_dump_regs) {
|
||||
DRM_INFO("DSI regs before:\n");
|
||||
vc4_dsi_dump_regs(dsi);
|
||||
@ -1211,13 +1116,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
|
||||
DRM_INFO("DSI regs after:\n");
|
||||
vc4_dsi_dump_regs(dsi);
|
||||
}
|
||||
|
||||
ret = drm_panel_enable(dsi->panel);
|
||||
if (ret) {
|
||||
DRM_ERROR("Panel failed to enable\n");
|
||||
drm_panel_unprepare(dsi->panel);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
|
||||
@ -1415,17 +1313,22 @@ static int vc4_dsi_host_attach(struct mipi_dsi_host *host,
|
||||
return 0;
|
||||
}
|
||||
|
||||
dsi->panel = of_drm_find_panel(device->dev.of_node);
|
||||
if (!dsi->panel)
|
||||
return 0;
|
||||
dsi->bridge = of_drm_find_bridge(device->dev.of_node);
|
||||
if (!dsi->bridge) {
|
||||
struct drm_panel *panel =
|
||||
of_drm_find_panel(device->dev.of_node);
|
||||
|
||||
ret = drm_panel_attach(dsi->panel, dsi->connector);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
dsi->bridge = drm_panel_bridge_add(panel,
|
||||
DRM_MODE_CONNECTOR_DSI);
|
||||
if (IS_ERR(dsi->bridge)) {
|
||||
ret = PTR_ERR(dsi->bridge);
|
||||
dsi->bridge = NULL;
|
||||
return ret;
|
||||
}
|
||||
dsi->is_panel_bridge = true;
|
||||
}
|
||||
|
||||
drm_helper_hpd_irq_event(dsi->connector->dev);
|
||||
|
||||
return 0;
|
||||
return drm_bridge_attach(dsi->encoder, dsi->bridge, NULL);
|
||||
}
|
||||
|
||||
static int vc4_dsi_host_detach(struct mipi_dsi_host *host,
|
||||
@ -1433,15 +1336,9 @@ static int vc4_dsi_host_detach(struct mipi_dsi_host *host,
|
||||
{
|
||||
struct vc4_dsi *dsi = host_to_dsi(host);
|
||||
|
||||
if (dsi->panel) {
|
||||
int ret = drm_panel_detach(dsi->panel);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dsi->panel = NULL;
|
||||
|
||||
drm_helper_hpd_irq_event(dsi->connector->dev);
|
||||
if (dsi->is_panel_bridge) {
|
||||
drm_panel_bridge_remove(dsi->bridge);
|
||||
dsi->bridge = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1708,12 +1605,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
|
||||
DRM_MODE_ENCODER_DSI, NULL);
|
||||
drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs);
|
||||
|
||||
dsi->connector = vc4_dsi_connector_init(drm, dsi);
|
||||
if (IS_ERR(dsi->connector)) {
|
||||
ret = PTR_ERR(dsi->connector);
|
||||
goto err_destroy_encoder;
|
||||
}
|
||||
|
||||
dsi->dsi_host.ops = &vc4_dsi_host_ops;
|
||||
dsi->dsi_host.dev = dev;
|
||||
|
||||
@ -1724,11 +1615,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_destroy_encoder:
|
||||
vc4_dsi_encoder_destroy(dsi->encoder);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vc4_dsi_unbind(struct device *dev, struct device *master,
|
||||
@ -1740,7 +1626,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
vc4_dsi_connector_destroy(dsi->connector);
|
||||
drm_bridge_remove(dsi->bridge);
|
||||
vc4_dsi_encoder_destroy(dsi->encoder);
|
||||
|
||||
mipi_dsi_host_unregister(&dsi->dsi_host);
|
||||
|
@ -111,8 +111,8 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
|
||||
&handle);
|
||||
|
||||
if (ret) {
|
||||
state->bo_count = i - 1;
|
||||
goto err;
|
||||
state->bo_count = i;
|
||||
goto err_delete_handle;
|
||||
}
|
||||
bo_state[i].handle = handle;
|
||||
bo_state[i].paddr = vc4_bo->base.paddr;
|
||||
@ -124,13 +124,16 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
|
||||
state->bo_count * sizeof(*bo_state)))
|
||||
ret = -EFAULT;
|
||||
|
||||
kfree(bo_state);
|
||||
err_delete_handle:
|
||||
if (ret) {
|
||||
for (i = 0; i < state->bo_count; i++)
|
||||
drm_gem_handle_delete(file_priv, bo_state[i].handle);
|
||||
}
|
||||
|
||||
err_free:
|
||||
|
||||
vc4_free_hang_state(dev, kernel_state);
|
||||
kfree(bo_state);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ static int zx_drm_probe(struct platform_device *pdev)
|
||||
struct component_match *match = NULL;
|
||||
int ret;
|
||||
|
||||
ret = of_platform_populate(parent, NULL, NULL, dev);
|
||||
ret = devm_of_platform_populate(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -125,7 +125,7 @@ struct dw_hdmi_phy_ops {
|
||||
struct dw_hdmi_plat_data {
|
||||
struct regmap *regm;
|
||||
enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode);
|
||||
const struct drm_display_mode *mode);
|
||||
unsigned long input_bus_format;
|
||||
unsigned long input_bus_encoding;
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <drm/drm_modes.h>
|
||||
|
||||
struct drm_bridge;
|
||||
struct drm_panel;
|
||||
|
||||
/**
|
||||
* struct drm_bridge_funcs - drm_bridge control functions
|
||||
@ -263,4 +264,10 @@ void drm_bridge_mode_set(struct drm_bridge *bridge,
|
||||
void drm_bridge_pre_enable(struct drm_bridge *bridge);
|
||||
void drm_bridge_enable(struct drm_bridge *bridge);
|
||||
|
||||
#ifdef CONFIG_DRM_PANEL_BRIDGE
|
||||
struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
|
||||
u32 connector_type);
|
||||
void drm_panel_bridge_remove(struct drm_bridge *bridge);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -30,8 +30,6 @@
|
||||
|
||||
#include <uapi/drm/drm_mode.h>
|
||||
|
||||
struct drm_device;
|
||||
|
||||
struct drm_connector_helper_funcs;
|
||||
struct drm_modeset_acquire_ctx;
|
||||
struct drm_device;
|
||||
|
@ -214,7 +214,7 @@ struct drm_property_blob {
|
||||
|
||||
struct drm_prop_enum_list {
|
||||
int type;
|
||||
char *name;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
#define obj_to_property(x) container_of(x, struct drm_property, base)
|
||||
|
Loading…
Reference in New Issue
Block a user