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 S6E3HA2 5.7" 1440x2560 AMOLED panel
Samsung S6E3HF2 5.65" 1600x2560 AMOLED panel
Required properties: Required properties:
- compatible: "samsung,s6e3ha2" - compatible: should be one of:
"samsung,s6e3ha2",
"samsung,s6e3hf2".
- reg: the virtual channel number of a DSI peripheral - reg: the virtual channel number of a DSI peripheral
- vdd3-supply: I/O voltage supply - vdd3-supply: I/O voltage supply
- vci-supply: voltage supply for analog circuits - vci-supply: voltage supply for analog circuits
......
...@@ -219,6 +219,7 @@ nexbox Nexbox ...@@ -219,6 +219,7 @@ nexbox Nexbox
newhaven Newhaven Display International newhaven Newhaven Display International
ni National Instruments ni National Instruments
nintendo Nintendo nintendo Nintendo
nlt NLT Technologies, Ltd.
nokia Nokia nokia Nokia
nordic Nordic Semiconductor nordic Nordic Semiconductor
nuvoton Nuvoton Technology Corporation nuvoton Nuvoton Technology Corporation
......
...@@ -143,6 +143,12 @@ Bridge Helper Reference ...@@ -143,6 +143,12 @@ Bridge Helper Reference
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c .. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:export: :export:
Panel-Bridge Helper Reference
-----------------------------
.. kernel-doc:: drivers/gpu/drm/bridge/panel.c
:export:
.. _drm_panel_helper: .. _drm_panel_helper:
Panel Helper Reference Panel Helper Reference
......
...@@ -35,6 +35,7 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ ...@@ -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_simple_kms_helper.o drm_modeset_helper.o \
drm_scdc_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_LOAD_EDID_FIRMWARE) += drm_edid_load.o
drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.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 drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
......
...@@ -23,139 +23,17 @@ ...@@ -23,139 +23,17 @@
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_of.h> #include <drm/drm_of.h>
#include <drm/drm_bridge.h>
#include "atmel_hlcdc_dc.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 = { static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = {
.destroy = atmel_hlcdc_rgb_encoder_destroy, .destroy = drm_encoder_cleanup,
};
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,
}; };
static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
{ {
struct atmel_hlcdc_dc *dc = dev->dev_private; struct drm_encoder *encoder;
struct atmel_hlcdc_rgb_output *output;
struct drm_panel *panel; struct drm_panel *panel;
struct drm_bridge *bridge; struct drm_bridge *bridge;
int ret; int ret;
...@@ -165,55 +43,34 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) ...@@ -165,55 +43,34 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
if (ret) if (ret)
return ret; return ret;
output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL); encoder = devm_kzalloc(dev->dev, sizeof(*encoder), GFP_KERNEL);
if (!output) if (!encoder)
return -EINVAL; return -EINVAL;
output->dc = dc; ret = drm_encoder_init(dev, encoder,
drm_encoder_helper_add(&output->encoder,
&atmel_hlcdc_panel_encoder_helper_funcs);
ret = drm_encoder_init(dev, &output->encoder,
&atmel_hlcdc_panel_encoder_funcs, &atmel_hlcdc_panel_encoder_funcs,
DRM_MODE_ENCODER_NONE, NULL); DRM_MODE_ENCODER_NONE, NULL);
if (ret) if (ret)
return ret; return ret;
output->encoder.possible_crtcs = 0x1; encoder->possible_crtcs = 0x1;
if (panel) { if (panel) {
output->connector.dpms = DRM_MODE_DPMS_OFF; bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown);
output->connector.polled = DRM_CONNECTOR_POLL_CONNECT; if (IS_ERR(bridge))
drm_connector_helper_add(&output->connector, return PTR_ERR(bridge);
&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;
} }
if (bridge) { if (bridge) {
ret = drm_bridge_attach(&output->encoder, bridge, NULL); ret = drm_bridge_attach(encoder, bridge, NULL);
if (!ret) if (!ret)
return 0; return 0;
if (panel)
drm_panel_bridge_remove(bridge);
} }
err_encoder_cleanup: drm_encoder_cleanup(encoder);
drm_encoder_cleanup(&output->encoder);
return ret; return ret;
} }
......
...@@ -4,6 +4,14 @@ config DRM_BRIDGE ...@@ -4,6 +4,14 @@ config DRM_BRIDGE
help help
Bridge registration and lookup framework. 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" menu "Display Interface Bridges"
depends on DRM && DRM_BRIDGE depends on DRM && DRM_BRIDGE
...@@ -27,8 +35,7 @@ config DRM_DUMB_VGA_DAC ...@@ -27,8 +35,7 @@ config DRM_DUMB_VGA_DAC
config DRM_LVDS_ENCODER config DRM_LVDS_ENCODER
tristate "Transparent parallel to LVDS encoder support" tristate "Transparent parallel to LVDS encoder support"
depends on OF depends on OF
select DRM_KMS_HELPER select DRM_PANEL_BRIDGE
select DRM_PANEL
help help
Support for transparent parallel to LVDS encoders that don't require Support for transparent parallel to LVDS encoders that don't require
any configuration. any configuration.
......
...@@ -8,144 +8,18 @@ ...@@ -8,144 +8,18 @@
*/ */
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.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> #include <drm/drm_panel.h>
#include <linux/of_graph.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) static int lvds_encoder_probe(struct platform_device *pdev)
{ {
struct lvds_encoder *lvds;
struct device_node *port; struct device_node *port;
struct device_node *endpoint; struct device_node *endpoint;
struct device_node *panel; struct device_node *panel_node;
struct drm_panel *panel;
lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); struct drm_bridge *bridge;
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;
/* Locate the panel DT node. */ /* Locate the panel DT node. */
port = of_graph_get_port_by_id(pdev->dev.of_node, 1); 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) ...@@ -161,29 +35,34 @@ static int lvds_encoder_probe(struct platform_device *pdev)
return -ENXIO; return -ENXIO;
} }
panel = of_graph_get_remote_port_parent(endpoint); panel_node = of_graph_get_remote_port_parent(endpoint);
of_node_put(endpoint); of_node_put(endpoint);
if (!panel) { if (!panel_node) {
dev_dbg(&pdev->dev, "no remote endpoint for port 1\n"); dev_dbg(&pdev->dev, "no remote endpoint for port 1\n");
return -ENXIO; return -ENXIO;
} }
lvds->panel = of_drm_find_panel(panel); panel = of_drm_find_panel(panel_node);
of_node_put(panel); of_node_put(panel_node);
if (!lvds->panel) { if (!panel) {
dev_dbg(&pdev->dev, "panel not found, deferring probe\n"); dev_dbg(&pdev->dev, "panel not found, deferring probe\n");
return -EPROBE_DEFER; return -EPROBE_DEFER;
} }
/* Register the bridge. */ bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_LVDS);
return drm_bridge_add(&lvds->bridge); if (IS_ERR(bridge))
return PTR_ERR(bridge);
platform_set_drvdata(pdev, bridge);
return 0;
} }
static int lvds_encoder_remove(struct platform_device *pdev) 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; 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) ...@@ -1907,24 +1907,6 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
return ret; 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) static void dw_hdmi_connector_force(struct drm_connector *connector)
{ {
struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
...@@ -1950,7 +1932,6 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = { ...@@ -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 = { static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
.get_modes = dw_hdmi_connector_get_modes, .get_modes = dw_hdmi_connector_get_modes,
.mode_valid = dw_hdmi_connector_mode_valid,
.best_encoder = drm_atomic_helper_best_encoder, .best_encoder = drm_atomic_helper_best_encoder,
}; };
...@@ -1973,18 +1954,22 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge) ...@@ -1973,18 +1954,22 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
return 0; return 0;
} }
static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, static enum drm_mode_status
const struct drm_display_mode *orig_mode, dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
struct drm_display_mode *mode) const struct drm_display_mode *mode)
{ {
struct dw_hdmi *hdmi = bridge->driver_private; struct dw_hdmi *hdmi = bridge->driver_private;
struct drm_connector *connector = &hdmi->connector; 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); /* We don't support double-clocked modes */
if (status != MODE_OK) if (mode->flags & DRM_MODE_FLAG_DBLCLK)
return false; return MODE_BAD;
return true;
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, 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 = { ...@@ -2028,7 +2013,7 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
.enable = dw_hdmi_bridge_enable, .enable = dw_hdmi_bridge_enable,
.disable = dw_hdmi_bridge_disable, .disable = dw_hdmi_bridge_disable,
.mode_set = dw_hdmi_bridge_mode_set, .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) static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)
......
...@@ -109,9 +109,10 @@ struct drm_atomic_state * ...@@ -109,9 +109,10 @@ struct drm_atomic_state *
drm_atomic_state_alloc(struct drm_device *dev) drm_atomic_state_alloc(struct drm_device *dev)
{ {
struct drm_mode_config *config = &dev->mode_config; struct drm_mode_config *config = &dev->mode_config;
struct drm_atomic_state *state;
if (!config->funcs->atomic_state_alloc) { if (!config->funcs->atomic_state_alloc) {
struct drm_atomic_state *state;
state = kzalloc(sizeof(*state), GFP_KERNEL); state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state) if (!state)
return NULL; return NULL;
......
...@@ -147,8 +147,9 @@ static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = { ...@@ -147,8 +147,9 @@ static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = {
.destroy = drm_encoder_cleanup, .destroy = drm_encoder_cleanup,
}; };
static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con, static enum drm_mode_status
struct drm_display_mode *mode) imx6q_hdmi_mode_valid(struct drm_connector *con,
const struct drm_display_mode *mode)
{ {
if (mode->clock < 13500) if (mode->clock < 13500)
return MODE_CLOCK_LOW; return MODE_CLOCK_LOW;
...@@ -159,8 +160,9 @@ static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con, ...@@ -159,8 +160,9 @@ static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con,
return MODE_OK; return MODE_OK;
} }
static enum drm_mode_status imx6dl_hdmi_mode_valid(struct drm_connector *con, static enum drm_mode_status
struct drm_display_mode *mode) imx6dl_hdmi_mode_valid(struct drm_connector *con,
const struct drm_display_mode *mode)
{ {
if (mode->clock < 13500) if (mode->clock < 13500)
return MODE_CLOCK_LOW; return MODE_CLOCK_LOW;
......
...@@ -536,8 +536,9 @@ static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id) ...@@ -536,8 +536,9 @@ static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id)
} }
/* TOFIX Enable support for non-vic modes */ /* TOFIX Enable support for non-vic modes */
static enum drm_mode_status dw_hdmi_mode_valid(struct drm_connector *connector, static enum drm_mode_status
struct drm_display_mode *mode) dw_hdmi_mode_valid(struct drm_connector *connector,
const struct drm_display_mode *mode)
{ {
unsigned int vclk_freq; unsigned int vclk_freq;
unsigned int venc_freq; unsigned int venc_freq;
......
...@@ -35,6 +35,13 @@ ...@@ -35,6 +35,13 @@
#include "mxsfb_drv.h" #include "mxsfb_drv.h"
#include "mxsfb_regs.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) static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
{ {
return (val & mxsfb->devdata->hs_wdth_mask) << return (val & mxsfb->devdata->hs_wdth_mask) <<
...@@ -159,6 +166,36 @@ static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb) ...@@ -159,6 +166,36 @@ static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
clk_disable_unprepare(mxsfb->clk_disp_axi); 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) static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
{ {
struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode; 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) ...@@ -173,6 +210,11 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
*/ */
mxsfb_enable_axi_clk(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 */ /* Clear the FIFOs */
writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET); writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
......
...@@ -28,6 +28,17 @@ config DRM_PANEL_SIMPLE ...@@ -28,6 +28,17 @@ config DRM_PANEL_SIMPLE
that it can be automatically turned off when the panel goes into a that it can be automatically turned off when the panel goes into a
low power state. 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 config DRM_PANEL_JDI_LT070ME05000
tristate "JDI LT070ME05000 WUXGA DSI panel" tristate "JDI LT070ME05000 WUXGA DSI panel"
depends on OF depends on OF
...@@ -66,6 +77,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA2 ...@@ -66,6 +77,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA2
tristate "Samsung S6E3HA2 DSI video mode panel" tristate "Samsung S6E3HA2 DSI video mode panel"
depends on OF depends on OF
depends on DRM_MIPI_DSI depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select VIDEOMODE_HELPERS select VIDEOMODE_HELPERS
config DRM_PANEL_SAMSUNG_S6E8AA0 config DRM_PANEL_SAMSUNG_S6E8AA0
...@@ -100,6 +112,7 @@ config DRM_PANEL_SHARP_LS043T1LE01 ...@@ -100,6 +112,7 @@ config DRM_PANEL_SHARP_LS043T1LE01
config DRM_PANEL_SITRONIX_ST7789V config DRM_PANEL_SITRONIX_ST7789V
tristate "Sitronix ST7789V panel" tristate "Sitronix ST7789V panel"
depends on OF && SPI depends on OF && SPI
depends on BACKLIGHT_CLASS_DEVICE
help help
Say Y here if you want to enable support for the Sitronix Say Y here if you want to enable support for the Sitronix
ST7789V controller for 240x320 LCD panels ST7789V controller for 240x320 LCD panels
......
obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.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_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.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 @@ ...@@ -16,6 +16,7 @@
#include <drm/drm_panel.h> #include <drm/drm_panel.h>
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#define S6E3HA2_MIN_BRIGHTNESS 0 #define S6E3HA2_MIN_BRIGHTNESS 0
...@@ -218,6 +219,16 @@ unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = { ...@@ -218,6 +219,16 @@ unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = {
0x1d, 0x1e, 0x1f, 0x20, 0x21 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 s6e3ha2 {
struct device *dev; struct device *dev;
struct drm_panel panel; struct drm_panel panel;
...@@ -226,6 +237,8 @@ struct s6e3ha2 { ...@@ -226,6 +237,8 @@ struct s6e3ha2 {
struct regulator_bulk_data supplies[2]; struct regulator_bulk_data supplies[2];
struct gpio_desc *reset_gpio; struct gpio_desc *reset_gpio;
struct gpio_desc *enable_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) 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) ...@@ -283,11 +296,21 @@ static int s6e3ha2_single_dsi_set(struct s6e3ha2 *ctx)
static int s6e3ha2_freq_calibration(struct s6e3ha2 *ctx) static int s6e3ha2_freq_calibration(struct s6e3ha2 *ctx)
{ {
s6e3ha2_dcs_write_seq_static(ctx, 0xfd, 0x1c); 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, 0x20, 0x39);
s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0); s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0);
s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20); 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; return 0;
} }
...@@ -583,7 +606,7 @@ static int s6e3ha2_enable(struct drm_panel *panel) ...@@ -583,7 +606,7 @@ static int s6e3ha2_enable(struct drm_panel *panel)
return 0; return 0;
} }
static const struct drm_display_mode default_mode = { static const struct drm_display_mode s6e3ha2_mode = {
.clock = 222372, .clock = 222372,
.hdisplay = 1440, .hdisplay = 1440,
.hsync_start = 1440 + 1, .hsync_start = 1440 + 1,
...@@ -597,16 +620,41 @@ static const struct drm_display_mode default_mode = { ...@@ -597,16 +620,41 @@ static const struct drm_display_mode default_mode = {
.flags = 0, .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) static int s6e3ha2_get_modes(struct drm_panel *panel)
{ {
struct drm_connector *connector = panel->connector; struct drm_connector *connector = panel->connector;
struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
struct drm_display_mode *mode; struct drm_display_mode *mode;
mode = drm_mode_duplicate(panel->drm, &default_mode); mode = drm_mode_duplicate(panel->drm, ctx->desc->mode);
if (!mode) { if (!mode) {
DRM_ERROR("failed to add mode %ux%ux@%u\n", DRM_ERROR("failed to add mode %ux%ux@%u\n",
default_mode.hdisplay, default_mode.vdisplay, ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
default_mode.vrefresh); ctx->desc->mode->vrefresh);
return -ENOMEM; return -ENOMEM;
} }
...@@ -642,6 +690,7 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi) ...@@ -642,6 +690,7 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
mipi_dsi_set_drvdata(dsi, ctx); mipi_dsi_set_drvdata(dsi, ctx);
ctx->dev = dev; ctx->dev = dev;
ctx->desc = of_device_get_match_data(dev);
dsi->lanes = 4; dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888; dsi->format = MIPI_DSI_FMT_RGB888;
...@@ -717,7 +766,8 @@ static int s6e3ha2_remove(struct mipi_dsi_device *dsi) ...@@ -717,7 +766,8 @@ static int s6e3ha2_remove(struct mipi_dsi_device *dsi)
} }
static const struct of_device_id s6e3ha2_of_match[] = { 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); MODULE_DEVICE_TABLE(of, s6e3ha2_of_match);
......
...@@ -638,6 +638,34 @@ static const struct panel_desc auo_g185han01 = { ...@@ -638,6 +638,34 @@ static const struct panel_desc auo_g185han01 = {
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, .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 = { static const struct drm_display_mode auo_t215hvn01_mode = {
.clock = 148800, .clock = 148800,
.hdisplay = 1920, .hdisplay = 1920,
...@@ -1322,6 +1350,33 @@ static const struct panel_desc lg_lp129qe = { ...@@ -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 = { static const struct drm_display_mode nec_nl4827hc19_05b_mode = {
.clock = 10870, .clock = 10870,
.hdisplay = 480, .hdisplay = 480,
...@@ -1371,6 +1426,32 @@ static const struct panel_desc netron_dy_e231732 = { ...@@ -1371,6 +1426,32 @@ static const struct panel_desc netron_dy_e231732 = {
.bus_format = MEDIA_BUS_FMT_RGB666_1X18, .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 = { static const struct drm_display_mode nvd_9128_mode = {
.clock = 29500, .clock = 29500,
.hdisplay = 800, .hdisplay = 800,
...@@ -1887,6 +1968,9 @@ static const struct of_device_id platform_of_match[] = { ...@@ -1887,6 +1968,9 @@ static const struct of_device_id platform_of_match[] = {
}, { }, {
.compatible = "auo,g185han01", .compatible = "auo,g185han01",
.data = &auo_g185han01, .data = &auo_g185han01,
}, {
.compatible = "auo,p320hvn03",
.data = &auo_p320hvn03,
}, { }, {
.compatible = "auo,t215hvn01", .compatible = "auo,t215hvn01",
.data = &auo_t215hvn01, .data = &auo_t215hvn01,
...@@ -1971,12 +2055,18 @@ static const struct of_device_id platform_of_match[] = { ...@@ -1971,12 +2055,18 @@ static const struct of_device_id platform_of_match[] = {
}, { }, {
.compatible = "lg,lp129qe", .compatible = "lg,lp129qe",
.data = &lg_lp129qe, .data = &lg_lp129qe,
}, {
.compatible = "nec,nl12880bc20-05",
.data = &nec_nl12880bc20_05,
}, { }, {
.compatible = "nec,nl4827hc19-05b", .compatible = "nec,nl4827hc19-05b",
.data = &nec_nl4827hc19_05b, .data = &nec_nl4827hc19_05b,
}, { }, {
.compatible = "netron-dy,e231732", .compatible = "netron-dy,e231732",
.data = &netron_dy_e231732, .data = &netron_dy_e231732,
}, {
.compatible = "nlt,nl192108ac18-02d",
.data = &nlt_nl192108ac18_02d,
}, { }, {
.compatible = "nvd,9128", .compatible = "nvd,9128",
.data = &nvd_9128, .data = &nvd_9128,
......
...@@ -50,17 +50,6 @@ irqreturn_t pl111_irq(int irq, void *data) ...@@ -50,17 +50,6 @@ irqreturn_t pl111_irq(int irq, void *data)
return status; 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, static int pl111_display_check(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *pstate, struct drm_plane_state *pstate,
struct drm_crtc_state *cstate) struct drm_crtc_state *cstate)
...@@ -73,7 +62,7 @@ static int pl111_display_check(struct drm_simple_display_pipe *pipe, ...@@ -73,7 +62,7 @@ static int pl111_display_check(struct drm_simple_display_pipe *pipe,
return -EINVAL; return -EINVAL;
if (fb) { 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. */ /* FB base address must be dword aligned. */
if (offset & 3) if (offset & 3)
...@@ -249,7 +238,7 @@ static void pl111_display_update(struct drm_simple_display_pipe *pipe, ...@@ -249,7 +238,7 @@ static void pl111_display_update(struct drm_simple_display_pipe *pipe,
struct drm_framebuffer *fb = pstate->fb; struct drm_framebuffer *fb = pstate->fb;
if (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); writel(addr, priv->regs + CLCD_UBAS);
} }
......
...@@ -155,7 +155,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) ...@@ -155,7 +155,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
static enum drm_mode_status static enum drm_mode_status
dw_hdmi_rockchip_mode_valid(struct drm_connector *connector, 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; const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
int pclk = mode->clock * 1000; int pclk = mode->clock * 1000;
......
...@@ -7,7 +7,7 @@ config DRM_VC4 ...@@ -7,7 +7,7 @@ config DRM_VC4
select DRM_KMS_HELPER select DRM_KMS_HELPER
select DRM_KMS_CMA_HELPER select DRM_KMS_CMA_HELPER
select DRM_GEM_CMA_HELPER select DRM_GEM_CMA_HELPER
select DRM_PANEL select DRM_PANEL_BRIDGE
select SND_PCM select SND_PCM
select SND_PCM_ELD select SND_PCM_ELD
select SND_SOC_GENERIC_DMAENGINE_PCM select SND_SOC_GENERIC_DMAENGINE_PCM
......
...@@ -91,8 +91,7 @@ static void vc4_bo_destroy(struct vc4_bo *bo) ...@@ -91,8 +91,7 @@ static void vc4_bo_destroy(struct vc4_bo *bo)
vc4->bo_stats.num_allocated--; vc4->bo_stats.num_allocated--;
vc4->bo_stats.size_allocated -= obj->size; 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); drm_gem_cma_free_object(obj);
} }
...@@ -212,6 +211,8 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) ...@@ -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.num_allocated++;
vc4->bo_stats.size_allocated += size; vc4->bo_stats.size_allocated += size;
mutex_unlock(&vc4->bo_lock); mutex_unlock(&vc4->bo_lock);
bo->resv = &bo->_resv;
reservation_object_init(bo->resv);
return &bo->base.base; return &bo->base.base;
} }
...@@ -250,12 +251,7 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size, ...@@ -250,12 +251,7 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
} }
bo = to_vc4_bo(&cma_obj->base); return to_vc4_bo(&cma_obj->base);
bo->resv = &bo->_resv;
reservation_object_init(bo->resv);
return bo;
} }
int vc4_dumb_create(struct drm_file *file_priv, int vc4_dumb_create(struct drm_file *file_priv,
......
...@@ -23,8 +23,10 @@ ...@@ -23,8 +23,10 @@
*/ */
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h> #include <drm/drm_panel.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/component.h> #include <linux/component.h>
...@@ -95,7 +97,8 @@ struct vc4_dpi { ...@@ -95,7 +97,8 @@ struct vc4_dpi {
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_connector *connector; struct drm_connector *connector;
struct drm_panel *panel; struct drm_bridge *bridge;
bool is_panel_bridge;
void __iomem *regs; void __iomem *regs;
...@@ -118,24 +121,6 @@ to_vc4_dpi_encoder(struct drm_encoder *encoder) ...@@ -118,24 +121,6 @@ to_vc4_dpi_encoder(struct drm_encoder *encoder)
return container_of(encoder, struct vc4_dpi_encoder, base.base); 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 } #define DPI_REG(reg) { reg, #reg }
static const struct { static const struct {
u32 reg; u32 reg;
...@@ -167,80 +152,6 @@ int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused) ...@@ -167,80 +152,6 @@ int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused)
} }
#endif #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 = { static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = {
.destroy = drm_encoder_cleanup, .destroy = drm_encoder_cleanup,
}; };
...@@ -250,11 +161,7 @@ static void vc4_dpi_encoder_disable(struct drm_encoder *encoder) ...@@ -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_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder);
struct vc4_dpi *dpi = vc4_encoder->dpi; struct vc4_dpi *dpi = vc4_encoder->dpi;
drm_panel_disable(dpi->panel);
clk_disable_unprepare(dpi->pixel_clock); clk_disable_unprepare(dpi->pixel_clock);
drm_panel_unprepare(dpi->panel);
} }
static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
...@@ -265,12 +172,6 @@ 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; u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE;
int ret; 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) { if (dpi->connector->display_info.num_bus_formats) {
u32 bus_format = dpi->connector->display_info.bus_formats[0]; u32 bus_format = dpi->connector->display_info.bus_formats[0];
...@@ -321,13 +222,6 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) ...@@ -321,13 +222,6 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
ret = clk_prepare_enable(dpi->pixel_clock); ret = clk_prepare_enable(dpi->pixel_clock);
if (ret) if (ret)
DRM_ERROR("Failed to set clock rate: %d\n", 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, 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[] = { ...@@ -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 /* Sets up the next link in the display chain, whether it's a panel or
* up the panel. * 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 *dev = &dpi->pdev->dev;
struct device_node *np = dev->of_node;
struct drm_panel *panel; struct drm_panel *panel;
int ret;
/* don't proceed if we have an endpoint but no panel_node tied to it */ ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
panel_node = of_graph_get_remote_node(np, 0, 0); &panel, &dpi->bridge);
if (!panel_node) if (ret) {
return NULL; /* 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); if (panel) {
of_node_put(panel_node); 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) 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) ...@@ -422,20 +326,13 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
if (ret) if (ret)
DRM_ERROR("Failed to turn on core clock: %d\n", 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_encoder_init(drm, dpi->encoder, &vc4_dpi_encoder_funcs,
DRM_MODE_ENCODER_DPI, NULL); DRM_MODE_ENCODER_DPI, NULL);
drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs); drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs);
dpi->connector = vc4_dpi_connector_init(drm, dpi); ret = vc4_dpi_init_bridge(dpi);
if (IS_ERR(dpi->connector)) { if (ret)
ret = PTR_ERR(dpi->connector);
goto err_destroy_encoder; goto err_destroy_encoder;
}
if (dpi->panel)
drm_panel_attach(dpi->panel, dpi->connector);
dev_set_drvdata(dev, dpi); dev_set_drvdata(dev, dpi);
...@@ -456,10 +353,9 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master, ...@@ -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_dev *vc4 = to_vc4_dev(drm);
struct vc4_dpi *dpi = dev_get_drvdata(dev); struct vc4_dpi *dpi = dev_get_drvdata(dev);
if (dpi->panel) if (dpi->is_panel_bridge)
drm_panel_detach(dpi->panel); drm_panel_bridge_remove(dpi->bridge);
vc4_dpi_connector_destroy(dpi->connector);
drm_encoder_cleanup(dpi->encoder); drm_encoder_cleanup(dpi->encoder);
clk_disable_unprepare(dpi->core_clock); clk_disable_unprepare(dpi->core_clock);
......
...@@ -503,8 +503,8 @@ struct vc4_dsi { ...@@ -503,8 +503,8 @@ struct vc4_dsi {
struct mipi_dsi_host dsi_host; struct mipi_dsi_host dsi_host;
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_connector *connector; struct drm_bridge *bridge;
struct drm_panel *panel; bool is_panel_bridge;
void __iomem *regs; void __iomem *regs;
...@@ -605,18 +605,6 @@ to_vc4_dsi_encoder(struct drm_encoder *encoder) ...@@ -605,18 +605,6 @@ to_vc4_dsi_encoder(struct drm_encoder *encoder)
return container_of(encoder, struct vc4_dsi_encoder, base.base); 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 } #define DSI_REG(reg) { reg, #reg }
static const struct { static const struct {
u32 reg; u32 reg;
...@@ -724,79 +712,6 @@ int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused) ...@@ -724,79 +712,6 @@ int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused)
} }
#endif #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) static void vc4_dsi_encoder_destroy(struct drm_encoder *encoder)
{ {
drm_encoder_cleanup(encoder); drm_encoder_cleanup(encoder);
...@@ -894,12 +809,8 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) ...@@ -894,12 +809,8 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
struct vc4_dsi *dsi = vc4_encoder->dsi; struct vc4_dsi *dsi = vc4_encoder->dsi;
struct device *dev = &dsi->pdev->dev; struct device *dev = &dsi->pdev->dev;
drm_panel_disable(dsi->panel);
vc4_dsi_ulps(dsi, true); vc4_dsi_ulps(dsi, true);
drm_panel_unprepare(dsi->panel);
clk_disable_unprepare(dsi->pll_phy_clock); clk_disable_unprepare(dsi->pll_phy_clock);
clk_disable_unprepare(dsi->escape_clock); clk_disable_unprepare(dsi->escape_clock);
clk_disable_unprepare(dsi->pixel_clock); clk_disable_unprepare(dsi->pixel_clock);
...@@ -984,12 +895,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) ...@@ -984,12 +895,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
return; return;
} }
ret = drm_panel_prepare(dsi->panel);
if (ret) {
DRM_ERROR("Panel failed to prepare\n");
return;
}
if (debug_dump_regs) { if (debug_dump_regs) {
DRM_INFO("DSI regs before:\n"); DRM_INFO("DSI regs before:\n");
vc4_dsi_dump_regs(dsi); vc4_dsi_dump_regs(dsi);
...@@ -1211,13 +1116,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) ...@@ -1211,13 +1116,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
DRM_INFO("DSI regs after:\n"); DRM_INFO("DSI regs after:\n");
vc4_dsi_dump_regs(dsi); 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, 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, ...@@ -1415,17 +1313,22 @@ static int vc4_dsi_host_attach(struct mipi_dsi_host *host,
return 0; return 0;
} }
dsi->panel = of_drm_find_panel(device->dev.of_node); dsi->bridge = of_drm_find_bridge(device->dev.of_node);
if (!dsi->panel) if (!dsi->bridge) {
return 0; 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;
drm_helper_hpd_irq_event(dsi->connector->dev); 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;
}
return 0; return drm_bridge_attach(dsi->encoder, dsi->bridge, NULL);
} }
static int vc4_dsi_host_detach(struct mipi_dsi_host *host, 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, ...@@ -1433,15 +1336,9 @@ static int vc4_dsi_host_detach(struct mipi_dsi_host *host,
{ {
struct vc4_dsi *dsi = host_to_dsi(host); struct vc4_dsi *dsi = host_to_dsi(host);
if (dsi->panel) { if (dsi->is_panel_bridge) {
int ret = drm_panel_detach(dsi->panel); drm_panel_bridge_remove(dsi->bridge);
dsi->bridge = NULL;
if (ret)
return ret;
dsi->panel = NULL;
drm_helper_hpd_irq_event(dsi->connector->dev);
} }
return 0; return 0;
...@@ -1708,12 +1605,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) ...@@ -1708,12 +1605,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
DRM_MODE_ENCODER_DSI, NULL); DRM_MODE_ENCODER_DSI, NULL);
drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs); 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.ops = &vc4_dsi_host_ops;
dsi->dsi_host.dev = dev; dsi->dsi_host.dev = dev;
...@@ -1724,11 +1615,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) ...@@ -1724,11 +1615,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
pm_runtime_enable(dev); pm_runtime_enable(dev);
return 0; return 0;
err_destroy_encoder:
vc4_dsi_encoder_destroy(dsi->encoder);
return ret;
} }
static void vc4_dsi_unbind(struct device *dev, struct device *master, 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, ...@@ -1740,7 +1626,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
pm_runtime_disable(dev); pm_runtime_disable(dev);
vc4_dsi_connector_destroy(dsi->connector); drm_bridge_remove(dsi->bridge);
vc4_dsi_encoder_destroy(dsi->encoder); vc4_dsi_encoder_destroy(dsi->encoder);
mipi_dsi_host_unregister(&dsi->dsi_host); mipi_dsi_host_unregister(&dsi->dsi_host);
......
...@@ -111,8 +111,8 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, ...@@ -111,8 +111,8 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
&handle); &handle);
if (ret) { if (ret) {
state->bo_count = i - 1; state->bo_count = i;
goto err; goto err_delete_handle;
} }
bo_state[i].handle = handle; bo_state[i].handle = handle;
bo_state[i].paddr = vc4_bo->base.paddr; bo_state[i].paddr = vc4_bo->base.paddr;
...@@ -124,13 +124,16 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, ...@@ -124,13 +124,16 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
state->bo_count * sizeof(*bo_state))) state->bo_count * sizeof(*bo_state)))
ret = -EFAULT; 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: err_free:
vc4_free_hang_state(dev, kernel_state); vc4_free_hang_state(dev, kernel_state);
kfree(bo_state);
err:
return ret; return ret;
} }
......
...@@ -196,7 +196,7 @@ static int zx_drm_probe(struct platform_device *pdev) ...@@ -196,7 +196,7 @@ static int zx_drm_probe(struct platform_device *pdev)
struct component_match *match = NULL; struct component_match *match = NULL;
int ret; int ret;
ret = of_platform_populate(parent, NULL, NULL, dev); ret = devm_of_platform_populate(dev);
if (ret) if (ret)
return ret; return ret;
......
...@@ -125,7 +125,7 @@ struct dw_hdmi_phy_ops { ...@@ -125,7 +125,7 @@ struct dw_hdmi_phy_ops {
struct dw_hdmi_plat_data { struct dw_hdmi_plat_data {
struct regmap *regm; struct regmap *regm;
enum drm_mode_status (*mode_valid)(struct drm_connector *connector, 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_format;
unsigned long input_bus_encoding; unsigned long input_bus_encoding;
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <drm/drm_modes.h> #include <drm/drm_modes.h>
struct drm_bridge; struct drm_bridge;
struct drm_panel;
/** /**
* struct drm_bridge_funcs - drm_bridge control functions * struct drm_bridge_funcs - drm_bridge control functions
...@@ -263,4 +264,10 @@ void drm_bridge_mode_set(struct drm_bridge *bridge, ...@@ -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_pre_enable(struct drm_bridge *bridge);
void drm_bridge_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 #endif
...@@ -30,8 +30,6 @@ ...@@ -30,8 +30,6 @@
#include <uapi/drm/drm_mode.h> #include <uapi/drm/drm_mode.h>
struct drm_device;
struct drm_connector_helper_funcs; struct drm_connector_helper_funcs;
struct drm_modeset_acquire_ctx; struct drm_modeset_acquire_ctx;
struct drm_device; struct drm_device;
......
...@@ -214,7 +214,7 @@ struct drm_property_blob { ...@@ -214,7 +214,7 @@ struct drm_property_blob {
struct drm_prop_enum_list { struct drm_prop_enum_list {
int type; int type;
char *name; const char *name;
}; };
#define obj_to_property(x) container_of(x, struct drm_property, base) #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