drm/panel: Changes for v4.5-rc1

This set of changes brings in a few more helpers for DSI support as well
 as a couple of new drivers and support for some more simple panels.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJWcZxDAAoJEN0jrNd/PrOhuWkP/0VX9HrqF0R3eGB4Vk7I2O1Q
 ftM7LNnLkN9fhs2D+uL3mkNyMEdYwCVuwkPM3dfWLDZ17Y1MQgqmdRCUwavzjxTx
 ZHATjCPry2sB/0A8a4HiRd7tmexJGSuxU4TKZgBMykdcdtK3Pxu4X3RyLeiirPsm
 /8XU/WmEnWnmNuyXWtgGDokiu/QW6kcjZrcHVVMklvdl6kNgSB60HoaH56z7+g51
 y4ixFHvdj4ijBNf5wB6Gd8CVQY/2yLNq5GVuh4H0KYL7LapzB2VNFJDp5jxM3KCA
 63hPBa9y2Yews0jdRK+BQCVerVzWG92cwm/3sKdUfABHULLdZ+qBwfKON/Obm3pQ
 a1QNFr+2KVTRT1BfVv5qoLNzC0xUa55tFeduFOssZI1DhQClrsWkkzPxs3TDrAYf
 6fo3pSYSlvb3P1vAcVcA15NlwoCGAV0QDIIFPL+uZPwObcCVuWWGGvfYT2hrrD+u
 Gh1L3Mw2+tvMm1xx3U38ABs5ZiFwnogZKOY0g3AWR0n0HP0Hs/znWtUE8wyfpMf4
 fVKrU+4vxYAYsY40jTrmOzT3Pt6b5lfIBvdpeqb8lFuWpsMzslc9p2mkXpIBkn3K
 VbY8Z0e6F0LySThG0iLykxaeMklEIfe9eybkCQGV5sWrIIQ2+falKAZvu5Ho9zzh
 ioGnJn/05KVIfKd6w2E8
 =vV7Q
 -----END PGP SIGNATURE-----

Merge tag 'drm/panel/for-4.5-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next

drm/panel: Changes for v4.5-rc1

This set of changes brings in a few more helpers for DSI support as well
as a couple of new drivers and support for some more simple panels.

* tag 'drm/panel/for-4.5-rc1' of git://anongit.freedesktop.org/tegra/linux:
  drm/panel: simple: Add QiaoDian qd43003c0-40
  of: Add vendor prefix for QiaoDian Xianshi
  drm/panel: add kernel doc for size attributes in panel_desc
  drm/panel: simple: Add support for Kyocera TCG121XGLP panel
  devicetree: add vendor prefix for Kyocera Corporation
  drm/bridge: Remove gratuitous blank line
  drm/bridge: dw-hdmi: Use dashes in filenames
  drm/panel: Add Sharp LS043T1LE01 MIPI DSI panel
  dt-bindings: Add Sharp LS043T1LE01 panel binding
  drm/dsi: Add Turn On/Shutdown Peripheral command helpers
  drm/panel: Add Panasonic VVX10F034N00 MIPI DSI panel
  dt-bindings: Add Panasonic VVX10F034N00 panel binding
  drm/panel: simple: Add support for Innolux G121X1-L03
  drm/panel: simple: Add support for BOE TV080WUM-NL0
  dt-bindings: Add BOE TV080WUM-NL0 panel binding
  of: Add vendor prefix for BOE Technology Group
  drm/dsi: Add a helper to get bits per pixel of MIPI DSI pixel format
This commit is contained in:
Dave Airlie 2015-12-17 08:37:52 +10:00
commit 07ade84461
20 changed files with 1008 additions and 6 deletions

View File

@ -0,0 +1,7 @@
Boe Corporation 8.0" WUXGA TFT LCD panel
Required properties:
- compatible: should be "boe,tv080wum-nl0"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.

View File

@ -0,0 +1,7 @@
Innolux Corporation 12.1" G121X1-L03 XGA (1024x768) TFT LCD panel
Required properties:
- compatible: should be "innolux,g121x1-l03"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.

View File

@ -0,0 +1,7 @@
Kyocera Corporation 12.1" XGA (1024x768) TFT LCD panel
Required properties:
- compatible: should be "kyo,tcg121xglp"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.

View File

@ -0,0 +1,22 @@
Sharp Microelectronics 4.3" qHD TFT LCD panel
Required properties:
- compatible: should be "sharp,ls043t1le01-qhd"
- reg: DSI virtual channel of the peripheral
- power-supply: phandle of the regulator that provides the supply voltage
Optional properties:
- backlight: phandle of the backlight device attached to the panel
- reset-gpios: a GPIO spec for the reset pin
Example:
mdss_dsi@fd922800 {
panel@0 {
compatible = "sharp,ls043t1le01-qhd";
reg = <0>;
avdd-supply = <&pm8941_l22>;
backlight = <&pm8941_wled>;
reset-gpios = <&pm8941_gpios 19 GPIO_ACTIVE_HIGH>;
};
};

