Commit 8aa3dc3c authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm/panel/for-3.19-rc1' of git://people.freedesktop.org/~tagr/linux into drm-next

drm/panel: Changes for v3.19-rc1

This contains support for a couple of new panels, updates for some GPIO
API changes and a bunch of updates to the MIPI DSI support that should
make it easier to write panel drivers in the future.

* tag 'drm/panel/for-3.19-rc1' of git://people.freedesktop.org/~tagr/linux: (31 commits)
  drm/panel: Add Sharp LQ101R1SX01 support
  drm/dsi: Do not require .owner field to be set
  drm/dsi: Resolve MIPI DSI device from phandle
  drm/dsi: Implement DCS set_{column,page}_address commands
  drm/dsi: Implement DCS {get,set}_pixel_format commands
  drm/dsi: Implement DCS get_power_mode command
  drm/dsi: Implement DCS soft_reset command
  drm/dsi: Implement DCS nop command
  drm/dsi: Add to DocBook documentation
  drm/dsi: Implement some standard DCS commands
  drm/dsi: Implement generic read and write commands
  drm/panel: s6e8aa0: Use standard MIPI DSI function
  drm/dsi: Add mipi_dsi_set_maximum_return_packet_size() helper
  drm/dsi: Constify mipi_dsi_msg
  drm/dsi: Make mipi_dsi_dcs_{read,write}() symmetrical
  drm/dsi: Add DSI transfer helper
  drm/dsi: Add message to packet translator
  drm/dsi: Introduce packet format helpers
  drm/panel: s6e8aa0: Fix build warnings on 64-bit
  drm/panel: ld9040: Fix build warnings on 64-bit
  ...
