diff options
-rw-r--r-- | Documentation/devicetree/bindings/display/panel/lg,lp120up1.txt | 7 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/display/panel/urt,umsh-8596md.txt | 16 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/vendor-prefixes.txt | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_bridge.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_mipi_dsi.c | 127 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-simple.c | 81 | ||||
-rw-r--r-- | include/drm/drm_crtc.h | 8 | ||||
-rw-r--r-- | include/drm/drm_mipi_dsi.h | 26 |
8 files changed, 262 insertions, 16 deletions
diff --git a/Documentation/devicetree/bindings/display/panel/lg,lp120up1.txt b/Documentation/devicetree/bindings/display/panel/lg,lp120up1.txt new file mode 100644 index 000000000000..8c5de692c55c --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/lg,lp120up1.txt @@ -0,0 +1,7 @@ +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. diff --git a/Documentation/devicetree/bindings/display/panel/urt,umsh-8596md.txt b/Documentation/devicetree/bindings/display/panel/urt,umsh-8596md.txt new file mode 100644 index 000000000000..088a6cea5015 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/urt,umsh-8596md.txt @@ -0,0 +1,16 @@ +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. diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 72e2c5a2b327..1bcef7300b37 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -240,6 +240,7 @@ tplink TP-LINK Technologies Co., Ltd. tronfy Tronfy truly Truly Semiconductors Limited upisemi uPI Semiconductor Corp. +urt United Radiant Technology Corporation usi Universal Scientific Industrial Co., Ltd. v3 V3 Semiconductor variscite Variscite Ltd. diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index bd93453afa61..b3654404abd0 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -186,7 +186,8 @@ void drm_bridge_disable(struct drm_bridge *bridge) drm_bridge_disable(bridge->next); - bridge->funcs->disable(bridge); + if (bridge->funcs->disable) + bridge->funcs->disable(bridge); } EXPORT_SYMBOL(drm_bridge_disable); @@ -206,7 +207,8 @@ void drm_bridge_post_disable(struct drm_bridge *bridge) if (!bridge) return; - bridge->funcs->post_disable(bridge); + if (bridge->funcs->post_disable) + bridge->funcs->post_disable(bridge); drm_bridge_post_disable(bridge->next); } @@ -256,7 +258,8 @@ void drm_bridge_pre_enable(struct drm_bridge *bridge) drm_bridge_pre_enable(bridge->next); - bridge->funcs->pre_enable(bridge); + if (bridge->funcs->pre_enable) + bridge->funcs->pre_enable(bridge); } EXPORT_SYMBOL(drm_bridge_pre_enable); @@ -276,7 +279,8 @@ void drm_bridge_enable(struct drm_bridge *bridge) if (!bridge) return; - bridge->funcs->enable(bridge); + if (bridge->funcs->enable) + bridge->funcs->enable(bridge); drm_bridge_enable(bridge->next); } diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 6e6a9c58d404..f5d80839a90c 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -47,7 +47,17 @@ 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 = { @@ -129,14 +139,20 @@ static int mipi_dsi_device_add(struct mipi_dsi_device *dsi) return device_add(&dsi->dev); } +#if IS_ENABLED(CONFIG_OF) static struct mipi_dsi_device * 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 mipi_dsi_device_info info = { }; int ret; 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", ®); if (ret) { 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) return ERR_PTR(-EINVAL); } - if (reg > 3) { - dev_err(dev, "device node %s has invalid reg property: %u\n", - node->full_name, reg); + info.channel = reg; + info.node = of_node_get(node); + + 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); } dsi = mipi_dsi_device_alloc(host); if (IS_ERR(dsi)) { - dev_err(dev, "failed to allocate DSI device %s: %ld\n", - node->full_name, PTR_ERR(dsi)); + dev_err(dev, "failed to allocate DSI device %ld\n", + PTR_ERR(dsi)); return dsi; } - dsi->dev.of_node = of_node_get(node); - dsi->channel = reg; + dsi->dev.of_node = info->node; + dsi->channel = info->channel; + strlcpy(dsi->name, info->type, sizeof(dsi->name)); ret = mipi_dsi_device_add(dsi); if (ret) { - dev_err(dev, "failed to add DSI device %s: %d\n", - node->full_name, ret); + dev_err(dev, "failed to add DSI device %d\n", ret); kfree(dsi); return ERR_PTR(ret); } 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) { @@ -182,6 +277,10 @@ int mipi_dsi_host_register(struct mipi_dsi_host *host) of_mipi_dsi_device_add(host, node); } + mutex_lock(&host_lock); + list_add_tail(&host->list, &host_list); + mutex_unlock(&host_lock); + return 0; } EXPORT_SYMBOL(mipi_dsi_host_register); @@ -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); - device_unregister(&dsi->dev); + mipi_dsi_device_unregister(dsi); return 0; } @@ -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) { 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); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 2164c999052c..ceb20486dacf 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -847,6 +847,7 @@ static const struct drm_display_mode innolux_g121x1_l03_mode = { .vsync_end = 768 + 38 + 1, .vtotal = 768 + 38 + 1 + 0, .vrefresh = 60, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, }; static const struct panel_desc innolux_g121x1_l03 = { @@ -982,6 +983,29 @@ static const struct panel_desc lg_lb070wv8 = { .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 = { .clock = 285250, .hdisplay = 2560, @@ -1177,6 +1201,42 @@ static const struct panel_desc shelly_sca07010_bfn_lnn = { .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[] = { { .compatible = "ampire,am800480r3tmqwa1h", @@ -1257,6 +1317,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "lg,lb070wv8", .data = &lg_lb070wv8, }, { + .compatible = "lg,lp120up1", + .data = &lg_lp120up1, + }, { .compatible = "lg,lp129qe", .data = &lg_lp129qe, }, { @@ -1281,6 +1344,24 @@ static const struct of_device_id platform_of_match[] = { .compatible = "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 */ } }; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index fb9672783fa7..e0170bf80bb0 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1597,6 +1597,8 @@ struct drm_bridge_funcs { * * The bridge can assume that the display pipe (i.e. clocks and timing * signals) feeding it is still running when this callback is called. + * + * The disable callback is optional. */ void (*disable)(struct drm_bridge *bridge); @@ -1613,6 +1615,8 @@ struct drm_bridge_funcs { * The bridge must assume that the display pipe (i.e. clocks and timing * singals) feeding it is no longer running when this callback is * called. + * + * The post_disable callback is optional. */ void (*post_disable)(struct drm_bridge *bridge); @@ -1641,6 +1645,8 @@ struct drm_bridge_funcs { * 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 * there is one) when this callback is called. + * + * The pre_enable callback is optional. */ void (*pre_enable)(struct drm_bridge *bridge); @@ -1658,6 +1664,8 @@ struct drm_bridge_funcs { * signals) feeding it is running when this callback is called. This * callback must enable the display link feeding the next bridge in the * chain if there is one. + * + * The enable callback is optional. */ void (*enable)(struct drm_bridge *bridge); }; diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 1b3b1f8c8cdf..7a9840f8b38e 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -96,14 +96,17 @@ struct mipi_dsi_host_ops { * struct mipi_dsi_host - DSI host device * @dev: driver model device node for this DSI host * @ops: DSI host operations + * @list: list management */ struct mipi_dsi_host { struct device *dev; const struct mipi_dsi_host_ops *ops; + struct list_head list; }; int mipi_dsi_host_register(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 */ @@ -139,10 +142,28 @@ enum mipi_dsi_pixel_format { 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 * @host: DSI host for this peripheral * @dev: driver model device node for this peripheral + * @name: DSI peripheral chip type * @channel: virtual channel assigned to the peripheral * @format: pixel format for video mode * @lanes: number of active data lanes @@ -152,6 +173,7 @@ struct mipi_dsi_device { struct mipi_dsi_host *host; struct device dev; + char name[DSI_DEV_NAME_SIZE]; unsigned int channel; unsigned int lanes; 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) 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); int mipi_dsi_attach(struct mipi_dsi_device *dsi); int mipi_dsi_detach(struct mipi_dsi_device *dsi); |