View File

@ -0,0 +1,20 @@
Panasonic 10" WUXGA TFT LCD panel
Required properties:
- compatible: should be "panasonic,vvx10f034n00"
- reg: DSI virtual channel of the peripheral
- power-supply: phandle of the regulator that provides the supply voltage
Optional properties:
- backlight: phandle of the backlight device attached to the panel
Example:
mdss_dsi@fd922800 {
panel@0 {
compatible = "panasonic,vvx10f034n00";
reg = <0>;
power-supply = <&vreg_vsp>;
backlight = <&lp8566_wled>;
};
};

View File

@ -0,0 +1,7 @@
QiaoDian XianShi Corporation 4"3 TFT LCD panel
Required properties:
- compatible: should be "qiaodian,qd43003c0-40"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.

View File

@ -33,6 +33,7 @@ auo AU Optronics Corporation
avago Avago Technologies avago Avago Technologies
avic Shanghai AVIC Optoelectronics Co., Ltd. avic Shanghai AVIC Optoelectronics Co., Ltd.
axis Axis Communications AB axis Axis Communications AB
boe BOE Technology Group Co., Ltd.
bosch Bosch Sensortec GmbH bosch Bosch Sensortec GmbH
boundary Boundary Devices Inc. boundary Boundary Devices Inc.
brcm Broadcom Corporation brcm Broadcom Corporation
@ -123,6 +124,7 @@ jedec JEDEC Solid State Technology Association
karo Ka-Ro electronics GmbH karo Ka-Ro electronics GmbH
keymile Keymile GmbH keymile Keymile GmbH
kinetic Kinetic Technologies kinetic Kinetic Technologies
kyo Kyocera Corporation
lacie LaCie lacie LaCie
lantiq Lantiq Semiconductor lantiq Lantiq Semiconductor
lenovo Lenovo Group Ltd. lenovo Lenovo Group Ltd.
@ -180,6 +182,7 @@ qca Qualcomm Atheros, Inc.
qcom Qualcomm Technologies, Inc qcom Qualcomm Technologies, Inc
qemu QEMU, a generic and open source machine emulator and virtualizer qemu QEMU, a generic and open source machine emulator and virtualizer
qi Qi Hardware qi Qi Hardware
qiaodian QiaoDian XianShi Corporation
qnap QNAP Systems, Inc. qnap QNAP Systems, Inc.
radxa Radxa radxa Radxa
raidsonic RaidSonic Technology GmbH raidsonic RaidSonic Technology GmbH

View File

@ -22,7 +22,6 @@ config DRM_DW_HDMI_AHB_AUDIO
Designware HDMI block. This is used in conjunction with Designware HDMI block. This is used in conjunction with
the i.MX6 HDMI driver. the i.MX6 HDMI driver.
config DRM_NXP_PTN3460 config DRM_NXP_PTN3460
tristate "NXP PTN3460 DP/LVDS bridge" tristate "NXP PTN3460 DP/LVDS bridge"
depends on OF depends on OF

View File

@ -1,6 +1,6 @@
ccflags-y := -Iinclude/drm ccflags-y := -Iinclude/drm
obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o

View File

@ -21,7 +21,7 @@
#include <sound/pcm_drm_eld.h> #include <sound/pcm_drm_eld.h>
#include <sound/pcm_iec958.h> #include <sound/pcm_iec958.h>
#include "dw_hdmi-audio.h" #include "dw-hdmi-audio.h"
#define DRIVER_NAME "dw-hdmi-ahb-audio" #define DRIVER_NAME "dw-hdmi-ahb-audio"

View File

@ -27,8 +27,8 @@
#include <drm/drm_encoder_slave.h> #include <drm/drm_encoder_slave.h>
#include <drm/bridge/dw_hdmi.h> #include <drm/bridge/dw_hdmi.h>
#include "dw_hdmi.h" #include "dw-hdmi.h"
#include "dw_hdmi-audio.h" #include "dw-hdmi-audio.h"
#define HDMI_EDID_LEN 512 #define HDMI_EDID_LEN 512

View File

@ -365,6 +365,44 @@ int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
} }
EXPORT_SYMBOL(mipi_dsi_create_packet); EXPORT_SYMBOL(mipi_dsi_create_packet);
/**
* mipi_dsi_shutdown_peripheral() - sends a Shutdown Peripheral command
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_SHUTDOWN_PERIPHERAL,
.tx_buf = (u8 [2]) { 0, 0 },
.tx_len = 2,
};
return mipi_dsi_device_transfer(dsi, &msg);
}
EXPORT_SYMBOL(mipi_dsi_shutdown_peripheral);
/**
* mipi_dsi_turn_on_peripheral() - sends a Turn On Peripheral command
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_TURN_ON_PERIPHERAL,
.tx_buf = (u8 [2]) { 0, 0 },
.tx_len = 2,
};
return mipi_dsi_device_transfer(dsi, &msg);
}
EXPORT_SYMBOL(mipi_dsi_turn_on_peripheral);
/* /*
* mipi_dsi_set_maximum_return_packet_size() - specify the maximum size of the * mipi_dsi_set_maximum_return_packet_size() - specify the maximum size of the
* the payload in a long packet transmitted from the peripheral back to the * the payload in a long packet transmitted from the peripheral back to the

View File

@ -31,6 +31,16 @@ config DRM_PANEL_LG_LG4573
Say Y here if you want to enable support for LG4573 RGB panel. Say Y here if you want to enable support for LG4573 RGB panel.
To compile this driver as a module, choose M here. To compile this driver as a module, choose M here.
config DRM_PANEL_PANASONIC_VVX10F034N00
tristate "Panasonic VVX10F034N00 1920x1200 video mode 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 Panasonic VVX10F034N00
WUXGA (1920x1200) Novatek NT1397-based DSI panel as found in some
Xperia Z2 tablets
config DRM_PANEL_SAMSUNG_S6E8AA0 config DRM_PANEL_SAMSUNG_S6E8AA0
tristate "Samsung S6E8AA0 DSI video mode panel" tristate "Samsung S6E8AA0 DSI video mode panel"
depends on OF depends on OF
@ -51,4 +61,13 @@ config DRM_PANEL_SHARP_LQ101R1SX01
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called panel-sharp-lq101r1sx01. will be called panel-sharp-lq101r1sx01.
config DRM_PANEL_SHARP_LS043T1LE01
tristate "Sharp LS043T1LE01 qHD video mode 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 Sharp LS043T1LE01 qHD
(540x960) DSI panel as found on the Qualcomm APQ8074 Dragonboard
endmenu endmenu

View File

@ -1,5 +1,7 @@
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.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_SAMSUNG_LD9040) += panel-samsung-ld9040.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o

View File

@ -0,0 +1,334 @@
/*
* Copyright (C) 2015 Red Hat
* Copyright (C) 2015 Sony Mobile Communications Inc.
* Author: Werner Johansson <werner.johansson@sonymobile.com>
*
* Based on AUO panel driver by Rob Clark <robdclark@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/backlight.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>
/*
* When power is turned off to this panel a minimum off time of 500ms has to be
* observed before powering back on as there's no external reset pin. Keep
* track of earliest wakeup time and delay subsequent prepare call accordingly
*/
#define MIN_POFF_MS (500)
struct wuxga_nt_panel {
struct drm_panel base;
struct mipi_dsi_device *dsi;
struct backlight_device *backlight;
struct regulator *supply;
bool prepared;
bool enabled;
ktime_t earliest_wake;
const struct drm_display_mode *mode;
};
static inline struct wuxga_nt_panel *to_wuxga_nt_panel(struct drm_panel *panel)
{
return container_of(panel, struct wuxga_nt_panel, base);
}
static int wuxga_nt_panel_on(struct wuxga_nt_panel *wuxga_nt)
{
struct mipi_dsi_device *dsi = wuxga_nt->dsi;
int ret;
ret = mipi_dsi_turn_on_peripheral(dsi);
if (ret < 0)
return ret;
return 0;
}
static int wuxga_nt_panel_disable(struct drm_panel *panel)
{
struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
if (!wuxga_nt->enabled)
return 0;
mipi_dsi_shutdown_peripheral(wuxga_nt->dsi);
if (wuxga_nt->backlight) {
wuxga_nt->backlight->props.power = FB_BLANK_POWERDOWN;
wuxga_nt->backlight->props.state |= BL_CORE_FBBLANK;
backlight_update_status(wuxga_nt->backlight);
}
wuxga_nt->enabled = false;
return 0;
}
static int wuxga_nt_panel_unprepare(struct drm_panel *panel)
{
struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
if (!wuxga_nt->prepared)
return 0;
regulator_disable(wuxga_nt->supply);
wuxga_nt->earliest_wake = ktime_add_ms(ktime_get_real(), MIN_POFF_MS);
wuxga_nt->prepared = false;
return 0;
}
static int wuxga_nt_panel_prepare(struct drm_panel *panel)
{
struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
int ret;
s64 enablewait;
if (wuxga_nt->prepared)
return 0;
/*
* If the user re-enabled the panel before the required off-time then
* we need to wait the remaining period before re-enabling regulator
*/
enablewait = ktime_ms_delta(wuxga_nt->earliest_wake, ktime_get_real());
/* Sanity check, this should never happen */
if (enablewait > MIN_POFF_MS)
enablewait = MIN_POFF_MS;
if (enablewait > 0)
msleep(enablewait);
ret = regulator_enable(wuxga_nt->supply);
if (ret < 0)
return ret;
/*
* A minimum delay of 250ms is required after power-up until commands
* can be sent
*/
msleep(250);
ret = wuxga_nt_panel_on(wuxga_nt);
if (ret < 0) {
dev_err(panel->dev, "failed to set panel on: %d\n", ret);
goto poweroff;
}
wuxga_nt->prepared = true;
return 0;
poweroff:
regulator_disable(wuxga_nt->supply);
return ret;
}
static int wuxga_nt_panel_enable(struct drm_panel *panel)
{
struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
if (wuxga_nt->enabled)
return 0;
if (wuxga_nt->backlight) {
wuxga_nt->backlight->props.power = FB_BLANK_UNBLANK;
wuxga_nt->backlight->props.state &= ~BL_CORE_FBBLANK;
backlight_update_status(wuxga_nt->backlight);
}
wuxga_nt->enabled = true;
return 0;
}
static const struct drm_display_mode default_mode = {
.clock = 164402,
.hdisplay = 1920,
.hsync_start = 1920 + 152,
.hsync_end = 1920 + 152 + 52,
.htotal = 1920 + 152 + 52 + 20,
.vdisplay = 1200,
.vsync_start = 1200 + 24,
.vsync_end = 1200 + 24 + 6,
.vtotal = 1200 + 24 + 6 + 48,
.vrefresh = 60,
};
static int wuxga_nt_panel_get_modes(struct drm_panel *panel)
{
struct drm_display_mode *mode;
mode = drm_mode_duplicate(panel->drm, &default_mode);
if (!mode) {
dev_err(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 = 217;
panel->connector->display_info.height_mm = 136;
return 1;
}
static const struct drm_panel_funcs wuxga_nt_panel_funcs = {
.disable = wuxga_nt_panel_disable,
.unprepare = wuxga_nt_panel_unprepare,
.prepare = wuxga_nt_panel_prepare,
.enable = wuxga_nt_panel_enable,
.get_modes = wuxga_nt_panel_get_modes,
};
static const struct of_device_id wuxga_nt_of_match[] = {
{ .compatible = "panasonic,vvx10f034n00", },
{ }
};
MODULE_DEVICE_TABLE(of, wuxga_nt_of_match);
static int wuxga_nt_panel_add(struct wuxga_nt_panel *wuxga_nt)
{
struct device *dev = &wuxga_nt->dsi->dev;
struct device_node *np;
int ret;
wuxga_nt->mode = &default_mode;
wuxga_nt->supply = devm_regulator_get(dev, "power");
if (IS_ERR(wuxga_nt->supply))
return PTR_ERR(wuxga_nt->supply);
np = of_parse_phandle(dev->of_node, "backlight", 0);
if (np) {
wuxga_nt->backlight = of_find_backlight_by_node(np);
of_node_put(np);
if (!wuxga_nt->backlight)
return -EPROBE_DEFER;
}
drm_panel_init(&wuxga_nt->base);
wuxga_nt->base.funcs = &wuxga_nt_panel_funcs;
wuxga_nt->base.dev = &wuxga_nt->dsi->dev;
ret = drm_panel_add(&wuxga_nt->base);
if (ret < 0)
goto put_backlight;
return 0;
put_backlight:
if (wuxga_nt->backlight)
put_device(&wuxga_nt->backlight->dev);
return ret;
}
static void wuxga_nt_panel_del(struct wuxga_nt_panel *wuxga_nt)
{
if (wuxga_nt->base.dev)
drm_panel_remove(&wuxga_nt->base);
if (wuxga_nt->backlight)
put_device(&wuxga_nt->backlight->dev);
}
static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi)
{
struct wuxga_nt_panel *wuxga_nt;
int ret;
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_HSE |
MIPI_DSI_CLOCK_NON_CONTINUOUS |
MIPI_DSI_MODE_LPM;
wuxga_nt = devm_kzalloc(&dsi->dev, sizeof(*wuxga_nt), GFP_KERNEL);
if (!wuxga_nt)
return -ENOMEM;
mipi_dsi_set_drvdata(dsi, wuxga_nt);
wuxga_nt->dsi = dsi;
ret = wuxga_nt_panel_add(wuxga_nt);
if (ret < 0)
return ret;
return mipi_dsi_attach(dsi);
}
static int wuxga_nt_panel_remove(struct mipi_dsi_device *dsi)
{
struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
int ret;
ret = wuxga_nt_panel_disable(&wuxga_nt->base);
if (ret < 0)
dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
ret = mipi_dsi_detach(dsi);
if (ret < 0)
dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
drm_panel_detach(&wuxga_nt->base);
wuxga_nt_panel_del(wuxga_nt);
return 0;
}
static void wuxga_nt_panel_shutdown(struct mipi_dsi_device *dsi)
{
struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
wuxga_nt_panel_disable(&wuxga_nt->base);
}
static struct mipi_dsi_driver wuxga_nt_panel_driver = {
.driver = {
.name = "panel-panasonic-vvx10f034n00",
.of_match_table = wuxga_nt_of_match,
},
.probe = wuxga_nt_panel_probe,
.remove = wuxga_nt_panel_remove,
.shutdown = wuxga_nt_panel_shutdown,
};
module_mipi_dsi_driver(wuxga_nt_panel_driver);
MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,387 @@
/*
* Copyright (C) 2015 Red Hat
* Copyright (C) 2015 Sony Mobile Communications Inc.
* Author: Werner Johansson <werner.johansson@sonymobile.com>
*
* Based on AUO panel driver by Rob Clark <robdclark@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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 sharp_nt_panel {
struct drm_panel base;
struct mipi_dsi_device *dsi;
struct backlight_device *backlight;
struct regulator *supply;
struct gpio_desc *reset_gpio;
bool prepared;
bool enabled;
const struct drm_display_mode *mode;
};
static inline struct sharp_nt_panel *to_sharp_nt_panel(struct drm_panel *panel)
{
return container_of(panel, struct sharp_nt_panel, base);
}
static int sharp_nt_panel_init(struct sharp_nt_panel *sharp_nt)
{
struct mipi_dsi_device *dsi = sharp_nt->dsi;
int ret;
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
if (ret < 0)
return ret;
msleep(120);
/* Novatek two-lane operation */
ret = mipi_dsi_dcs_write(dsi, 0xae, (u8[]){ 0x03 }, 1);
if (ret < 0)
return ret;
/* Set both MCU and RGB I/F to 24bpp */
ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT |
(MIPI_DCS_PIXEL_FMT_24BIT << 4));
if (ret < 0)
return ret;
return 0;
}
static int sharp_nt_panel_on(struct sharp_nt_panel *sharp_nt)
{
struct mipi_dsi_device *dsi = sharp_nt->dsi;
int ret;
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
ret = mipi_dsi_dcs_set_display_on(dsi);
if (ret < 0)
return ret;
return 0;
}
static int sharp_nt_panel_off(struct sharp_nt_panel *sharp_nt)
{
struct mipi_dsi_device *dsi = sharp_nt->dsi;
int ret;
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
ret = mipi_dsi_dcs_set_display_off(dsi);
if (ret < 0)
return ret;
ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
if (ret < 0)
return ret;
return 0;
}
static int sharp_nt_panel_disable(struct drm_panel *panel)
{
struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
if (!sharp_nt->enabled)
return 0;
if (sharp_nt->backlight) {
sharp_nt->backlight->props.power = FB_BLANK_POWERDOWN;
backlight_update_status(sharp_nt->backlight);
}
sharp_nt->enabled = false;
return 0;
}
static int sharp_nt_panel_unprepare(struct drm_panel *panel)
{
struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
int ret;
if (!sharp_nt->prepared)
return 0;
ret = sharp_nt_panel_off(sharp_nt);
if (ret < 0) {
dev_err(panel->dev, "failed to set panel off: %d\n", ret);
return ret;
}
regulator_disable(sharp_nt->supply);
if (sharp_nt->reset_gpio)
gpiod_set_value(sharp_nt->reset_gpio, 0);
sharp_nt->prepared = false;
return 0;
}
static int sharp_nt_panel_prepare(struct drm_panel *panel)
{
struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
int ret;
if (sharp_nt->prepared)
return 0;
ret = regulator_enable(sharp_nt->supply);
if (ret < 0)
return ret;
msleep(20);
if (sharp_nt->reset_gpio) {
gpiod_set_value(sharp_nt->reset_gpio, 1);
msleep(1);
gpiod_set_value(sharp_nt->reset_gpio, 0);
msleep(1);
gpiod_set_value(sharp_nt->reset_gpio, 1);
msleep(10);
}
ret = sharp_nt_panel_init(sharp_nt);
if (ret < 0) {
dev_err(panel->dev, "failed to init panel: %d\n", ret);
goto poweroff;
}
ret = sharp_nt_panel_on(sharp_nt);
if (ret < 0) {
dev_err(panel->dev, "failed to set panel on: %d\n", ret);
goto poweroff;
}
sharp_nt->prepared = true;
return 0;
poweroff:
regulator_disable(sharp_nt->supply);
if (sharp_nt->reset_gpio)
gpiod_set_value(sharp_nt->reset_gpio, 0);
return ret;
}
static int sharp_nt_panel_enable(struct drm_panel *panel)
{
struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
if (sharp_nt->enabled)
return 0;
if (sharp_nt->backlight) {
sharp_nt->backlight->props.power = FB_BLANK_UNBLANK;
backlight_update_status(sharp_nt->backlight);
}
sharp_nt->enabled = true;
return 0;
}
static const struct drm_display_mode default_mode = {
.clock = 41118,
.hdisplay = 540,
.hsync_start = 540 + 48,
.hsync_end = 540 + 48 + 80,
.htotal = 540 + 48 + 80 + 32,
.vdisplay = 960,
.vsync_start = 960 + 3,
.vsync_end = 960 + 3 + 15,
.vtotal = 960 + 3 + 15 + 1,
.vrefresh = 60,
};
static int sharp_nt_panel_get_modes(struct drm_panel *panel)
{
struct drm_display_mode *mode;
mode = drm_mode_duplicate(panel->drm, &default_mode);
if (!mode) {
dev_err(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 = 54;
panel->connector->display_info.height_mm = 95;
return 1;
}
static const struct drm_panel_funcs sharp_nt_panel_funcs = {
.disable = sharp_nt_panel_disable,
.unprepare = sharp_nt_panel_unprepare,
.prepare = sharp_nt_panel_prepare,
.enable = sharp_nt_panel_enable,
.get_modes = sharp_nt_panel_get_modes,
};
static int sharp_nt_panel_add(struct sharp_nt_panel *sharp_nt)
{
struct device *dev = &sharp_nt->dsi->dev;
struct device_node *np;
int ret;
sharp_nt->mode = &default_mode;
sharp_nt->supply = devm_regulator_get(dev, "avdd");
if (IS_ERR(sharp_nt->supply))
return PTR_ERR(sharp_nt->supply);
sharp_nt->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(sharp_nt->reset_gpio)) {
dev_err(dev, "cannot get reset-gpios %ld\n",
PTR_ERR(sharp_nt->reset_gpio));
sharp_nt->reset_gpio = NULL;
} else {
gpiod_set_value(sharp_nt->reset_gpio, 0);
}
np = of_parse_phandle(dev->of_node, "backlight", 0);
if (np) {
sharp_nt->backlight = of_find_backlight_by_node(np);
of_node_put(np);
if (!sharp_nt->backlight)
return -EPROBE_DEFER;
}
drm_panel_init(&sharp_nt->base);
sharp_nt->base.funcs = &sharp_nt_panel_funcs;
sharp_nt->base.dev = &sharp_nt->dsi->dev;
ret = drm_panel_add(&sharp_nt->base);
if (ret < 0)
goto put_backlight;
return 0;
put_backlight:
if (sharp_nt->backlight)
put_device(&sharp_nt->backlight->dev);
return ret;
}
static void sharp_nt_panel_del(struct sharp_nt_panel *sharp_nt)
{
if (sharp_nt->base.dev)
drm_panel_remove(&sharp_nt->base);
if (sharp_nt->backlight)
put_device(&sharp_nt->backlight->dev);
}
static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi)
{
struct sharp_nt_panel *sharp_nt;
int ret;
dsi->lanes = 2;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_HSE |
MIPI_DSI_CLOCK_NON_CONTINUOUS |
MIPI_DSI_MODE_EOT_PACKET;
sharp_nt = devm_kzalloc(&dsi->dev, sizeof(*sharp_nt), GFP_KERNEL);
if (!sharp_nt)
return -ENOMEM;
mipi_dsi_set_drvdata(dsi, sharp_nt);
sharp_nt->dsi = dsi;
ret = sharp_nt_panel_add(sharp_nt);
if (ret < 0)
return ret;
return mipi_dsi_attach(dsi);
}
static int sharp_nt_panel_remove(struct mipi_dsi_device *dsi)
{
struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
int ret;
ret = sharp_nt_panel_disable(&sharp_nt->base);
if (ret < 0)
dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
ret = mipi_dsi_detach(dsi);
if (ret < 0)
dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
drm_panel_detach(&sharp_nt->base);
sharp_nt_panel_del(sharp_nt);
return 0;
}
static void sharp_nt_panel_shutdown(struct mipi_dsi_device *dsi)
{
struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
sharp_nt_panel_disable(&sharp_nt->base);
}
static const struct of_device_id sharp_nt_of_match[] = {
{ .compatible = "sharp,ls043t1le01-qhd", },
{ }
};
MODULE_DEVICE_TABLE(of, sharp_nt_of_match);
static struct mipi_dsi_driver sharp_nt_panel_driver = {
.driver = {
.name = "panel-sharp-ls043t1le01-qhd",
.of_match_table = sharp_nt_of_match,
},
.probe = sharp_nt_panel_probe,
.remove = sharp_nt_panel_remove,
.shutdown = sharp_nt_panel_shutdown,
};
module_mipi_dsi_driver(sharp_nt_panel_driver);
MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
MODULE_DESCRIPTION("Sharp LS043T1LE01 NT35565-based qHD (540x960) video mode panel driver");
MODULE_LICENSE("GPL v2");

