Commit bfda9aa1 authored by Dave Airlie's avatar Dave Airlie

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.
  ...
parents 3ee45a3b ac7c7483
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.
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";
};
};
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.
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.
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;
}
......
/*
* 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
......
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
......
/*
* 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)
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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment