Commit 5e2368a3 authored by Dave Airlie's avatar Dave Airlie

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

drm/panel: Changes for v4.6-rc1

This contains a refactoring of parts of the DSI core to allow creating
DSI devices from non-DSI control busses (i.e. I2C, SPI, ...).

Other than that there's support for a couple of new panels as well as
a few cleanup patches.

* tag 'drm/panel/for-4.6-rc1' of http://anongit.freedesktop.org/git/tegra/linux:
  drm/bridge: Make (pre/post) enable/disable callbacks optional
  drm/panel: simple: Add URT UMSH-8596MD-xT panels support
  dt-bindings: Add URT UMSH-8596MD-xT panel bindings
  of: Add United Radiant Technology Corporation vendor prefix
  drm/panel: simple: Support for LG lp120up1 panel
  dt-bindings: Add LG lp120up1 panel bindings
  drm/panel: simple: Fix g121x1_l03 hsync/vsync polarity
  drm/dsi: Get DSI host by DT device node
  drm/dsi: Add routine to unregister a DSI device
  drm/dsi: Try to match non-DT DSI devices
  drm/dsi: Use mipi_dsi_device_register_full() for DSI device creation
  drm/dsi: Check for CONFIG_OF when defining of_mipi_dsi_device_add()
parents 0e5dc9a8 c8a3b2ae
LG 12.0" (1920x1280 pixels) TFT LCD panel
Required properties:
- compatible: should be "lg,lp120up1"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.
United Radiant Technology UMSH-8596MD-xT 7.0" WVGA TFT LCD panel
Supported are LVDS versions (-11T, -19T) and parallel ones
(-T, -1T, -7T, -20T).
Required properties:
- compatible: should be one of:
"urt,umsh-8596md-t",
"urt,umsh-8596md-1t",
"urt,umsh-8596md-7t",
"urt,umsh-8596md-11t",
"urt,umsh-8596md-19t",
"urt,umsh-8596md-20t".
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.
...@@ -240,6 +240,7 @@ tplink TP-LINK Technologies Co., Ltd. ...@@ -240,6 +240,7 @@ tplink TP-LINK Technologies Co., Ltd.
tronfy Tronfy tronfy Tronfy
truly Truly Semiconductors Limited truly Truly Semiconductors Limited
upisemi uPI Semiconductor Corp. upisemi uPI Semiconductor Corp.
urt United Radiant Technology Corporation
usi Universal Scientific Industrial Co., Ltd. usi Universal Scientific Industrial Co., Ltd.
v3 V3 Semiconductor v3 V3 Semiconductor
variscite Variscite Ltd. variscite Variscite Ltd.
......
...@@ -186,6 +186,7 @@ void drm_bridge_disable(struct drm_bridge *bridge) ...@@ -186,6 +186,7 @@ void drm_bridge_disable(struct drm_bridge *bridge)
drm_bridge_disable(bridge->next); drm_bridge_disable(bridge->next);
if (bridge->funcs->disable)
bridge->funcs->disable(bridge); bridge->funcs->disable(bridge);
} }
EXPORT_SYMBOL(drm_bridge_disable); EXPORT_SYMBOL(drm_bridge_disable);
...@@ -206,6 +207,7 @@ void drm_bridge_post_disable(struct drm_bridge *bridge) ...@@ -206,6 +207,7 @@ void drm_bridge_post_disable(struct drm_bridge *bridge)
if (!bridge) if (!bridge)
return; return;
if (bridge->funcs->post_disable)
bridge->funcs->post_disable(bridge); bridge->funcs->post_disable(bridge);
drm_bridge_post_disable(bridge->next); drm_bridge_post_disable(bridge->next);
...@@ -256,6 +258,7 @@ void drm_bridge_pre_enable(struct drm_bridge *bridge) ...@@ -256,6 +258,7 @@ void drm_bridge_pre_enable(struct drm_bridge *bridge)
drm_bridge_pre_enable(bridge->next); drm_bridge_pre_enable(bridge->next);
if (bridge->funcs->pre_enable)
bridge->funcs->pre_enable(bridge); bridge->funcs->pre_enable(bridge);
} }
EXPORT_SYMBOL(drm_bridge_pre_enable); EXPORT_SYMBOL(drm_bridge_pre_enable);
...@@ -276,6 +279,7 @@ void drm_bridge_enable(struct drm_bridge *bridge) ...@@ -276,6 +279,7 @@ void drm_bridge_enable(struct drm_bridge *bridge)
if (!bridge) if (!bridge)
return; return;
if (bridge->funcs->enable)
bridge->funcs->enable(bridge); bridge->funcs->enable(bridge);
drm_bridge_enable(bridge->next); drm_bridge_enable(bridge->next);
......
...@@ -47,7 +47,17 @@ ...@@ -47,7 +47,17 @@
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); struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
/* attempt OF style match */
if (of_driver_match_device(dev, drv))
return 1;
/* compare DSI device and driver names */
if (!strcmp(dsi->name, drv->name))
return 1;
return 0;
} }
static const struct dev_pm_ops mipi_dsi_device_pm_ops = { static const struct dev_pm_ops mipi_dsi_device_pm_ops = {
...@@ -129,14 +139,20 @@ static int mipi_dsi_device_add(struct mipi_dsi_device *dsi) ...@@ -129,14 +139,20 @@ static int mipi_dsi_device_add(struct mipi_dsi_device *dsi)
return device_add(&dsi->dev); return device_add(&dsi->dev);
} }
#if IS_ENABLED(CONFIG_OF)
static struct mipi_dsi_device * static struct mipi_dsi_device *
of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node) of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node)
{ {
struct mipi_dsi_device *dsi;
struct device *dev = host->dev; struct device *dev = host->dev;
struct mipi_dsi_device_info info = { };
int ret; int ret;
u32 reg; u32 reg;
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
dev_err(dev, "modalias failure on %s\n", node->full_name);
return ERR_PTR(-EINVAL);
}
ret = of_property_read_u32(node, "reg", &reg); ret = of_property_read_u32(node, "reg", &reg);
if (ret) { if (ret) {
dev_err(dev, "device node %s has no valid reg property: %d\n", dev_err(dev, "device node %s has no valid reg property: %d\n",
...@@ -144,32 +160,111 @@ of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node) ...@@ -144,32 +160,111 @@ of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
if (reg > 3) { info.channel = reg;
dev_err(dev, "device node %s has invalid reg property: %u\n", info.node = of_node_get(node);
node->full_name, reg);
return mipi_dsi_device_register_full(host, &info);
}
#else
static struct mipi_dsi_device *
of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node)
{
return ERR_PTR(-ENODEV);
}
#endif
/**
* mipi_dsi_device_register_full - create a MIPI DSI device
* @host: DSI host to which this device is connected
* @info: pointer to template containing DSI device information
*
* Create a MIPI DSI device by using the device information provided by
* mipi_dsi_device_info template
*
* Returns:
* A pointer to the newly created MIPI DSI device, or, a pointer encoded
* with an error
*/
struct mipi_dsi_device *
mipi_dsi_device_register_full(struct mipi_dsi_host *host,
const struct mipi_dsi_device_info *info)
{
struct mipi_dsi_device *dsi;
struct device *dev = host->dev;
int ret;
if (!info) {
dev_err(dev, "invalid mipi_dsi_device_info pointer\n");
return ERR_PTR(-EINVAL);
}
if (info->channel > 3) {
dev_err(dev, "invalid virtual channel: %u\n", info->channel);
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
dsi = mipi_dsi_device_alloc(host); dsi = mipi_dsi_device_alloc(host);
if (IS_ERR(dsi)) { if (IS_ERR(dsi)) {
dev_err(dev, "failed to allocate DSI device %s: %ld\n", dev_err(dev, "failed to allocate DSI device %ld\n",
node->full_name, PTR_ERR(dsi)); PTR_ERR(dsi));
return dsi; return dsi;
} }
dsi->dev.of_node = of_node_get(node); dsi->dev.of_node = info->node;
dsi->channel = reg; dsi->channel = info->channel;
strlcpy(dsi->name, info->type, sizeof(dsi->name));
ret = mipi_dsi_device_add(dsi); ret = mipi_dsi_device_add(dsi);
if (ret) { if (ret) {
dev_err(dev, "failed to add DSI device %s: %d\n", dev_err(dev, "failed to add DSI device %d\n", ret);
node->full_name, ret);
kfree(dsi); kfree(dsi);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
return dsi; return dsi;
} }
EXPORT_SYMBOL(mipi_dsi_device_register_full);
/**
* mipi_dsi_device_unregister - unregister MIPI DSI device
* @dsi: DSI peripheral device
*/
void mipi_dsi_device_unregister(struct mipi_dsi_device *dsi)
{
device_unregister(&dsi->dev);
}
EXPORT_SYMBOL(mipi_dsi_device_unregister);
static DEFINE_MUTEX(host_lock);
static LIST_HEAD(host_list);
/**
* of_find_mipi_dsi_host_by_node() - find the MIPI DSI host matching a
* device tree node
* @node: device tree node
*
* Returns:
* A pointer to the MIPI DSI host corresponding to @node or NULL if no
* such device exists (or has not been registered yet).
*/
struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node)
{
struct mipi_dsi_host *host;
mutex_lock(&host_lock);
list_for_each_entry(host, &host_list, list) {
if (host->dev->of_node == node) {
mutex_unlock(&host_lock);
return host;
}
}
mutex_unlock(&host_lock);
return NULL;
}
EXPORT_SYMBOL(of_find_mipi_dsi_host_by_node);
int mipi_dsi_host_register(struct mipi_dsi_host *host) int mipi_dsi_host_register(struct mipi_dsi_host *host)
{ {
...@@ -182,6 +277,10 @@ int mipi_dsi_host_register(struct mipi_dsi_host *host) ...@@ -182,6 +277,10 @@ int mipi_dsi_host_register(struct mipi_dsi_host *host)
of_mipi_dsi_device_add(host, node); of_mipi_dsi_device_add(host, node);
} }
mutex_lock(&host_lock);
list_add_tail(&host->list, &host_list);
mutex_unlock(&host_lock);
return 0; return 0;
} }
EXPORT_SYMBOL(mipi_dsi_host_register); EXPORT_SYMBOL(mipi_dsi_host_register);
...@@ -190,7 +289,7 @@ static int mipi_dsi_remove_device_fn(struct device *dev, void *priv) ...@@ -190,7 +289,7 @@ static int mipi_dsi_remove_device_fn(struct device *dev, void *priv)
{ {
struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev); struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
device_unregister(&dsi->dev); mipi_dsi_device_unregister(dsi);
return 0; return 0;
} }
...@@ -198,6 +297,10 @@ static int mipi_dsi_remove_device_fn(struct device *dev, void *priv) ...@@ -198,6 +297,10 @@ static int mipi_dsi_remove_device_fn(struct device *dev, void *priv)
void mipi_dsi_host_unregister(struct mipi_dsi_host *host) void mipi_dsi_host_unregister(struct mipi_dsi_host *host)
{ {
device_for_each_child(host->dev, NULL, mipi_dsi_remove_device_fn); device_for_each_child(host->dev, NULL, mipi_dsi_remove_device_fn);
mutex_lock(&host_lock);
list_del_init(&host->list);
mutex_unlock(&host_lock);
} }
EXPORT_SYMBOL(mipi_dsi_host_unregister); EXPORT_SYMBOL(mipi_dsi_host_unregister);
......
...@@ -847,6 +847,7 @@ static const struct drm_display_mode innolux_g121x1_l03_mode = { ...@@ -847,6 +847,7 @@ static const struct drm_display_mode innolux_g121x1_l03_mode = {
.vsync_end = 768 + 38 + 1, .vsync_end = 768 + 38 + 1,
.vtotal = 768 + 38 + 1 + 0, .vtotal = 768 + 38 + 1 + 0,
.vrefresh = 60, .vrefresh = 60,
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
}; };
static const struct panel_desc innolux_g121x1_l03 = { static const struct panel_desc innolux_g121x1_l03 = {
...@@ -982,6 +983,29 @@ static const struct panel_desc lg_lb070wv8 = { ...@@ -982,6 +983,29 @@ static const struct panel_desc lg_lb070wv8 = {
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
}; };
static const struct drm_display_mode lg_lp120up1_mode = {
.clock = 162300,
.hdisplay = 1920,
.hsync_start = 1920 + 40,
.hsync_end = 1920 + 40 + 40,
.htotal = 1920 + 40 + 40+ 80,
.vdisplay = 1280,
.vsync_start = 1280 + 4,
.vsync_end = 1280 + 4 + 4,
.vtotal = 1280 + 4 + 4 + 12,
.vrefresh = 60,
};
static const struct panel_desc lg_lp120up1 = {
.modes = &lg_lp120up1_mode,
.num_modes = 1,
.bpc = 8,
.size = {
.width = 267,
.height = 183,
},
};
static const struct drm_display_mode lg_lp129qe_mode = { static const struct drm_display_mode lg_lp129qe_mode = {
.clock = 285250, .clock = 285250,
.hdisplay = 2560, .hdisplay = 2560,
...@@ -1177,6 +1201,42 @@ static const struct panel_desc shelly_sca07010_bfn_lnn = { ...@@ -1177,6 +1201,42 @@ static const struct panel_desc shelly_sca07010_bfn_lnn = {
.bus_format = MEDIA_BUS_FMT_RGB666_1X18, .bus_format = MEDIA_BUS_FMT_RGB666_1X18,
}; };
static const struct display_timing urt_umsh_8596md_timing = {
.pixelclock = { 33260000, 33260000, 33260000 },
.hactive = { 800, 800, 800 },
.hfront_porch = { 41, 41, 41 },
.hback_porch = { 216 - 128, 216 - 128, 216 - 128 },
.hsync_len = { 71, 128, 128 },
.vactive = { 480, 480, 480 },
.vfront_porch = { 10, 10, 10 },
.vback_porch = { 35 - 2, 35 - 2, 35 - 2 },
.vsync_len = { 2, 2, 2 },
.flags = DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_NEGEDGE |
DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
};
static const struct panel_desc urt_umsh_8596md_lvds = {
.timings = &urt_umsh_8596md_timing,
.num_timings = 1,
.bpc = 6,
.size = {
.width = 152,
.height = 91,
},
.bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
};
static const struct panel_desc urt_umsh_8596md_parallel = {
.timings = &urt_umsh_8596md_timing,
.num_timings = 1,
.bpc = 6,
.size = {
.width = 152,
.height = 91,
},
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
};
static const struct of_device_id platform_of_match[] = { static const struct of_device_id platform_of_match[] = {
{ {
.compatible = "ampire,am800480r3tmqwa1h", .compatible = "ampire,am800480r3tmqwa1h",
...@@ -1256,6 +1316,9 @@ static const struct of_device_id platform_of_match[] = { ...@@ -1256,6 +1316,9 @@ static const struct of_device_id platform_of_match[] = {
}, { }, {
.compatible = "lg,lb070wv8", .compatible = "lg,lb070wv8",
.data = &lg_lb070wv8, .data = &lg_lb070wv8,
}, {
.compatible = "lg,lp120up1",
.data = &lg_lp120up1,
}, { }, {
.compatible = "lg,lp129qe", .compatible = "lg,lp129qe",
.data = &lg_lp129qe, .data = &lg_lp129qe,
...@@ -1280,6 +1343,24 @@ static const struct of_device_id platform_of_match[] = { ...@@ -1280,6 +1343,24 @@ static const struct of_device_id platform_of_match[] = {
}, { }, {
.compatible = "shelly,sca07010-bfn-lnn", .compatible = "shelly,sca07010-bfn-lnn",
.data = &shelly_sca07010_bfn_lnn, .data = &shelly_sca07010_bfn_lnn,
}, {
.compatible = "urt,umsh-8596md-t",
.data = &urt_umsh_8596md_parallel,
}, {
.compatible = "urt,umsh-8596md-1t",
.data = &urt_umsh_8596md_parallel,
}, {
.compatible = "urt,umsh-8596md-7t",
.data = &urt_umsh_8596md_parallel,
}, {
.compatible = "urt,umsh-8596md-11t",
.data = &urt_umsh_8596md_lvds,
}, {
.compatible = "urt,umsh-8596md-19t",
.data = &urt_umsh_8596md_lvds,
}, {
.compatible = "urt,umsh-8596md-20t",
.data = &urt_umsh_8596md_parallel,
}, { }, {
/* sentinel */ /* sentinel */
} }
......
...@@ -1597,6 +1597,8 @@ struct drm_bridge_funcs { ...@@ -1597,6 +1597,8 @@ struct drm_bridge_funcs {
* *
* The bridge can assume that the display pipe (i.e. clocks and timing * The bridge can assume that the display pipe (i.e. clocks and timing
* signals) feeding it is still running when this callback is called. * signals) feeding it is still running when this callback is called.
*
* The disable callback is optional.
*/ */
void (*disable)(struct drm_bridge *bridge); void (*disable)(struct drm_bridge *bridge);
...@@ -1613,6 +1615,8 @@ struct drm_bridge_funcs { ...@@ -1613,6 +1615,8 @@ struct drm_bridge_funcs {
* The bridge must assume that the display pipe (i.e. clocks and timing * The bridge must assume that the display pipe (i.e. clocks and timing
* singals) feeding it is no longer running when this callback is * singals) feeding it is no longer running when this callback is
* called. * called.
*
* The post_disable callback is optional.
*/ */
void (*post_disable)(struct drm_bridge *bridge); void (*post_disable)(struct drm_bridge *bridge);
...@@ -1641,6 +1645,8 @@ struct drm_bridge_funcs { ...@@ -1641,6 +1645,8 @@ struct drm_bridge_funcs {
* will not yet be running when this callback is called. The bridge must * will not yet be running when this callback is called. The bridge must
* not enable the display link feeding the next bridge in the chain (if * not enable the display link feeding the next bridge in the chain (if
* there is one) when this callback is called. * there is one) when this callback is called.
*
* The pre_enable callback is optional.
*/ */
void (*pre_enable)(struct drm_bridge *bridge); void (*pre_enable)(struct drm_bridge *bridge);
...@@ -1658,6 +1664,8 @@ struct drm_bridge_funcs { ...@@ -1658,6 +1664,8 @@ struct drm_bridge_funcs {
* signals) feeding it is running when this callback is called. This * signals) feeding it is running when this callback is called. This
* callback must enable the display link feeding the next bridge in the * callback must enable the display link feeding the next bridge in the
* chain if there is one. * chain if there is one.
*
* The enable callback is optional.
*/ */
void (*enable)(struct drm_bridge *bridge); void (*enable)(struct drm_bridge *bridge);
}; };
......
...@@ -96,14 +96,17 @@ struct mipi_dsi_host_ops { ...@@ -96,14 +96,17 @@ struct mipi_dsi_host_ops {
* struct mipi_dsi_host - DSI host device * struct mipi_dsi_host - DSI host device
* @dev: driver model device node for this DSI host * @dev: driver model device node for this DSI host
* @ops: DSI host operations * @ops: DSI host operations
* @list: list management
*/ */
struct mipi_dsi_host { struct mipi_dsi_host {
struct device *dev; struct device *dev;
const struct mipi_dsi_host_ops *ops; const struct mipi_dsi_host_ops *ops;
struct list_head list;
}; };
int mipi_dsi_host_register(struct mipi_dsi_host *host); int mipi_dsi_host_register(struct mipi_dsi_host *host);
void mipi_dsi_host_unregister(struct mipi_dsi_host *host); void mipi_dsi_host_unregister(struct mipi_dsi_host *host);
struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node);
/* DSI mode flags */ /* DSI mode flags */
...@@ -139,10 +142,28 @@ enum mipi_dsi_pixel_format { ...@@ -139,10 +142,28 @@ enum mipi_dsi_pixel_format {
MIPI_DSI_FMT_RGB565, MIPI_DSI_FMT_RGB565,
}; };
#define DSI_DEV_NAME_SIZE 20
/**
* struct mipi_dsi_device_info - template for creating a mipi_dsi_device
* @type: DSI peripheral chip type
* @channel: DSI virtual channel assigned to peripheral
* @node: pointer to OF device node or NULL
*
* This is populated and passed to mipi_dsi_device_new to create a new
* DSI device
*/
struct mipi_dsi_device_info {
char type[DSI_DEV_NAME_SIZE];
u32 channel;
struct device_node *node;
};
/** /**
* struct mipi_dsi_device - DSI peripheral device * struct mipi_dsi_device - DSI peripheral device
* @host: DSI host for this peripheral * @host: DSI host for this peripheral
* @dev: driver model device node for this peripheral * @dev: driver model device node for this peripheral
* @name: DSI peripheral chip type
* @channel: virtual channel assigned to the peripheral * @channel: virtual channel assigned to the peripheral
* @format: pixel format for video mode * @format: pixel format for video mode
* @lanes: number of active data lanes * @lanes: number of active data lanes
...@@ -152,6 +173,7 @@ struct mipi_dsi_device { ...@@ -152,6 +173,7 @@ struct mipi_dsi_device {
struct mipi_dsi_host *host; struct mipi_dsi_host *host;
struct device dev; struct device dev;
char name[DSI_DEV_NAME_SIZE];
unsigned int channel; unsigned int channel;
unsigned int lanes; unsigned int lanes;
enum mipi_dsi_pixel_format format; enum mipi_dsi_pixel_format format;
...@@ -188,6 +210,10 @@ static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt) ...@@ -188,6 +210,10 @@ static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
return -EINVAL; return -EINVAL;
} }
struct mipi_dsi_device *
mipi_dsi_device_register_full(struct mipi_dsi_host *host,
const struct mipi_dsi_device_info *info);
void mipi_dsi_device_unregister(struct mipi_dsi_device *dsi);
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);
......
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