View File

@ -44,6 +44,10 @@ struct panel_desc {
unsigned int bpc; unsigned int bpc;
/**
* @width: width (in millimeters) of the panel's active display area
* @height: height (in millimeters) of the panel's active display area
*/
struct { struct {
unsigned int width; unsigned int width;
unsigned int height; unsigned int height;
@ -832,6 +836,34 @@ static const struct panel_desc innolux_g121i1_l01 = {
}, },
}; };
static const struct drm_display_mode innolux_g121x1_l03_mode = {
.clock = 65000,
.hdisplay = 1024,
.hsync_start = 1024 + 0,
.hsync_end = 1024 + 1,
.htotal = 1024 + 0 + 1 + 320,
.vdisplay = 768,
.vsync_start = 768 + 38,
.vsync_end = 768 + 38 + 1,
.vtotal = 768 + 38 + 1 + 0,
.vrefresh = 60,
};
static const struct panel_desc innolux_g121x1_l03 = {
.modes = &innolux_g121x1_l03_mode,
.num_modes = 1,
.bpc = 6,
.size = {
.width = 246,
.height = 185,
},
.delay = {
.enable = 200,
.unprepare = 200,
.disable = 400,
},
};
static const struct drm_display_mode innolux_n116bge_mode = { static const struct drm_display_mode innolux_n116bge_mode = {
.clock = 76420, .clock = 76420,
.hdisplay = 1366, .hdisplay = 1366,
@ -902,6 +934,30 @@ static const struct panel_desc innolux_zj070na_01p = {
}, },
}; };
static const struct display_timing kyo_tcg121xglp_timing = {
.pixelclock = { 52000000, 65000000, 71000000 },
.hactive = { 1024, 1024, 1024 },
.hfront_porch = { 2, 2, 2 },
.hback_porch = { 2, 2, 2 },
.hsync_len = { 86, 124, 244 },
.vactive = { 768, 768, 768 },
.vfront_porch = { 2, 2, 2 },
.vback_porch = { 2, 2, 2 },
.vsync_len = { 6, 34, 73 },
.flags = DISPLAY_FLAGS_DE_HIGH,
};
static const struct panel_desc kyo_tcg121xglp = {
.timings = &kyo_tcg121xglp_timing,
.num_timings = 1,
.bpc = 8,
.size = {
.width = 246,
.height = 184,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
};
static const struct drm_display_mode lg_lb070wv8_mode = { static const struct drm_display_mode lg_lb070wv8_mode = {
.clock = 33246, .clock = 33246,
.hdisplay = 800, .hdisplay = 800,
@ -1027,6 +1083,30 @@ static const struct panel_desc ortustech_com43h4m85ulc = {
.bus_format = MEDIA_BUS_FMT_RGB888_1X24, .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
}; };
static const struct drm_display_mode qd43003c0_40_mode = {
.clock = 9000,
.hdisplay = 480,
.hsync_start = 480 + 8,
.hsync_end = 480 + 8 + 4,
.htotal = 480 + 8 + 4 + 39,
.vdisplay = 272,
.vsync_start = 272 + 4,
.vsync_end = 272 + 4 + 10,
.vtotal = 272 + 4 + 10 + 2,
.vrefresh = 60,
};
static const struct panel_desc qd43003c0_40 = {
.modes = &qd43003c0_40_mode,
.num_modes = 1,
.bpc = 8,
.size = {
.width = 95,
.height = 53,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
static const struct drm_display_mode samsung_ltn101nt05_mode = { static const struct drm_display_mode samsung_ltn101nt05_mode = {
.clock = 54030, .clock = 54030,
.hdisplay = 1024, .hdisplay = 1024,
@ -1157,6 +1237,9 @@ static const struct of_device_id platform_of_match[] = {
}, { }, {
.compatible ="innolux,g121i1-l01", .compatible ="innolux,g121i1-l01",
.data = &innolux_g121i1_l01 .data = &innolux_g121i1_l01
}, {
.compatible = "innolux,g121x1-l03",
.data = &innolux_g121x1_l03,
}, { }, {
.compatible = "innolux,n116bge", .compatible = "innolux,n116bge",
.data = &innolux_n116bge, .data = &innolux_n116bge,
@ -1166,6 +1249,9 @@ static const struct of_device_id platform_of_match[] = {
}, { }, {
.compatible = "innolux,zj070na-01p", .compatible = "innolux,zj070na-01p",
.data = &innolux_zj070na_01p, .data = &innolux_zj070na_01p,
}, {
.compatible = "kyo,tcg121xglp",
.data = &kyo_tcg121xglp,
}, { }, {
.compatible = "lg,lb070wv8", .compatible = "lg,lb070wv8",
.data = &lg_lb070wv8, .data = &lg_lb070wv8,
@ -1181,6 +1267,9 @@ static const struct of_device_id platform_of_match[] = {
}, { }, {
.compatible = "ortustech,com43h4m85ulc", .compatible = "ortustech,com43h4m85ulc",
.data = &ortustech_com43h4m85ulc, .data = &ortustech_com43h4m85ulc,
}, {
.compatible = "qiaodian,qd43003c0-40",
.data = &qd43003c0_40,
}, { }, {
.compatible = "samsung,ltn101nt05", .compatible = "samsung,ltn101nt05",
.data = &samsung_ltn101nt05, .data = &samsung_ltn101nt05,
@ -1263,6 +1352,36 @@ static const struct panel_desc_dsi auo_b080uan01 = {
.lanes = 4, .lanes = 4,
}; };
static const struct drm_display_mode boe_tv080wum_nl0_mode = {
.clock = 160000,
.hdisplay = 1200,
.hsync_start = 1200 + 120,
.hsync_end = 1200 + 120 + 20,
.htotal = 1200 + 120 + 20 + 21,
.vdisplay = 1920,
.vsync_start = 1920 + 21,
.vsync_end = 1920 + 21 + 3,
.vtotal = 1920 + 21 + 3 + 18,
.vrefresh = 60,
.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
};
static const struct panel_desc_dsi boe_tv080wum_nl0 = {
.desc = {
.modes = &boe_tv080wum_nl0_mode,
.num_modes = 1,
.size = {
.width = 107,
.height = 172,
},
},
.flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
.format = MIPI_DSI_FMT_RGB888,
.lanes = 4,
};
static const struct drm_display_mode lg_ld070wx3_sl01_mode = { static const struct drm_display_mode lg_ld070wx3_sl01_mode = {
.clock = 71000, .clock = 71000,
.hdisplay = 800, .hdisplay = 800,
@ -1348,10 +1467,14 @@ static const struct panel_desc_dsi panasonic_vvx10f004b00 = {
.lanes = 4, .lanes = 4,
}; };
static const struct of_device_id dsi_of_match[] = { static const struct of_device_id dsi_of_match[] = {
{ {
.compatible = "auo,b080uan01", .compatible = "auo,b080uan01",
.data = &auo_b080uan01 .data = &auo_b080uan01
}, {
.compatible = "boe,tv080wum-nl0",
.data = &boe_tv080wum_nl0
}, { }, {
.compatible = "lg,ld070wx3-sl01", .compatible = "lg,ld070wx3-sl01",
.data = &lg_ld070wx3_sl01 .data = &lg_ld070wx3_sl01

View File

@ -163,9 +163,36 @@ static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev)
return container_of(dev, struct mipi_dsi_device, dev); return container_of(dev, struct mipi_dsi_device, dev);
} }
/**
* mipi_dsi_pixel_format_to_bpp - obtain the number of bits per pixel for any
* given pixel format defined by the MIPI DSI
* specification
* @fmt: MIPI DSI pixel format
*
* Returns: The number of bits per pixel of the given pixel format.
*/
static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
{
switch (fmt) {
case MIPI_DSI_FMT_RGB888:
case MIPI_DSI_FMT_RGB666:
return 24;
case MIPI_DSI_FMT_RGB666_PACKED:
return 18;
case MIPI_DSI_FMT_RGB565:
return 16;
}
return -EINVAL;
}
struct mipi_dsi_device *of_find_mipi_dsi_device_by_node(struct device_node *np); struct mipi_dsi_device *of_find_mipi_dsi_device_by_node(struct device_node *np);
int mipi_dsi_attach(struct mipi_dsi_device *dsi); int mipi_dsi_attach(struct mipi_dsi_device *dsi);
int mipi_dsi_detach(struct mipi_dsi_device *dsi); int mipi_dsi_detach(struct mipi_dsi_device *dsi);
int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi);
int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi);
int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi, int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
u16 value); u16 value);