parents fd172d0c 1976dbca
...@@ -2366,6 +2366,12 @@ void intel_crt_init(struct drm_device *dev) ...@@ -2366,6 +2366,12 @@ void intel_crt_init(struct drm_device *dev)
!Pdrivers/gpu/drm/drm_dp_mst_topology.c dp mst helper !Pdrivers/gpu/drm/drm_dp_mst_topology.c dp mst helper
!Iinclude/drm/drm_dp_mst_helper.h !Iinclude/drm/drm_dp_mst_helper.h
!Edrivers/gpu/drm/drm_dp_mst_topology.c !Edrivers/gpu/drm/drm_dp_mst_topology.c
</sect2>
<sect2>
<title>MIPI DSI Helper Functions Reference</title>
!Pdrivers/gpu/drm/drm_mipi_dsi.c dsi helpers
!Iinclude/drm/drm_mipi_dsi.h
!Edrivers/gpu/drm/drm_mipi_dsi.c
</sect2> </sect2>
<sect2> <sect2>
<title>EDID Helper Functions Reference</title> <title>EDID Helper Functions Reference</title>
......
AU Optronics Corporation 11.6" HD (1366x768) color TFT-LCD panel
Required properties:
- compatible: should be "auo,b116xw03"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.
HannStar Display Corp. HSD070PWW1 7.0" WXGA TFT LCD panel
Required properties:
- compatible: should be "hannstar,hsd070pww1"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.
Hitachi Ltd. Corporation 9" WVGA (800x480) TFT LCD panel
Required properties:
- compatible: should be "hit,tx23d38vm0caa"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.
Innolux Corporation 12.1" WXGA (1280x800) TFT LCD panel
Required properties:
- compatible: should be "innolux,g121i1-l01"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.
Sharp Microelectronics 10.1" WQXGA TFT LCD panel
This panel requires a dual-channel DSI host to operate. It supports two modes:
- left-right: each channel drives the left or right half of the screen
- even-odd: each channel drives the even or odd lines of the screen
Each of the DSI channels controls a separate DSI peripheral. The peripheral
driven by the first link (DSI-LINK1), left or even, is considered the primary
peripheral and controls the device. The 'link2' property contains a phandle
to the peripheral driven by the second link (DSI-LINK2, right or odd).
Note that in video mode the DSI-LINK1 interface always provides the left/even
pixels and DSI-LINK2 always provides the right/odd pixels. In command mode it
is possible to program either link to drive the left/even or right/odd pixels
but for the sake of consistency this binding assumes that the same assignment
is chosen as for video mode.
Required properties:
- compatible: should be "sharp,lq101r1sx01"
- reg: DSI virtual channel of the peripheral
Required properties (for DSI-LINK1 only):
- link2: phandle to the DSI peripheral on the secondary link. Note that the
presence of this property marks the containing node as DSI-LINK1.
- power-supply: phandle of the regulator that provides the supply voltage
Optional properties (for DSI-LINK1 only):
- backlight: phandle of the backlight device attached to the panel
Example:
dsi@54300000 {
panel: panel@0 {
compatible = "sharp,lq101r1sx01";
reg = <0>;
link2 = <&secondary>;
power-supply = <...>;
backlight = <...>;
};
};
dsi@54400000 {
secondary: panel@0 {
compatible = "sharp,lq101r1sx01";
reg = <0>;
};
};
...@@ -64,8 +64,10 @@ gmt Global Mixed-mode Technology, Inc. ...@@ -64,8 +64,10 @@ gmt Global Mixed-mode Technology, Inc.
google Google, Inc. google Google, Inc.
gumstix Gumstix, Inc. gumstix Gumstix, Inc.
gw Gateworks Corporation gw Gateworks Corporation
hannstar HannStar Display Corporation
haoyu Haoyu Microelectronic Co. Ltd. haoyu Haoyu Microelectronic Co. Ltd.
hisilicon Hisilicon Limited. hisilicon Hisilicon Limited.
hit Hitachi Ltd.
honeywell Honeywell honeywell Honeywell
hp Hewlett Packard hp Hewlett Packard
i2se I2SE GmbH i2se I2SE GmbH
......
...@@ -35,6 +35,16 @@ ...@@ -35,6 +35,16 @@
#include <video/mipi_display.h> #include <video/mipi_display.h>
/**
* DOC: dsi helpers
*
* These functions contain some common logic and helpers to deal with MIPI DSI
* peripherals.
*
* Helpers are provided for a number of standard MIPI DSI command as well as a
* subset of the MIPI DCS command set.
*/
static int mipi_dsi_device_match(struct device *dev, struct device_driver *drv) static int mipi_dsi_device_match(struct device *dev, struct device_driver *drv)
{ {
return of_driver_match_device(dev, drv); return of_driver_match_device(dev, drv);
...@@ -57,6 +67,29 @@ static struct bus_type mipi_dsi_bus_type = { ...@@ -57,6 +67,29 @@ static struct bus_type mipi_dsi_bus_type = {
.pm = &mipi_dsi_device_pm_ops, .pm = &mipi_dsi_device_pm_ops,
}; };
static int of_device_match(struct device *dev, void *data)
{
return dev->of_node == data;
}
/**
* of_find_mipi_dsi_device_by_node() - find the MIPI DSI device matching a
* device tree node
* @np: device tree node
*
* Return: A pointer to the MIPI DSI device corresponding to @np or NULL if no
* such device exists (or has not been registered yet).
*/
struct mipi_dsi_device *of_find_mipi_dsi_device_by_node(struct device_node *np)
{
struct device *dev;
dev = bus_find_device(&mipi_dsi_bus_type, NULL, np, of_device_match);
return dev ? to_mipi_dsi_device(dev) : NULL;
}
EXPORT_SYMBOL(of_find_mipi_dsi_device_by_node);
static void mipi_dsi_dev_release(struct device *dev) static void mipi_dsi_dev_release(struct device *dev)
{ {
struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev); struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
...@@ -198,59 +231,351 @@ int mipi_dsi_detach(struct mipi_dsi_device *dsi) ...@@ -198,59 +231,351 @@ int mipi_dsi_detach(struct mipi_dsi_device *dsi)
} }
EXPORT_SYMBOL(mipi_dsi_detach); EXPORT_SYMBOL(mipi_dsi_detach);
static ssize_t mipi_dsi_device_transfer(struct mipi_dsi_device *dsi,
struct mipi_dsi_msg *msg)
{
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
if (!ops || !ops->transfer)
return -ENOSYS;
if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
msg->flags |= MIPI_DSI_MSG_USE_LPM;
return ops->transfer(dsi->host, msg);
}
/** /**
* mipi_dsi_dcs_write - send DCS write command * mipi_dsi_packet_format_is_short - check if a packet is of the short format
* @dsi: DSI device * @type: MIPI DSI data type of the packet
* @data: pointer to the command followed by parameters *
* @len: length of @data * Return: true if the packet for the given data type is a short packet, false
* otherwise.
*/ */
ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data, bool mipi_dsi_packet_format_is_short(u8 type)
size_t len) {
switch (type) {
case MIPI_DSI_V_SYNC_START:
case MIPI_DSI_V_SYNC_END:
case MIPI_DSI_H_SYNC_START:
case MIPI_DSI_H_SYNC_END:
case MIPI_DSI_END_OF_TRANSMISSION:
case MIPI_DSI_COLOR_MODE_OFF:
case MIPI_DSI_COLOR_MODE_ON:
case MIPI_DSI_SHUTDOWN_PERIPHERAL:
case MIPI_DSI_TURN_ON_PERIPHERAL:
case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
case MIPI_DSI_DCS_SHORT_WRITE:
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
case MIPI_DSI_DCS_READ:
case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
return true;
}
return false;
}
EXPORT_SYMBOL(mipi_dsi_packet_format_is_short);
/**
* mipi_dsi_packet_format_is_long - check if a packet is of the long format
* @type: MIPI DSI data type of the packet
*
* Return: true if the packet for the given data type is a long packet, false
* otherwise.
*/
bool mipi_dsi_packet_format_is_long(u8 type)
{
switch (type) {
case MIPI_DSI_NULL_PACKET:
case MIPI_DSI_BLANKING_PACKET:
case MIPI_DSI_GENERIC_LONG_WRITE:
case MIPI_DSI_DCS_LONG_WRITE:
case MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20:
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24:
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16:
case MIPI_DSI_PACKED_PIXEL_STREAM_30:
case MIPI_DSI_PACKED_PIXEL_STREAM_36:
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12:
case MIPI_DSI_PACKED_PIXEL_STREAM_16:
case MIPI_DSI_PACKED_PIXEL_STREAM_18:
case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
case MIPI_DSI_PACKED_PIXEL_STREAM_24:
return true;
}
return false;
}
EXPORT_SYMBOL(mipi_dsi_packet_format_is_long);
/**
* mipi_dsi_create_packet - create a packet from a message according to the
* DSI protocol
* @packet: pointer to a DSI packet structure
* @msg: message to translate into a packet
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
const struct mipi_dsi_msg *msg)
{
const u8 *tx = msg->tx_buf;
if (!packet || !msg)
return -EINVAL;
/* do some minimum sanity checking */
if (!mipi_dsi_packet_format_is_short(msg->type) &&
!mipi_dsi_packet_format_is_long(msg->type))
return -EINVAL;
if (msg->channel > 3)
return -EINVAL;
memset(packet, 0, sizeof(*packet));
packet->header[0] = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
/* TODO: compute ECC if hardware support is not available */
/*
* Long write packets contain the word count in header bytes 1 and 2.
* The payload follows the header and is word count bytes long.
*
* Short write packets encode up to two parameters in header bytes 1
* and 2.
*/
if (mipi_dsi_packet_format_is_long(msg->type)) {
packet->header[1] = (msg->tx_len >> 0) & 0xff;
packet->header[2] = (msg->tx_len >> 8) & 0xff;
packet->payload_length = msg->tx_len;
packet->payload = tx;
} else {
packet->header[1] = (msg->tx_len > 0) ? tx[0] : 0;
packet->header[2] = (msg->tx_len > 1) ? tx[1] : 0;
}
packet->size = sizeof(packet->header) + packet->payload_length;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_create_packet);
/*
* 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
* host processor
* @dsi: DSI peripheral device
* @value: the maximum size of the payload
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
u16 value)
{
u8 tx[2] = { value & 0xff, value >> 8 };
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
.tx_len = sizeof(tx),
.tx_buf = tx,
};
return mipi_dsi_device_transfer(dsi, &msg);
}
EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
/**
* mipi_dsi_generic_write() - transmit data using a generic write packet
* @dsi: DSI peripheral device
* @payload: buffer containing the payload
* @size: size of payload buffer
*
* This function will automatically choose the right data type depending on
* the payload length.
*
* Return: The number of bytes transmitted on success or a negative error code
* on failure.
*/
ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
size_t size)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.tx_buf = payload,
.tx_len = size
};
switch (size) {
case 0:
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
break;
case 1:
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
break;
case 2:
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
break;
default:
msg.type = MIPI_DSI_GENERIC_LONG_WRITE;
break;
}
return mipi_dsi_device_transfer(dsi, &msg);
}
EXPORT_SYMBOL(mipi_dsi_generic_write);
/**
* mipi_dsi_generic_read() - receive data using a generic read packet
* @dsi: DSI peripheral device
* @params: buffer containing the request parameters
* @num_params: number of request parameters
* @data: buffer in which to return the received data
* @size: size of receive buffer
*
* This function will automatically choose the right data type depending on
* the number of parameters passed in.
*
* Return: The number of bytes successfully read or a negative error code on
* failure.
*/
ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
size_t num_params, void *data, size_t size)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.tx_len = num_params,
.tx_buf = params,
.rx_len = size,
.rx_buf = data
};
switch (num_params) {
case 0:
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
break;
case 1:
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
break;
case 2:
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
break;
default:
return -EINVAL;
}
return mipi_dsi_device_transfer(dsi, &msg);
}
EXPORT_SYMBOL(mipi_dsi_generic_read);
/**
* mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload
* @dsi: DSI peripheral device
* @data: buffer containing data to be transmitted
* @len: size of transmission buffer
*
* This function will automatically choose the right data type depending on
* the command payload length.
*
* Return: The number of bytes successfully transmitted or a negative error
* code on failure.
*/
ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
const void *data, size_t len)
{ {
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
struct mipi_dsi_msg msg = { struct mipi_dsi_msg msg = {
.channel = dsi->channel, .channel = dsi->channel,
.tx_buf = data, .tx_buf = data,
.tx_len = len .tx_len = len
}; };
if (!ops || !ops->transfer)
return -ENOSYS;
switch (len) { switch (len) {
case 0: case 0:
return -EINVAL; return -EINVAL;
case 1: case 1:
msg.type = MIPI_DSI_DCS_SHORT_WRITE; msg.type = MIPI_DSI_DCS_SHORT_WRITE;
break; break;
case 2: case 2:
msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM; msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
break; break;
default: default:
msg.type = MIPI_DSI_DCS_LONG_WRITE; msg.type = MIPI_DSI_DCS_LONG_WRITE;
break; break;
} }
if (dsi->mode_flags & MIPI_DSI_MODE_LPM) return mipi_dsi_device_transfer(dsi, &msg);
msg.flags = MIPI_DSI_MSG_USE_LPM; }
EXPORT_SYMBOL(mipi_dsi_dcs_write_buffer);
return ops->transfer(dsi->host, &msg); /**
* mipi_dsi_dcs_write() - send DCS write command
* @dsi: DSI peripheral device
* @cmd: DCS command
* @data: buffer containing the command payload
* @len: command payload length
*
* This function will automatically choose the right data type depending on
* the command payload length.
*
* Return: The number of bytes successfully transmitted or a negative error
* code on failure.
*/
ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
const void *data, size_t len)
{
ssize_t err;
size_t size;
u8 *tx;
if (len > 0) {
size = 1 + len;
tx = kmalloc(size, GFP_KERNEL);
if (!tx)
return -ENOMEM;
/* concatenate the DCS command byte and the payload */
tx[0] = cmd;
memcpy(&tx[1], data, len);
} else {
tx = &cmd;
size = 1;
}
err = mipi_dsi_dcs_write_buffer(dsi, tx, size);
if (len > 0)
kfree(tx);
return err;
} }
EXPORT_SYMBOL(mipi_dsi_dcs_write); EXPORT_SYMBOL(mipi_dsi_dcs_write);
/** /**
* mipi_dsi_dcs_read - send DCS read request command * mipi_dsi_dcs_read() - send DCS read request command
* @dsi: DSI device * @dsi: DSI peripheral device
* @cmd: DCS read command * @cmd: DCS command
* @data: pointer to read buffer * @data: buffer in which to receive data
* @len: length of @data * @len: size of receive buffer
* *
* Function returns number of read bytes or error code. * Return: The number of bytes read or a negative error code on failure.
*/ */
ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
size_t len) size_t len)
{ {
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
struct mipi_dsi_msg msg = { struct mipi_dsi_msg msg = {
.channel = dsi->channel, .channel = dsi->channel,
.type = MIPI_DSI_DCS_READ, .type = MIPI_DSI_DCS_READ,
...@@ -260,15 +585,282 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, ...@@ -260,15 +585,282 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
.rx_len = len .rx_len = len
}; };
if (!ops || !ops->transfer) return mipi_dsi_device_transfer(dsi, &msg);
return -ENOSYS; }
EXPORT_SYMBOL(mipi_dsi_dcs_read);
if (dsi->mode_flags & MIPI_DSI_MODE_LPM) /**
msg.flags = MIPI_DSI_MSG_USE_LPM; * mipi_dsi_dcs_nop() - send DCS nop packet
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_NOP, NULL, 0);
if (err < 0)
return err;
return ops->transfer(dsi->host, &msg); return 0;
} }
EXPORT_SYMBOL(mipi_dsi_dcs_read); EXPORT_SYMBOL(mipi_dsi_dcs_nop);
/**
* mipi_dsi_dcs_soft_reset() - perform a software reset of the display module
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SOFT_RESET, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_soft_reset);
/**
* mipi_dsi_dcs_get_power_mode() - query the display module's current power
* mode
* @dsi: DSI peripheral device
* @mode: return location for the current power mode
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode)
{
ssize_t err;
err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_POWER_MODE, mode,
sizeof(*mode));
if (err <= 0) {
if (err == 0)
err = -ENODATA;
return err;
}
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_get_power_mode);
/**
* mipi_dsi_dcs_get_pixel_format() - gets the pixel format for the RGB image
* data used by the interface
* @dsi: DSI peripheral device
* @format: return location for the pixel format
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format)
{
ssize_t err;
err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_PIXEL_FORMAT, format,
sizeof(*format));
if (err <= 0) {
if (err == 0)
err = -ENODATA;
return err;
}
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_get_pixel_format);
/**
* mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
* display module except interface communication
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_enter_sleep_mode);
/**
* mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
* module
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_exit_sleep_mode);
/**
* mipi_dsi_dcs_set_display_off() - stop displaying the image data on the
* display device
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_display_off);
/**
* mipi_dsi_dcs_set_display_on() - start displaying the image data on the
* display device
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure
*/
int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_display_on);
/**
* mipi_dsi_dcs_set_column_address() - define the column extent of the frame
* memory accessed by the host processor
* @dsi: DSI peripheral device
* @start: first column of frame memory
* @end: last column of frame memory
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
u16 end)
{
u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload,
sizeof(payload));
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_column_address);
/**
* mipi_dsi_dcs_set_page_address() - define the page extent of the frame
* memory accessed by the host processor
* @dsi: DSI peripheral device
* @start: first page of frame memory
* @end: last page of frame memory
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
u16 end)
{
u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload,
sizeof(payload));
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_page_address);
/**
* mipi_dsi_dcs_set_tear_off() - turn off the display module's Tearing Effect
* output signal on the TE signal line
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure
*/
int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_OFF, NULL, 0);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_off);
/**
* mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect
* output signal on the TE signal line.
* @dsi: DSI peripheral device
* @mode: the Tearing Effect Output Line mode
*
* Return: 0 on success or a negative error code on failure
*/
int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
enum mipi_dsi_dcs_tear_mode mode)
{
u8 value = mode;
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value,
sizeof(value));
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on);
/**
* mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
* data used by the interface
* @dsi: DSI peripheral device
* @format: pixel format
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
sizeof(format));
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_pixel_format);
static int mipi_dsi_drv_probe(struct device *dev) static int mipi_dsi_drv_probe(struct device *dev)
{ {
...@@ -295,12 +887,18 @@ static void mipi_dsi_drv_shutdown(struct device *dev) ...@@ -295,12 +887,18 @@ static void mipi_dsi_drv_shutdown(struct device *dev)
} }
/** /**
* mipi_dsi_driver_register - register a driver for DSI devices * mipi_dsi_driver_register_full() - register a driver for DSI devices
* @drv: DSI driver structure * @drv: DSI driver structure
* @owner: owner module
*
* Return: 0 on success or a negative error code on failure.
*/ */
int mipi_dsi_driver_register(struct mipi_dsi_driver *drv) int mipi_dsi_driver_register_full(struct mipi_dsi_driver *drv,
struct module *owner)
{ {
drv->driver.bus = &mipi_dsi_bus_type; drv->driver.bus = &mipi_dsi_bus_type;
drv->driver.owner = owner;
if (drv->probe) if (drv->probe)
drv->driver.probe = mipi_dsi_drv_probe; drv->driver.probe = mipi_dsi_drv_probe;
if (drv->remove) if (drv->remove)
...@@ -310,11 +908,13 @@ int mipi_dsi_driver_register(struct mipi_dsi_driver *drv) ...@@ -310,11 +908,13 @@ int mipi_dsi_driver_register(struct mipi_dsi_driver *drv)
return driver_register(&drv->driver); return driver_register(&drv->driver);
} }
EXPORT_SYMBOL(mipi_dsi_driver_register); EXPORT_SYMBOL(mipi_dsi_driver_register_full);
/** /**
* mipi_dsi_driver_unregister - unregister a driver for DSI devices * mipi_dsi_driver_unregister() - unregister a driver for DSI devices
* @drv: DSI driver structure * @drv: DSI driver structure
*
* Return: 0 on success or a negative error code on failure.
*/ */
void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv) void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv)
{ {
......
...@@ -1236,7 +1236,7 @@ static bool exynos_dsi_is_short_dsi_type(u8 type) ...@@ -1236,7 +1236,7 @@ static bool exynos_dsi_is_short_dsi_type(u8 type)
} }
static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host, static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
struct mipi_dsi_msg *msg) const struct mipi_dsi_msg *msg)
{ {
struct exynos_dsi *dsi = host_to_dsi(host); struct exynos_dsi *dsi = host_to_dsi(host);
struct exynos_dsi_transfer xfer; struct exynos_dsi_transfer xfer;
......
...@@ -27,4 +27,17 @@ config DRM_PANEL_S6E8AA0 ...@@ -27,4 +27,17 @@ config DRM_PANEL_S6E8AA0
select DRM_MIPI_DSI select DRM_MIPI_DSI
select VIDEOMODE_HELPERS select VIDEOMODE_HELPERS
config DRM_PANEL_SHARP_LQ101R1SX01
tristate "Sharp LQ101R1SX01 panel"
depends on OF
depends on DRM_MIPI_DSI
help
Say Y here if you want to enable support for Sharp LQ101R1SX01
TFT-LCD modules. The panel has a 2560x1600 resolution and uses
24 bit RGB per pixel. It provides a dual MIPI DSI interface to
the host and has a built-in LED backlight.
To compile this driver as a module, choose M here: the module
will be called panel-sharp-lq101r1sx01.
endmenu endmenu
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o
obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o
obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
...@@ -145,7 +145,7 @@ static void ld9040_dcs_write(struct ld9040 *ctx, const u8 *data, size_t len) ...@@ -145,7 +145,7 @@ static void ld9040_dcs_write(struct ld9040 *ctx, const u8 *data, size_t len)
if (ctx->error < 0 || len == 0) if (ctx->error < 0 || len == 0)
return; return;
dev_dbg(ctx->dev, "writing dcs seq: %*ph\n", len, data); dev_dbg(ctx->dev, "writing dcs seq: %*ph\n", (int)len, data);
ret = ld9040_spi_write_word(ctx, *data); ret = ld9040_spi_write_word(ctx, *data);
while (!ret && --len) { while (!ret && --len) {
...@@ -154,8 +154,8 @@ static void ld9040_dcs_write(struct ld9040 *ctx, const u8 *data, size_t len) ...@@ -154,8 +154,8 @@ static void ld9040_dcs_write(struct ld9040 *ctx, const u8 *data, size_t len)
} }
if (ret) { if (ret) {
dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, len, dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret,
data); (int)len, data);
ctx->error = ret; ctx->error = ret;
} }
...@@ -336,17 +336,12 @@ static int ld9040_probe(struct spi_device *spi) ...@@ -336,17 +336,12 @@ static int ld9040_probe(struct spi_device *spi)
if (ret < 0) if (ret < 0)
return ret; return ret;
ctx->reset_gpio = devm_gpiod_get(dev, "reset"); ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ctx->reset_gpio)) { if (IS_ERR(ctx->reset_gpio)) {
dev_err(dev, "cannot get reset-gpios %ld\n", dev_err(dev, "cannot get reset-gpios %ld\n",
PTR_ERR(ctx->reset_gpio)); PTR_ERR(ctx->reset_gpio));
return PTR_ERR(ctx->reset_gpio); return PTR_ERR(ctx->reset_gpio);
} }
ret = gpiod_direction_output(ctx->reset_gpio, 1);
if (ret < 0) {
dev_err(dev, "cannot configure reset-gpios %d\n", ret);
return ret;
}
spi->bits_per_word = 9; spi->bits_per_word = 9;
ret = spi_setup(spi); ret = spi_setup(spi);
......
...@@ -141,10 +141,10 @@ static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len) ...@@ -141,10 +141,10 @@ static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len)
if (ctx->error < 0) if (ctx->error < 0)
return; return;
ret = mipi_dsi_dcs_write(dsi, data, len); ret = mipi_dsi_dcs_write_buffer(dsi, data, len);
if (ret < 0) { if (ret < 0) {
dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret, len, dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret,
data); (int)len, data);
ctx->error = ret; ctx->error = ret;
} }
} }
...@@ -800,27 +800,15 @@ static void s6e8aa0_panel_init(struct s6e8aa0 *ctx) ...@@ -800,27 +800,15 @@ static void s6e8aa0_panel_init(struct s6e8aa0 *ctx)
} }
static void s6e8aa0_set_maximum_return_packet_size(struct s6e8aa0 *ctx, static void s6e8aa0_set_maximum_return_packet_size(struct s6e8aa0 *ctx,
int size) u16 size)
{ {
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
u8 buf[] = {size, 0};
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
.tx_len = sizeof(buf),
.tx_buf = buf
};
int ret; int ret;
if (ctx->error < 0) if (ctx->error < 0)
return; return;
if (!ops || !ops->transfer) ret = mipi_dsi_set_maximum_return_packet_size(dsi, size);
ret = -EIO;
else
ret = ops->transfer(dsi->host, &msg);
if (ret < 0) { if (ret < 0) {
dev_err(ctx->dev, dev_err(ctx->dev,
"error %d setting maximum return packet size to %d\n", "error %d setting maximum return packet size to %d\n",
...@@ -1019,17 +1007,12 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi) ...@@ -1019,17 +1007,12 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi)
return ret; return ret;
} }
ctx->reset_gpio = devm_gpiod_get(dev, "reset"); ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ctx->reset_gpio)) { if (IS_ERR(ctx->reset_gpio)) {
dev_err(dev, "cannot get reset-gpios %ld\n", dev_err(dev, "cannot get reset-gpios %ld\n",
PTR_ERR(ctx->reset_gpio)); PTR_ERR(ctx->reset_gpio));
return PTR_ERR(ctx->reset_gpio); return PTR_ERR(ctx->reset_gpio);
} }
ret = gpiod_direction_output(ctx->reset_gpio, 1);
if (ret < 0) {
dev_err(dev, "cannot configure reset-gpios %d\n", ret);
return ret;
}
ctx->brightness = GAMMA_LEVEL_NUM - 1; ctx->brightness = GAMMA_LEVEL_NUM - 1;
...@@ -1069,7 +1052,6 @@ static struct mipi_dsi_driver s6e8aa0_driver = { ...@@ -1069,7 +1052,6 @@ static struct mipi_dsi_driver s6e8aa0_driver = {
.remove = s6e8aa0_remove, .remove = s6e8aa0_remove,
.driver = { .driver = {
.name = "panel_s6e8aa0", .name = "panel_s6e8aa0",
.owner = THIS_MODULE,
.of_match_table = s6e8aa0_of_match, .of_match_table = s6e8aa0_of_match,
}, },
}; };
......
/*
* Copyright (C) 2014 NVIDIA Corporation
*
* 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.
*/
#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>
#include <linux/host1x.h>
struct sharp_panel {
struct drm_panel base;
/* the datasheet refers to them as DSI-LINK1 and DSI-LINK2 */
struct mipi_dsi_device *link1;
struct mipi_dsi_device *link2;
struct backlight_device *backlight;
struct regulator *supply;
bool prepared;
bool enabled;
const struct drm_display_mode *mode;
};
static inline struct sharp_panel *to_sharp_panel(struct drm_panel *panel)
{
return container_of(panel, struct sharp_panel, base);
}
static int sharp_panel_write(struct sharp_panel *sharp, u16 offset, u8 value)
{
u8 payload[3] = { offset >> 8, offset & 0xff, value };
struct mipi_dsi_device *dsi = sharp->link1;
ssize_t err;
err = mipi_dsi_generic_write(dsi, payload, sizeof(payload));
if (err < 0) {
dev_err(&dsi->dev, "failed to write %02x to %04x: %zd\n",
value, offset, err);
return err;
}
err = mipi_dsi_dcs_nop(dsi);
if (err < 0) {
dev_err(&dsi->dev, "failed to send DCS nop: %zd\n", err);
return err;
}
usleep_range(10, 20);
return 0;
}
static __maybe_unused int sharp_panel_read(struct sharp_panel *sharp,
u16 offset, u8 *value)
{
ssize_t err;
cpu_to_be16s(&offset);
err = mipi_dsi_generic_read(sharp->link1, &offset, sizeof(offset),
value, sizeof(*value));
if (err < 0)
dev_err(&sharp->link1->dev, "failed to read from %04x: %zd\n",
offset, err);
return err;
}
static int sharp_panel_disable(struct drm_panel *panel)
{
struct sharp_panel *sharp = to_sharp_panel(panel);
if (!sharp->enabled)
return 0;
if (sharp->backlight) {
sharp->backlight->props.power = FB_BLANK_POWERDOWN;
backlight_update_status(sharp->backlight);
}
sharp->enabled = false;
return 0;
}
static int sharp_panel_unprepare(struct drm_panel *panel)
{
struct sharp_panel *sharp = to_sharp_panel(panel);
int err;
if (!sharp->prepared)
return 0;
err = mipi_dsi_dcs_set_display_off(sharp->link1);
if (err < 0)
dev_err(panel->dev, "failed to set display off: %d\n", err);
err = mipi_dsi_dcs_enter_sleep_mode(sharp->link1);
if (err < 0)
dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
msleep(120);
regulator_disable(sharp->supply);
sharp->prepared = false;
return 0;
}
static int sharp_setup_symmetrical_split(struct mipi_dsi_device *left,
struct mipi_dsi_device *right,
const struct drm_display_mode *mode)
{
int err;
err = mipi_dsi_dcs_set_column_address(left, 0, mode->hdisplay / 2 - 1);
if (err < 0) {
dev_err(&left->dev, "failed to set column address: %d\n", err);
return err;
}
err = mipi_dsi_dcs_set_page_address(left, 0, mode->vdisplay - 1);
if (err < 0) {
dev_err(&left->dev, "failed to set page address: %d\n", err);
return err;
}
err = mipi_dsi_dcs_set_column_address(right, mode->hdisplay / 2,
mode->hdisplay - 1);
if (err < 0) {
dev_err(&right->dev, "failed to set column address: %d\n", err);
return err;
}
err = mipi_dsi_dcs_set_page_address(right, 0, mode->vdisplay - 1);
if (err < 0) {
dev_err(&right->dev, "failed to set page address: %d\n", err);
return err;
}
return 0;
}
static int sharp_panel_prepare(struct drm_panel *panel)
{
struct sharp_panel *sharp = to_sharp_panel(panel);
u8 format = MIPI_DCS_PIXEL_FMT_24BIT;
int err;
if (sharp->prepared)
return 0;
err = regulator_enable(sharp->supply);
if (err < 0)
return err;
usleep_range(10000, 20000);
err = mipi_dsi_dcs_soft_reset(sharp->link1);
if (err < 0) {
dev_err(panel->dev, "soft reset failed: %d\n", err);
goto poweroff;
}
msleep(120);
err = mipi_dsi_dcs_exit_sleep_mode(sharp->link1);
if (err < 0) {
dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
goto poweroff;
}
/*
* The MIPI DCS specification mandates this delay only between the
* exit_sleep_mode and enter_sleep_mode commands, so it isn't strictly
* necessary here.
*/
/*
msleep(120);
*/
/* set left-right mode */
err = sharp_panel_write(sharp, 0x1000, 0x2a);
if (err < 0) {
dev_err(panel->dev, "failed to set left-right mode: %d\n", err);
goto poweroff;
}
/* enable command mode */
err = sharp_panel_write(sharp, 0x1001, 0x01);
if (err < 0) {
dev_err(panel->dev, "failed to enable command mode: %d\n", err);
goto poweroff;
}
err = mipi_dsi_dcs_set_pixel_format(sharp->link1, format);
if (err < 0) {
dev_err(panel->dev, "failed to set pixel format: %d\n", err);
goto poweroff;
}
/*
* TODO: The device supports both left-right and even-odd split
* configurations, but this driver currently supports only the left-
* right split. To support a different mode a mechanism needs to be
* put in place to communicate the configuration back to the DSI host
* controller.
*/
err = sharp_setup_symmetrical_split(sharp->link1, sharp->link2,
sharp->mode);
if (err < 0) {
dev_err(panel->dev, "failed to set up symmetrical split: %d\n",
err);
goto poweroff;
}
err = mipi_dsi_dcs_set_display_on(sharp->link1);
if (err < 0) {
dev_err(panel->dev, "failed to set display on: %d\n", err);
goto poweroff;
}
sharp->prepared = true;
return 0;
poweroff:
regulator_disable(sharp->supply);
return err;
}
static int sharp_panel_enable(struct drm_panel *panel)
{
struct sharp_panel *sharp = to_sharp_panel(panel);
if (sharp->enabled)
return 0;
if (sharp->backlight) {
sharp->backlight->props.power = FB_BLANK_UNBLANK;
backlight_update_status(sharp->backlight);
}
sharp->enabled = true;
return 0;
}
static const struct drm_display_mode default_mode = {
.clock = 278000,
.hdisplay = 2560,
.hsync_start = 2560 + 128,
.hsync_end = 2560 + 128 + 64,
.htotal = 2560 + 128 + 64 + 64,
.vdisplay = 1600,
.vsync_start = 1600 + 4,
.vsync_end = 1600 + 4 + 8,
.vtotal = 1600 + 4 + 8 + 32,
.vrefresh = 60,
};
static int sharp_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 sharp_panel_funcs = {
.disable = sharp_panel_disable,
.unprepare = sharp_panel_unprepare,
.prepare = sharp_panel_prepare,
.enable = sharp_panel_enable,
.get_modes = sharp_panel_get_modes,
};
static const struct of_device_id sharp_of_match[] = {
{ .compatible = "sharp,lq101r1sx01", },
{ }
};
MODULE_DEVICE_TABLE(of, sharp_of_match);
static int sharp_panel_add(struct sharp_panel *sharp)
{
struct device_node *np;
int err;
sharp->mode = &default_mode;
sharp->supply = devm_regulator_get(&sharp->link1->dev, "power");
if (IS_ERR(sharp->supply))
return PTR_ERR(sharp->supply);
np = of_parse_phandle(sharp->link1->dev.of_node, "backlight", 0);
if (np) {
sharp->backlight = of_find_backlight_by_node(np);
of_node_put(np);
if (!sharp->backlight)
return -EPROBE_DEFER;
}
drm_panel_init(&sharp->base);
sharp->base.funcs = &sharp_panel_funcs;
sharp->base.dev = &sharp->link1->dev;
err = drm_panel_add(&sharp->base);
if (err < 0)
goto put_backlight;
return 0;
put_backlight:
if (sharp->backlight)
put_device(&sharp->backlight->dev);
return err;
}
static void sharp_panel_del(struct sharp_panel *sharp)
{
if (sharp->base.dev)
drm_panel_remove(&sharp->base);
if (sharp->backlight)
put_device(&sharp->backlight->dev);
if (sharp->link2)
put_device(&sharp->link2->dev);
}
static int sharp_panel_probe(struct mipi_dsi_device *dsi)
{
struct mipi_dsi_device *secondary = NULL;
struct sharp_panel *sharp;
struct device_node *np;
int err;
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_LPM;
/* Find DSI-LINK1 */
np = of_parse_phandle(dsi->dev.of_node, "link2", 0);
if (np) {
secondary = of_find_mipi_dsi_device_by_node(np);
of_node_put(np);
if (!secondary)
return -EPROBE_DEFER;
}
/* register a panel for only the DSI-LINK1 interface */
if (secondary) {
sharp = devm_kzalloc(&dsi->dev, sizeof(*sharp), GFP_KERNEL);
if (!sharp) {
put_device(&secondary->dev);
return -ENOMEM;
}
mipi_dsi_set_drvdata(dsi, sharp);
sharp->link2 = secondary;
sharp->link1 = dsi;
err = sharp_panel_add(sharp);
if (err < 0) {
put_device(&secondary->dev);
return err;
}
}
err = mipi_dsi_attach(dsi);
if (err < 0) {
if (secondary)
sharp_panel_del(sharp);
return err;
}
return 0;
}
static int sharp_panel_remove(struct mipi_dsi_device *dsi)
{
struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi);
int err;
/* only detach from host for the DSI-LINK2 interface */
if (!sharp) {
mipi_dsi_detach(dsi);
return 0;
}
err = sharp_panel_disable(&sharp->base);
if (err < 0)
dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
err = mipi_dsi_detach(dsi);
if (err < 0)
dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
drm_panel_detach(&sharp->base);
sharp_panel_del(sharp);
return 0;
}
static void sharp_panel_shutdown(struct mipi_dsi_device *dsi)
{
struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi);
/* nothing to do for DSI-LINK2 */
if (!sharp)
return;
sharp_panel_disable(&sharp->base);
}
static struct mipi_dsi_driver sharp_panel_driver = {
.driver = {
.name = "panel-sharp-lq101r1sx01",
.of_match_table = sharp_of_match,
},
.probe = sharp_panel_probe,
.remove = sharp_panel_remove,
.shutdown = sharp_panel_shutdown,
};
module_mipi_dsi_driver(sharp_panel_driver);
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_DESCRIPTION("Sharp LQ101R1SX01 panel driver");
MODULE_LICENSE("GPL v2");
...@@ -247,21 +247,14 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) ...@@ -247,21 +247,14 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
if (IS_ERR(panel->supply)) if (IS_ERR(panel->supply))
return PTR_ERR(panel->supply); return PTR_ERR(panel->supply);
panel->enable_gpio = devm_gpiod_get_optional(dev, "enable"); panel->enable_gpio = devm_gpiod_get_optional(dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR(panel->enable_gpio)) { if (IS_ERR(panel->enable_gpio)) {
err = PTR_ERR(panel->enable_gpio); err = PTR_ERR(panel->enable_gpio);
dev_err(dev, "failed to request GPIO: %d\n", err); dev_err(dev, "failed to request GPIO: %d\n", err);
return err; return err;
} }
if (panel->enable_gpio) {
err = gpiod_direction_output(panel->enable_gpio, 0);
if (err < 0) {
dev_err(dev, "failed to setup GPIO: %d\n", err);
return err;
}
}
backlight = of_parse_phandle(dev->of_node, "backlight", 0); backlight = of_parse_phandle(dev->of_node, "backlight", 0);
if (backlight) { if (backlight) {
panel->backlight = of_find_backlight_by_node(backlight); panel->backlight = of_find_backlight_by_node(backlight);
...@@ -376,6 +369,29 @@ static const struct panel_desc auo_b101xtn01 = { ...@@ -376,6 +369,29 @@ static const struct panel_desc auo_b101xtn01 = {
}, },
}; };
static const struct drm_display_mode auo_b116xw03_mode = {
.clock = 70589,
.hdisplay = 1366,
.hsync_start = 1366 + 40,
.hsync_end = 1366 + 40 + 40,
.htotal = 1366 + 40 + 40 + 32,
.vdisplay = 768,
.vsync_start = 768 + 10,
.vsync_end = 768 + 10 + 12,
.vtotal = 768 + 10 + 12 + 6,
.vrefresh = 60,
};
static const struct panel_desc auo_b116xw03 = {
.modes = &auo_b116xw03_mode,
.num_modes = 1,
.bpc = 6,
.size = {
.width = 256,
.height = 144,
},
};
static const struct drm_display_mode auo_b133xtn01_mode = { static const struct drm_display_mode auo_b133xtn01_mode = {
.clock = 69500, .clock = 69500,
.hdisplay = 1366, .hdisplay = 1366,
...@@ -415,6 +431,7 @@ static const struct drm_display_mode auo_b133htn01_mode = { ...@@ -415,6 +431,7 @@ static const struct drm_display_mode auo_b133htn01_mode = {
static const struct panel_desc auo_b133htn01 = { static const struct panel_desc auo_b133htn01 = {
.modes = &auo_b133htn01_mode, .modes = &auo_b133htn01_mode,
.num_modes = 1, .num_modes = 1,
.bpc = 6,
.size = { .size = {
.width = 293, .width = 293,
.height = 165, .height = 165,
...@@ -536,22 +553,92 @@ static const struct drm_display_mode foxlink_fl500wvr00_a0t_mode = { ...@@ -536,22 +553,92 @@ static const struct drm_display_mode foxlink_fl500wvr00_a0t_mode = {
static const struct panel_desc foxlink_fl500wvr00_a0t = { static const struct panel_desc foxlink_fl500wvr00_a0t = {
.modes = &foxlink_fl500wvr00_a0t_mode, .modes = &foxlink_fl500wvr00_a0t_mode,
.num_modes = 1, .num_modes = 1,
.bpc = 8,
.size = { .size = {
.width = 108, .width = 108,
.height = 65, .height = 65,
}, },
}; };
static const struct drm_display_mode innolux_n116bge_mode = { static const struct drm_display_mode hannstar_hsd070pww1_mode = {
.clock = 71100,
.hdisplay = 1280,
.hsync_start = 1280 + 1,
.hsync_end = 1280 + 1 + 158,
.htotal = 1280 + 1 + 158 + 1,
.vdisplay = 800,
.vsync_start = 800 + 1,
.vsync_end = 800 + 1 + 21,
.vtotal = 800 + 1 + 21 + 1,
.vrefresh = 60,
};
static const struct panel_desc hannstar_hsd070pww1 = {
.modes = &hannstar_hsd070pww1_mode,
.num_modes = 1,
.bpc = 6,
.size = {
.width = 151,
.height = 94,
},
};
static const struct drm_display_mode hitachi_tx23d38vm0caa_mode = {
.clock = 33333,
.hdisplay = 800,
.hsync_start = 800 + 85,
.hsync_end = 800 + 85 + 86,
.htotal = 800 + 85 + 86 + 85,
.vdisplay = 480,
.vsync_start = 480 + 16,
.vsync_end = 480 + 16 + 13,
.vtotal = 480 + 16 + 13 + 16,
.vrefresh = 60,
};
static const struct panel_desc hitachi_tx23d38vm0caa = {
.modes = &hitachi_tx23d38vm0caa_mode,
.num_modes = 1,
.bpc = 6,
.size = {
.width = 195,
.height = 117,
},
};
static const struct drm_display_mode innolux_g121i1_l01_mode = {
.clock = 71000, .clock = 71000,
.hdisplay = 1280,
.hsync_start = 1280 + 64,
.hsync_end = 1280 + 64 + 32,
.htotal = 1280 + 64 + 32 + 64,
.vdisplay = 800,
.vsync_start = 800 + 9,
.vsync_end = 800 + 9 + 6,
.vtotal = 800 + 9 + 6 + 9,
.vrefresh = 60,
};
static const struct panel_desc innolux_g121i1_l01 = {
.modes = &innolux_g121i1_l01_mode,
.num_modes = 1,
.bpc = 6,
.size = {
.width = 261,
.height = 163,
},
};
static const struct drm_display_mode innolux_n116bge_mode = {
.clock = 76420,
.hdisplay = 1366, .hdisplay = 1366,
.hsync_start = 1366 + 64, .hsync_start = 1366 + 136,
.hsync_end = 1366 + 64 + 6, .hsync_end = 1366 + 136 + 30,
.htotal = 1366 + 64 + 6 + 64, .htotal = 1366 + 136 + 30 + 60,
.vdisplay = 768, .vdisplay = 768,
.vsync_start = 768 + 8, .vsync_start = 768 + 8,
.vsync_end = 768 + 8 + 4, .vsync_end = 768 + 8 + 12,
.vtotal = 768 + 8 + 4 + 8, .vtotal = 768 + 8 + 12 + 12,
.vrefresh = 60, .vrefresh = 60,
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
}; };
...@@ -642,6 +729,9 @@ static const struct of_device_id platform_of_match[] = { ...@@ -642,6 +729,9 @@ static const struct of_device_id platform_of_match[] = {
}, { }, {
.compatible = "auo,b101xtn01", .compatible = "auo,b101xtn01",
.data = &auo_b101xtn01, .data = &auo_b101xtn01,
}, {
.compatible = "auo,b116xw03",
.data = &auo_b116xw03,
}, { }, {
.compatible = "auo,b133htn01", .compatible = "auo,b133htn01",
.data = &auo_b133htn01, .data = &auo_b133htn01,
...@@ -666,6 +756,15 @@ static const struct of_device_id platform_of_match[] = { ...@@ -666,6 +756,15 @@ static const struct of_device_id platform_of_match[] = {
}, { }, {
.compatible = "foxlink,fl500wvr00-a0t", .compatible = "foxlink,fl500wvr00-a0t",
.data = &foxlink_fl500wvr00_a0t, .data = &foxlink_fl500wvr00_a0t,
}, {
.compatible = "hannstar,hsd070pww1",
.data = &hannstar_hsd070pww1,
}, {
.compatible = "hit,tx23d38vm0caa",
.data = &hitachi_tx23d38vm0caa
}, {
.compatible ="innolux,g121i1-l01",
.data = &innolux_g121i1_l01
}, { }, {
.compatible = "innolux,n116bge", .compatible = "innolux,n116bge",
.data = &innolux_n116bge, .data = &innolux_n116bge,
...@@ -741,6 +840,7 @@ static const struct panel_desc_dsi lg_ld070wx3_sl01 = { ...@@ -741,6 +840,7 @@ static const struct panel_desc_dsi lg_ld070wx3_sl01 = {
.desc = { .desc = {
.modes = &lg_ld070wx3_sl01_mode, .modes = &lg_ld070wx3_sl01_mode,
.num_modes = 1, .num_modes = 1,
.bpc = 8,
.size = { .size = {
.width = 94, .width = 94,
.height = 151, .height = 151,
...@@ -768,6 +868,7 @@ static const struct panel_desc_dsi lg_lh500wx1_sd03 = { ...@@ -768,6 +868,7 @@ static const struct panel_desc_dsi lg_lh500wx1_sd03 = {
.desc = { .desc = {
.modes = &lg_lh500wx1_sd03_mode, .modes = &lg_lh500wx1_sd03_mode,
.num_modes = 1, .num_modes = 1,
.bpc = 8,
.size = { .size = {
.width = 62, .width = 62,
.height = 110, .height = 110,
...@@ -795,6 +896,7 @@ static const struct panel_desc_dsi panasonic_vvx10f004b00 = { ...@@ -795,6 +896,7 @@ static const struct panel_desc_dsi panasonic_vvx10f004b00 = {
.desc = { .desc = {
.modes = &panasonic_vvx10f004b00_mode, .modes = &panasonic_vvx10f004b00_mode,
.num_modes = 1, .num_modes = 1,
.bpc = 8,
.size = { .size = {
.width = 217, .width = 217,
.height = 136, .height = 136,
...@@ -864,7 +966,6 @@ static void panel_simple_dsi_shutdown(struct mipi_dsi_device *dsi) ...@@ -864,7 +966,6 @@ static void panel_simple_dsi_shutdown(struct mipi_dsi_device *dsi)
static struct mipi_dsi_driver panel_simple_dsi_driver = { static struct mipi_dsi_driver panel_simple_dsi_driver = {
.driver = { .driver = {
.name = "panel-simple-dsi", .name = "panel-simple-dsi",
.owner = THIS_MODULE,
.of_match_table = dsi_of_match, .of_match_table = dsi_of_match,
}, },
.probe = panel_simple_dsi_probe, .probe = panel_simple_dsi_probe,
......
...@@ -26,6 +26,7 @@ struct mipi_dsi_device; ...@@ -26,6 +26,7 @@ struct mipi_dsi_device;
* struct mipi_dsi_msg - read/write DSI buffer * struct mipi_dsi_msg - read/write DSI buffer
* @channel: virtual channel id * @channel: virtual channel id
* @type: payload data type * @type: payload data type
* @flags: flags controlling this message transmission
* @tx_len: length of @tx_buf * @tx_len: length of @tx_buf
* @tx_buf: data to be written * @tx_buf: data to be written
* @rx_len: length of @rx_buf * @rx_len: length of @rx_buf
...@@ -43,12 +44,44 @@ struct mipi_dsi_msg { ...@@ -43,12 +44,44 @@ struct mipi_dsi_msg {
void *rx_buf; void *rx_buf;
}; };
bool mipi_dsi_packet_format_is_short(u8 type);
bool mipi_dsi_packet_format_is_long(u8 type);
/**
* struct mipi_dsi_packet - represents a MIPI DSI packet in protocol format
* @size: size (in bytes) of the packet
* @header: the four bytes that make up the header (Data ID, Word Count or
* Packet Data, and ECC)
* @payload_length: number of bytes in the payload
* @payload: a pointer to a buffer containing the payload, if any
*/
struct mipi_dsi_packet {
size_t size;
u8 header[4];
size_t payload_length;
const u8 *payload;
};
int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
const struct mipi_dsi_msg *msg);
/** /**
* struct mipi_dsi_host_ops - DSI bus operations * struct mipi_dsi_host_ops - DSI bus operations
* @attach: attach DSI device to DSI host * @attach: attach DSI device to DSI host
* @detach: detach DSI device from DSI host * @detach: detach DSI device from DSI host
* @transfer: send and/or receive DSI packet, return number of received bytes, * @transfer: transmit a DSI packet
* or error *
* DSI packets transmitted by .transfer() are passed in as mipi_dsi_msg
* structures. This structure contains information about the type of packet
* being transmitted as well as the transmit and receive buffers. When an
* error is encountered during transmission, this function will return a
* negative error code. On success it shall return the number of bytes
* transmitted for write packets or the number of bytes received for read
* packets.
*
* Note that typically DSI packet transmission is atomic, so the .transfer()
* function will seldomly return anything other than the number of bytes
* contained in the transmit buffer on success.
*/ */
struct mipi_dsi_host_ops { struct mipi_dsi_host_ops {
int (*attach)(struct mipi_dsi_host *host, int (*attach)(struct mipi_dsi_host *host,
...@@ -56,7 +89,7 @@ struct mipi_dsi_host_ops { ...@@ -56,7 +89,7 @@ struct mipi_dsi_host_ops {
int (*detach)(struct mipi_dsi_host *host, int (*detach)(struct mipi_dsi_host *host,
struct mipi_dsi_device *dsi); struct mipi_dsi_device *dsi);
ssize_t (*transfer)(struct mipi_dsi_host *host, ssize_t (*transfer)(struct mipi_dsi_host *host,
struct mipi_dsi_msg *msg); const struct mipi_dsi_msg *msg);
}; };
/** /**
...@@ -130,12 +163,57 @@ static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev) ...@@ -130,12 +163,57 @@ 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);
} }
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);
ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data, int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
size_t len); u16 value);
ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
size_t size);
ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
size_t num_params, void *data, size_t size);
/**
* enum mipi_dsi_dcs_tear_mode - Tearing Effect Output Line mode
* @MIPI_DSI_DCS_TEAR_MODE_VBLANK: the TE output line consists of V-Blanking
* information only
* @MIPI_DSI_DCS_TEAR_MODE_VHBLANK : the TE output line consists of both
* V-Blanking and H-Blanking information
*/
enum mipi_dsi_dcs_tear_mode {
MIPI_DSI_DCS_TEAR_MODE_VBLANK,
MIPI_DSI_DCS_TEAR_MODE_VHBLANK,
};
#define MIPI_DSI_DCS_POWER_MODE_DISPLAY (1 << 2)
#define MIPI_DSI_DCS_POWER_MODE_NORMAL (1 << 3)
#define MIPI_DSI_DCS_POWER_MODE_SLEEP (1 << 4)
#define MIPI_DSI_DCS_POWER_MODE_PARTIAL (1 << 5)
#define MIPI_DSI_DCS_POWER_MODE_IDLE (1 << 6)
ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
const void *data, size_t len);
ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
const void *data, size_t len);
ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
size_t len); size_t len);
int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode);
int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format);
int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
u16 end);
int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
u16 end);
int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
enum mipi_dsi_dcs_tear_mode mode);
int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format);
/** /**
* struct mipi_dsi_driver - DSI driver * struct mipi_dsi_driver - DSI driver
...@@ -167,9 +245,13 @@ static inline void mipi_dsi_set_drvdata(struct mipi_dsi_device *dsi, void *data) ...@@ -167,9 +245,13 @@ static inline void mipi_dsi_set_drvdata(struct mipi_dsi_device *dsi, void *data)
dev_set_drvdata(&dsi->dev, data); dev_set_drvdata(&dsi->dev, data);
} }
int mipi_dsi_driver_register(struct mipi_dsi_driver *driver); int mipi_dsi_driver_register_full(struct mipi_dsi_driver *driver,
struct module *owner);
void mipi_dsi_driver_unregister(struct mipi_dsi_driver *driver); void mipi_dsi_driver_unregister(struct mipi_dsi_driver *driver);
#define mipi_dsi_driver_register(driver) \
mipi_dsi_driver_register_full(driver, THIS_MODULE)
#define module_mipi_dsi_driver(__mipi_dsi_driver) \ #define module_mipi_dsi_driver(__mipi_dsi_driver) \
module_driver(__mipi_dsi_driver, mipi_dsi_driver_register, \ module_driver(__mipi_dsi_driver, mipi_dsi_driver_register, \
mipi_dsi_driver_unregister) mipi_dsi_driver_unregister)
......
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