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. ...tirimbino
commit
bfda9aa153
@ -0,0 +1,8 @@ |
||||
AU Optronics Corporation 31.5" FHD (1920x1080) TFT LCD panel |
||||
|
||||
Required properties: |
||||
- compatible: should be "auo,p320hvn03" |
||||
- power-supply: as specified in the base binding |
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified |
||||
in simple-panel.txt in this directory. |
@ -0,0 +1,23 @@ |
||||
Innolux P079ZCA 7.85" 768x1024 TFT LCD panel |
||||
|
||||
Required properties: |
||||
- compatible: should be "innolux,p079zca" |
||||
- reg: DSI virtual channel of the peripheral |
||||
- power-supply: phandle of the regulator that provides the supply voltage |
||||
- enable-gpios: panel enable gpio |
||||
|
||||
Optional properties: |
||||
- backlight: phandle of the backlight device attached to the panel |
||||
|
||||
Example: |
||||
|
||||
&mipi_dsi { |
||||
panel { |
||||
compatible = "innolux,p079zca"; |
||||
reg = <0>; |
||||
power-supply = <...>; |
||||
backlight = <&backlight>; |
||||
enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; |
||||
status = "okay"; |
||||
}; |
||||
}; |
@ -0,0 +1,8 @@ |
||||
NEC LCD Technologies, Ltd. 12.1" WXGA (1280x800) LVDS TFT LCD panel |
||||
|
||||
Required properties: |
||||
- compatible: should be "nec,nl12880bc20-05" |
||||
- power-supply: as specified in the base binding |
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified |
||||
in simple-panel.txt in this directory. |
@ -0,0 +1,8 @@ |
||||
NLT Technologies, Ltd. 15.6" FHD (1920x1080) LVDS TFT LCD panel |
||||
|
||||
Required properties: |
||||
- compatible: should be "nlt,nl192108ac18-02d" |
||||
- power-supply: as specified in the base binding |
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified |
||||
in simple-panel.txt in this directory. |
@ -0,0 +1,200 @@ |
||||
/*
|
||||
* Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
||||
* Copyright (C) 2017 Broadcom |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of |
||||
* the License, or (at your option) any later version. |
||||
*/ |
||||
|
||||
#include <drm/drmP.h> |
||||
#include <drm/drm_panel.h> |
||||
#include <drm/drm_atomic_helper.h> |
||||
#include <drm/drm_connector.h> |
||||
#include <drm/drm_crtc_helper.h> |
||||
#include <drm/drm_encoder.h> |
||||
#include <drm/drm_modeset_helper_vtables.h> |
||||
#include <drm/drm_panel.h> |
||||
|
||||
struct panel_bridge { |
||||
struct drm_bridge bridge; |
||||
struct drm_connector connector; |
||||
struct drm_panel *panel; |
||||
u32 connector_type; |
||||
}; |
||||
|
||||
static inline struct panel_bridge * |
||||
drm_bridge_to_panel_bridge(struct drm_bridge *bridge) |
||||
{ |
||||
return container_of(bridge, struct panel_bridge, bridge); |
||||
} |
||||
|
||||
static inline struct panel_bridge * |
||||
drm_connector_to_panel_bridge(struct drm_connector *connector) |
||||
{ |
||||
return container_of(connector, struct panel_bridge, connector); |
||||
} |
||||
|
||||
static int panel_bridge_connector_get_modes(struct drm_connector *connector) |
||||
{ |
||||
struct panel_bridge *panel_bridge = |
||||
drm_connector_to_panel_bridge(connector); |
||||
|
||||
return drm_panel_get_modes(panel_bridge->panel); |
||||
} |
||||
|
||||
static const struct drm_connector_helper_funcs |
||||
panel_bridge_connector_helper_funcs = { |
||||
.get_modes = panel_bridge_connector_get_modes, |
||||
}; |
||||
|
||||
static const struct drm_connector_funcs panel_bridge_connector_funcs = { |
||||
.dpms = drm_atomic_helper_connector_dpms, |
||||
.reset = drm_atomic_helper_connector_reset, |
||||
.fill_modes = drm_helper_probe_single_connector_modes, |
||||
.destroy = drm_connector_cleanup, |
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
||||
}; |
||||
|
||||
static int panel_bridge_attach(struct drm_bridge *bridge) |
||||
{ |
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); |
||||
struct drm_connector *connector = &panel_bridge->connector; |
||||
int ret; |
||||
|
||||
if (!bridge->encoder) { |
||||
DRM_ERROR("Missing encoder\n"); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
drm_connector_helper_add(connector, |
||||
&panel_bridge_connector_helper_funcs); |
||||
|
||||
ret = drm_connector_init(bridge->dev, connector, |
||||
&panel_bridge_connector_funcs, |
||||
panel_bridge->connector_type); |
||||
if (ret) { |
||||
DRM_ERROR("Failed to initialize connector\n"); |
||||
return ret; |
||||
} |
||||
|
||||
drm_mode_connector_attach_encoder(&panel_bridge->connector, |
||||
bridge->encoder); |
||||
|
||||
ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void panel_bridge_detach(struct drm_bridge *bridge) |
||||
{ |
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); |
||||
|
||||
drm_panel_detach(panel_bridge->panel); |
||||
} |
||||
|
||||
static void panel_bridge_pre_enable(struct drm_bridge *bridge) |
||||
{ |
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); |
||||
|
||||
drm_panel_prepare(panel_bridge->panel); |
||||
} |
||||
|
||||
static void panel_bridge_enable(struct drm_bridge *bridge) |
||||
{ |
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); |
||||
|
||||
drm_panel_enable(panel_bridge->panel); |
||||
} |
||||
|
||||
static void panel_bridge_disable(struct drm_bridge *bridge) |
||||
{ |
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); |
||||
|
||||
drm_panel_disable(panel_bridge->panel); |
||||
} |
||||
|
||||
static void panel_bridge_post_disable(struct drm_bridge *bridge) |
||||
{ |
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); |
||||
|
||||
drm_panel_unprepare(panel_bridge->panel); |
||||
} |
||||
|
||||
static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { |
||||
.attach = panel_bridge_attach, |
||||
.detach = panel_bridge_detach, |
||||
.pre_enable = panel_bridge_pre_enable, |
||||
.enable = panel_bridge_enable, |
||||
.disable = panel_bridge_disable, |
||||
.post_disable = panel_bridge_post_disable, |
||||
}; |
||||
|
||||
/**
|
||||
* drm_panel_bridge_add - Creates a drm_bridge and drm_connector that |
||||
* just calls the appropriate functions from drm_panel. |
||||
* |
||||
* @panel: The drm_panel being wrapped. Must be non-NULL. |
||||
* @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be |
||||
* created. |
||||
* |
||||
* For drivers converting from directly using drm_panel: The expected |
||||
* usage pattern is that during either encoder module probe or DSI |
||||
* host attach, a drm_panel will be looked up through |
||||
* drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to |
||||
* wrap that panel in the new bridge, and the result can then be |
||||
* passed to drm_bridge_attach(). The drm_panel_prepare() and related |
||||
* functions can be dropped from the encoder driver (they're now |
||||
* called by the KMS helpers before calling into the encoder), along |
||||
* with connector creation. When done with the bridge, |
||||
* drm_bridge_detach() should be called as normal, then |
||||
* drm_panel_bridge_remove() to free it. |
||||
*/ |
||||
struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel, |
||||
u32 connector_type) |
||||
{ |
||||
struct panel_bridge *panel_bridge; |
||||
int ret; |
||||
|
||||
if (!panel) |
||||
return ERR_PTR(EINVAL); |
||||
|
||||
panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge), |
||||
GFP_KERNEL); |
||||
if (!panel_bridge) |
||||
return ERR_PTR(-ENOMEM); |
||||
|
||||
panel_bridge->connector_type = connector_type; |
||||
panel_bridge->panel = panel; |
||||
|
||||
panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs; |
||||
#ifdef CONFIG_OF |
||||
panel_bridge->bridge.of_node = panel->dev->of_node; |
||||
#endif |
||||
|
||||
ret = drm_bridge_add(&panel_bridge->bridge); |
||||
if (ret) |
||||
return ERR_PTR(ret); |
||||
|
||||
return &panel_bridge->bridge; |
||||
} |
||||
EXPORT_SYMBOL(drm_panel_bridge_add); |
||||
|
||||
/**
|
||||
* drm_panel_bridge_remove - Unregisters and frees a drm_bridge |
||||
* created by drm_panel_bridge_add(). |
||||
* |
||||
* @bridge: The drm_bridge being freed. |
||||
*/ |
||||
void drm_panel_bridge_remove(struct drm_bridge *bridge) |
||||
{ |
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); |
||||
|
||||
drm_bridge_remove(bridge); |
||||
devm_kfree(panel_bridge->panel->dev, bridge); |
||||
} |
||||
EXPORT_SYMBOL(drm_panel_bridge_remove); |
@ -0,0 +1,340 @@ |
||||
/*
|
||||
* Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License as published by |
||||
* the Free Software Foundation; either version 2 of the License, or |
||||
* (at your option) any later version. |
||||
*/ |
||||
|
||||
#include <linux/backlight.h> |
||||
#include <linux/gpio/consumer.h> |
||||
#include <linux/module.h> |
||||
#include <linux/of.h> |
||||
#include <linux/regulator/consumer.h> |
||||
|
||||
#include <drm/drmP.h> |
||||
#include <drm/drm_crtc.h> |
||||
#include <drm/drm_mipi_dsi.h> |
||||
#include <drm/drm_panel.h> |
||||
|
||||
#include <video/mipi_display.h> |
||||
|
||||
struct innolux_panel { |
||||
struct drm_panel base; |
||||
struct mipi_dsi_device *link; |
||||
|
||||
struct backlight_device *backlight; |
||||
struct regulator *supply; |
||||
struct gpio_desc *enable_gpio; |
||||
|
||||
bool prepared; |
||||
bool enabled; |
||||
}; |
||||
|
||||
static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel) |
||||
{ |
||||
return container_of(panel, struct innolux_panel, base); |
||||
} |
||||
|
||||
static int innolux_panel_disable(struct drm_panel *panel) |
||||
{ |
||||
struct innolux_panel *innolux = to_innolux_panel(panel); |
||||
int err; |
||||
|
||||
if (!innolux->enabled) |
||||
return 0; |
||||
|
||||
innolux->backlight->props.power = FB_BLANK_POWERDOWN; |
||||
backlight_update_status(innolux->backlight); |
||||
|
||||
err = mipi_dsi_dcs_set_display_off(innolux->link); |
||||
if (err < 0) |
||||
DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n", |
||||
err); |
||||
|
||||
innolux->enabled = false; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int innolux_panel_unprepare(struct drm_panel *panel) |
||||
{ |
||||
struct innolux_panel *innolux = to_innolux_panel(panel); |
||||
int err; |
||||
|
||||
if (!innolux->prepared) |
||||
return 0; |
||||
|
||||
err = mipi_dsi_dcs_enter_sleep_mode(innolux->link); |
||||
if (err < 0) { |
||||
DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n", |
||||
err); |
||||
return err; |
||||
} |
||||
|
||||
gpiod_set_value_cansleep(innolux->enable_gpio, 0); |
||||
|
||||
/* T8: 80ms - 1000ms */ |
||||
msleep(80); |
||||
|
||||
err = regulator_disable(innolux->supply); |
||||
if (err < 0) |
||||
return err; |
||||
|
||||
innolux->prepared = false; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int innolux_panel_prepare(struct drm_panel *panel) |
||||
{ |
||||
struct innolux_panel *innolux = to_innolux_panel(panel); |
||||
int err, regulator_err; |
||||
|
||||
if (innolux->prepared) |
||||
return 0; |
||||
|
||||
gpiod_set_value_cansleep(innolux->enable_gpio, 0); |
||||
|
||||
err = regulator_enable(innolux->supply); |
||||
if (err < 0) |
||||
return err; |
||||
|
||||
/* T2: 15ms - 1000ms */ |
||||
usleep_range(15000, 16000); |
||||
|
||||
gpiod_set_value_cansleep(innolux->enable_gpio, 1); |
||||
|
||||
/* T4: 15ms - 1000ms */ |
||||
usleep_range(15000, 16000); |
||||
|
||||
err = mipi_dsi_dcs_exit_sleep_mode(innolux->link); |
||||
if (err < 0) { |
||||
DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n", |
||||
err); |
||||
goto poweroff; |
||||
} |
||||
|
||||
/* T6: 120ms - 1000ms*/ |
||||
msleep(120); |
||||
|
||||
err = mipi_dsi_dcs_set_display_on(innolux->link); |
||||
if (err < 0) { |
||||
DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n", |
||||
err); |
||||
goto poweroff; |
||||
} |
||||
|
||||
/* T7: 5ms */ |
||||
usleep_range(5000, 6000); |
||||
|
||||
innolux->prepared = true; |
||||
|
||||
return 0; |
||||
|
||||
poweroff: |
||||
regulator_err = regulator_disable(innolux->supply); |
||||
if (regulator_err) |
||||
DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n", |
||||
regulator_err); |
||||
|
||||
gpiod_set_value_cansleep(innolux->enable_gpio, 0); |
||||
return err; |
||||
} |
||||
|
||||
static int innolux_panel_enable(struct drm_panel *panel) |
||||
{ |
||||
struct innolux_panel *innolux = to_innolux_panel(panel); |
||||
int ret; |
||||
|
||||
if (innolux->enabled) |
||||
return 0; |
||||
|
||||
innolux->backlight->props.power = FB_BLANK_UNBLANK; |
||||
ret = backlight_update_status(innolux->backlight); |
||||
if (ret) { |
||||
DRM_DEV_ERROR(panel->drm->dev, |
||||
"Failed to enable backlight %d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
innolux->enabled = true; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct drm_display_mode default_mode = { |
||||
.clock = 56900, |
||||
.hdisplay = 768, |
||||
.hsync_start = 768 + 40, |
||||
.hsync_end = 768 + 40 + 40, |
||||
.htotal = 768 + 40 + 40 + 40, |
||||
.vdisplay = 1024, |
||||
.vsync_start = 1024 + 20, |
||||
.vsync_end = 1024 + 20 + 4, |
||||
.vtotal = 1024 + 20 + 4 + 20, |
||||
.vrefresh = 60, |
||||
}; |
||||
|
||||
static int innolux_panel_get_modes(struct drm_panel *panel) |
||||
{ |
||||
struct drm_display_mode *mode; |
||||
|
||||
mode = drm_mode_duplicate(panel->drm, &default_mode); |
||||
if (!mode) { |
||||
DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n", |
||||
default_mode.hdisplay, default_mode.vdisplay, |
||||
default_mode.vrefresh); |
||||
return -ENOMEM; |
||||
} |
||||
|
||||
drm_mode_set_name(mode); |
||||
|
||||
drm_mode_probed_add(panel->connector, mode); |
||||
|
||||
panel->connector->display_info.width_mm = 120; |
||||
panel->connector->display_info.height_mm = 160; |
||||
panel->connector->display_info.bpc = 8; |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
static const struct drm_panel_funcs innolux_panel_funcs = { |
||||
.disable = innolux_panel_disable, |
||||
.unprepare = innolux_panel_unprepare, |
||||
.prepare = innolux_panel_prepare, |
||||
.enable = innolux_panel_enable, |
||||
.get_modes = innolux_panel_get_modes, |
||||
}; |
||||
|
||||
static const struct of_device_id innolux_of_match[] = { |
||||
{ .compatible = "innolux,p079zca", }, |
||||
{ } |
||||
}; |
||||
MODULE_DEVICE_TABLE(of, innolux_of_match); |
||||
|
||||
static int innolux_panel_add(struct innolux_panel *innolux) |
||||
{ |
||||
struct device *dev = &innolux->link->dev; |
||||
struct device_node *np; |
||||
int err; |
||||
|
||||
innolux->supply = devm_regulator_get(dev, "power"); |
||||
if (IS_ERR(innolux->supply)) |
||||
return PTR_ERR(innolux->supply); |
||||
|
||||
innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable", |
||||
GPIOD_OUT_HIGH); |
||||
if (IS_ERR(innolux->enable_gpio)) { |
||||
err = PTR_ERR(innolux->enable_gpio); |
||||
dev_dbg(dev, "failed to get enable gpio: %d\n", err); |
||||
innolux->enable_gpio = NULL; |
||||
} |
||||
|
||||
np = of_parse_phandle(dev->of_node, "backlight", 0); |
||||
if (np) { |
||||
innolux->backlight = of_find_backlight_by_node(np); |
||||
of_node_put(np); |
||||
|
||||
if (!innolux->backlight) |
||||
return -EPROBE_DEFER; |
||||
} |
||||
|
||||
drm_panel_init(&innolux->base); |
||||
innolux->base.funcs = &innolux_panel_funcs; |
||||
innolux->base.dev = &innolux->link->dev; |
||||
|
||||
err = drm_panel_add(&innolux->base); |
||||
if (err < 0) |
||||
goto put_backlight; |
||||
|
||||
return 0; |
||||
|
||||
put_backlight: |
||||
put_device(&innolux->backlight->dev); |
||||
|
||||
return err; |
||||
} |
||||
|
||||
static void innolux_panel_del(struct innolux_panel *innolux) |
||||
{ |
||||
if (innolux->base.dev) |
||||
drm_panel_remove(&innolux->base); |
||||
|
||||
put_device(&innolux->backlight->dev); |
||||
} |
||||
|
||||
static int innolux_panel_probe(struct mipi_dsi_device *dsi) |
||||
{ |
||||
struct innolux_panel *innolux; |
||||
int err; |
||||
|
||||
dsi->lanes = 4; |
||||
dsi->format = MIPI_DSI_FMT_RGB888; |
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | |
||||
MIPI_DSI_MODE_LPM; |
||||
|
||||
innolux = devm_kzalloc(&dsi->dev, sizeof(*innolux), GFP_KERNEL); |
||||
if (!innolux) |
||||
return -ENOMEM; |
||||
|
||||
mipi_dsi_set_drvdata(dsi, innolux); |
||||
|
||||
innolux->link = dsi; |
||||
|
||||
err = innolux_panel_add(innolux); |
||||
if (err < 0) |
||||
return err; |
||||
|
||||
err = mipi_dsi_attach(dsi); |
||||
return err; |
||||
} |
||||
|
||||
static int innolux_panel_remove(struct mipi_dsi_device *dsi) |
||||
{ |
||||
struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi); |
||||
int err; |
||||
|
||||
err = innolux_panel_unprepare(&innolux->base); |
||||
if (err < 0) |
||||
DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n", |
||||
err); |
||||
|
||||
err = innolux_panel_disable(&innolux->base); |
||||
if (err < 0) |
||||
DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err); |
||||
|
||||
err = mipi_dsi_detach(dsi); |
||||
if (err < 0) |
||||
DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n", |
||||
err); |
||||
|
||||
drm_panel_detach(&innolux->base); |
||||
innolux_panel_del(innolux); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void innolux_panel_shutdown(struct mipi_dsi_device *dsi) |
||||
{ |
||||
struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi); |
||||
|
||||
innolux_panel_unprepare(&innolux->base); |
||||
innolux_panel_disable(&innolux->base); |
||||
} |
||||
|
||||
static struct mipi_dsi_driver innolux_panel_driver = { |
||||
.driver = { |
||||
.name = "panel-innolux-p079zca", |
||||
.of_match_table = innolux_of_match, |
||||
}, |
||||
.probe = innolux_panel_probe, |
||||
.remove = innolux_panel_remove, |
||||
.shutdown = innolux_panel_shutdown, |
||||
}; |
||||
module_mipi_dsi_driver(innolux_panel_driver); |
||||
|
||||
MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>"); |
||||
MODULE_DESCRIPTION("Innolux P079ZCA panel driver"); |
||||
MODULE_LICENSE("GPL v2"); |
Loading…
Reference in new issue