* [PATCH] pwm-backlight: add "max-brightness" property
From: Andrew Bresticker @ 2013-08-12 22:04 UTC (permalink / raw)
To: Richard Purdie, Jingoo Han
Cc: Olof Johansson, Rob Herring, Pawel Moll, Mark Rutland,
Stephen Warren, Ian Campbell, Rob Landley, Thierry Reding,
Jean-Christophe Plagniol-Villard, Tomi Valkeinen, devicetree,
linux-doc, linux-kernel, linux-pwm, linux-fbdev,
Andrew Bresticker
Specifying each individual brightness value via the "brightness-levels"
property can be a pain if we want to use a large continuous range of
brightness values. Add the property "max-brightness", which can be
given in place of "brightness-levels", that specifies that all values
between 0 and the given value can be used.
Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
---
.../bindings/video/backlight/pwm-backlight.txt | 22 +++++++++---
drivers/video/backlight/pwm_bl.c | 39 +++++++++++++---------
2 files changed, 42 insertions(+), 19 deletions(-)
diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
index 1e4fc72..856dfc9 100644
--- a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
+++ b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
@@ -3,15 +3,21 @@ pwm-backlight bindings
Required properties:
- compatible: "pwm-backlight"
- pwms: OF device-tree PWM specification (see PWM binding[0])
+ - one of "brightness-levels" or "max-brightness", described below
+ - default-brightness-level: the default brightness level (index into the array
+ defined by the "brightness-levels" property or a value between 0 and
+ "max-brightness")
+
+Optional properties:
- brightness-levels: Array of distinct brightness levels. Typically these
are in the range from 0 to 255, but any range starting at 0 will do.
The actual brightness level (PWM duty cycle) will be interpolated
from these values. 0 means a 0% duty cycle (darkest/off), while the
last value in the array represents a 100% duty cycle (brightest).
- - default-brightness-level: the default brightness level (index into the
- array defined by the "brightness-levels" property)
-
-Optional properties:
+ - max-brightness: Instead of specifying a complete set of brightness
+ levels, a single maximum brightness value may be given. This indicates
+ that all integers between 0 and max-brightness are valid brightness
+ values.
- pwm-names: a list of names for the PWM devices specified in the
"pwms" property (see PWM binding[0])
@@ -26,3 +32,11 @@ Example:
brightness-levels = <0 4 8 16 32 64 128 255>;
default-brightness-level = <6>;
};
+or:
+ backlight {
+ compatible = "pwm-backlight";
+ pwms = <&pwm 0 1000000 0>;
+
+ max-brightness = <1024>;
+ default-brightness-level = <700>;
+ };
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 1fea627..92973b1 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -98,7 +98,6 @@ static int pwm_backlight_parse_dt(struct device *dev,
struct platform_pwm_backlight_data *data)
{
struct device_node *node = dev->of_node;
- struct property *prop;
int length;
u32 value;
int ret;
@@ -108,34 +107,44 @@ static int pwm_backlight_parse_dt(struct device *dev,
memset(data, 0, sizeof(*data));
- /* determine the number of brightness levels */
- prop = of_find_property(node, "brightness-levels", &length);
- if (!prop)
- return -EINVAL;
+ if (of_find_property(node, "brightness-levels", &length)) {
+ data->max_brightness = length / sizeof(u32);
- data->max_brightness = length / sizeof(u32);
+ /* read brightness levels from DT property */
+ if (data->max_brightness > 0) {
+ size_t size = sizeof(*data->levels) *
+ data->max_brightness;
- /* read brightness levels from DT property */
- if (data->max_brightness > 0) {
- size_t size = sizeof(*data->levels) * data->max_brightness;
-
- data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
- if (!data->levels)
- return -ENOMEM;
+ data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!data->levels)
+ return -ENOMEM;
- ret = of_property_read_u32_array(node, "brightness-levels",
+ ret = of_property_read_u32_array(node,
+ "brightness-levels",
data->levels,
data->max_brightness);
+ if (ret < 0)
+ return ret;
+
+ data->max_brightness--;
+ }
+ } else {
+ ret = of_property_read_u32(node, "max-brightness",
+ &value);
if (ret < 0)
return ret;
+ /* brightness values are 0 to max-brightness */
+ data->max_brightness = value;
+ }
+
+ if (data->max_brightness > 0) {
ret = of_property_read_u32(node, "default-brightness-level",
&value);
if (ret < 0)
return ret;
data->dft_brightness = value;
- data->max_brightness--;
}
/*
--
1.8.3
^ permalink raw reply related
* Re: [RFC 0/1] Adding DT support to video/da8xx-fb.c
From: Darren Etheridge @ 2013-08-12 19:11 UTC (permalink / raw)
To: Tomi Valkeinen; +Cc: devicetree, plagnioj, linux-fbdev, afzal
In-Reply-To: <52052D10.7060803@ti.com>
Tomi Valkeinen <tomi.valkeinen@ti.com> wrote on Fri [2013-Aug-09 20:55:28 +0300]:
> Hi,
>
> On 08/08/13 23:15, Darren Etheridge wrote:
> > This is part of a larger series of patches to upgrade the da8xx-fb.c driver
> > to support the Texas Instruments AM335x device. As part of this upgrade
> > we also want to add devicetree support for both the original da8xx and the
> > am335x. Tomi Valkeinen has reviewed the fbdev changes but he suggested
> > that it was prudent to extract the dt pieces and run it through the
> > devicetree mailing list for review.
> >
> > Thanks,
> > Darren
> >
> > Darren Etheridge (1):
> > video: da8xx-fb: adding dt support
> >
> > .../devicetree/bindings/video/fb-da8xx.txt | 37 +++++++++++
> > drivers/video/da8xx-fb.c | 67 +++++++++++++++++++-
> > 2 files changed, 101 insertions(+), 3 deletions(-)
> > create mode 100644 Documentation/devicetree/bindings/video/fb-da8xx.txt
>
> I just realized something. We have both drm and fb drivers for the LCDC
> IP. The hardware is the same, thus, there should only be one set of DT
> bindings, used by both drivers.
>
> I don't know why this didn't occur to me earlier, as it's the same
> situation with OMAP DSS.
>
OK that makes sense. So in this case what I need to do is change the
.compatible to match the gpu/drm/driver/tilcdc driver which has the
existing binding so it would become ti,am33xx-tilcdc instead of
ti,am3352-lcdc. This fbdev driver also supports da8xx class devices
so I would just rename this second .compatible to be ti,da8xx-tilcdc
to be consistent with the other. The fbdev driver doesn't support
some of the options that the tilcdc/drm driver supports, but that
shouldn't be a problem for the fbdev driver.
I will also change the name of the documentation file to be
da8xx-fb.txt to match the driver name.
Darren
^ permalink raw reply
* Re: [V4 1/5] video: mmp: rb swap setting update for mmp display
From: jett zhou @ 2013-08-12 11:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1375077384-7467-1-git-send-email-jtzhou@marvell.com>
Hi Jean
Would you please help to review these 5 patches when you have
time, thanks very much.
Thanks
2013/7/29 Jett.Zhou <jtzhou@marvell.com>:
> From: Guoqing Li <ligq@marvell.com>
>
> We could set rb swap in two modules: DMA controler input part and
> dsi interface output part.
> DMA input part is based on pix_fmt to set rbswap, dsi output interface
> part will set rbswap based on platform dsi_rbswap configuration.
>
> This patch include below change and enhancement:
>
> 1) The input format which support rbswap is based on RGB format,
> eg. RGB565 indicates the source data in memory is that Red is [15~11],
> Green is [10~5], Blue is [4:0], Red is MSB, Blue is LSB, but for the
> display dma input default setting(rbswap = 0), it only support Blue
> is [15~11], Green is [10~5], Red is [4:0], Red is LSB, Blue is MSB,
> so for this format(RGB565), display controller need to set rbswap
> = 1 and it can support the MSB/LSB correctly.
> BGR/YUV format will not set it in mmp display driver.
>
> 2) The dsi output part of rbswap is depend on dsi_rbswap which is
> defined in specific platfrom. For output dsi interface, it has this
> feature to do rbswap again if it needs specifc byte sequence of RGB
> byte for DSI panel.
> eg. If display content is set RGB565 in memory and DMA input part set
> rbswap in driver to support Red as MSB , Blue LSB, but dsi panel only
> support Red as LSB, Blue as MSB, then it can use this feature.
> If there is no this requirement of panel, this dsi output part is not
> needed.
>
> Signed-off-by: Guoqing Li <ligq@marvell.com>
> Signed-off-by: Jett.Zhou <jtzhou@marvell.com>
> Reviewed-by: Daniel Drake <dsd@laptop.org>
> ---
> drivers/video/mmp/hw/mmp_ctrl.c | 19 +++++++++++--------
> drivers/video/mmp/hw/mmp_ctrl.h | 5 +++++
> include/video/mmp_disp.h | 1 +
> 3 files changed, 17 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/video/mmp/hw/mmp_ctrl.c b/drivers/video/mmp/hw/mmp_ctrl.c
> index 75dca19..a40d95a 100644
> --- a/drivers/video/mmp/hw/mmp_ctrl.c
> +++ b/drivers/video/mmp/hw/mmp_ctrl.c
> @@ -60,8 +60,7 @@ static irqreturn_t ctrl_handle_irq(int irq, void *dev_id)
>
> static u32 fmt_to_reg(struct mmp_overlay *overlay, int pix_fmt)
> {
> - u32 link_config = path_to_path_plat(overlay->path)->link_config;
> - u32 rbswap, uvswap = 0, yuvswap = 0,
> + u32 rbswap = 0, uvswap = 0, yuvswap = 0,
> csc_en = 0, val = 0,
> vid = overlay_is_vid(overlay);
>
> @@ -71,27 +70,23 @@ static u32 fmt_to_reg(struct mmp_overlay *overlay, int pix_fmt)
> case PIXFMT_RGB888PACK:
> case PIXFMT_RGB888UNPACK:
> case PIXFMT_RGBA888:
> - rbswap = !(link_config & 0x1);
> + rbswap = 1;
> break;
> case PIXFMT_VYUY:
> case PIXFMT_YVU422P:
> case PIXFMT_YVU420P:
> - rbswap = link_config & 0x1;
> uvswap = 1;
> break;
> case PIXFMT_YUYV:
> - rbswap = link_config & 0x1;
> yuvswap = 1;
> break;
> default:
> - rbswap = link_config & 0x1;
> break;
> }
>
> switch (pix_fmt) {
> case PIXFMT_RGB565:
> case PIXFMT_BGR565:
> - val = 0;
> break;
> case PIXFMT_RGB1555:
> case PIXFMT_BGR1555:
> @@ -248,7 +243,8 @@ static void path_set_mode(struct mmp_path *path, struct mmp_mode *mode)
> {
> struct lcd_regs *regs = path_regs(path);
> u32 total_x, total_y, vsync_ctrl, tmp, sclk_src, sclk_div,
> - link_config = path_to_path_plat(path)->link_config;
> + link_config = path_to_path_plat(path)->link_config,
> + dsi_rbswap = path_to_path_plat(path)->link_config;
>
> /* FIXME: assert videomode supported */
> memcpy(&path->mode, mode, sizeof(struct mmp_mode));
> @@ -263,6 +259,12 @@ static void path_set_mode(struct mmp_path *path, struct mmp_mode *mode)
> tmp |= CFG_DUMB_ENA(1);
> writel_relaxed(tmp, ctrl_regs(path) + intf_ctrl(path->id));
>
> + /* interface rb_swap setting */
> + tmp = readl_relaxed(ctrl_regs(path) + intf_rbswap_ctrl(path->id)) &
> + (~(CFG_INTFRBSWAP_MASK));
> + tmp |= dsi_rbswap & CFG_INTFRBSWAP_MASK;
> + writel_relaxed(tmp, ctrl_regs(path) + intf_rbswap_ctrl(path->id));
> +
> writel_relaxed((mode->yres << 16) | mode->xres, ®s->screen_active);
> writel_relaxed((mode->left_margin << 16) | mode->right_margin,
> ®s->screen_h_porch);
> @@ -419,6 +421,7 @@ static int path_init(struct mmphw_path_plat *path_plat,
> path_plat->path = path;
> path_plat->path_config = config->path_config;
> path_plat->link_config = config->link_config;
> + path_plat->dsi_rbswap = config->dsi_rbswap;
> path_set_default(path);
>
> kfree(path_info);
> diff --git a/drivers/video/mmp/hw/mmp_ctrl.h b/drivers/video/mmp/hw/mmp_ctrl.h
> index edd2002..53301cf 100644
> --- a/drivers/video/mmp/hw/mmp_ctrl.h
> +++ b/drivers/video/mmp/hw/mmp_ctrl.h
> @@ -163,6 +163,8 @@ struct lcd_regs {
>
> #define LCD_SCLK(path) ((PATH_PN = path->id) ? LCD_CFG_SCLK_DIV :\
> ((PATH_TV = path->id) ? LCD_TCLK_DIV : LCD_PN2_SCLK_DIV))
> +#define intf_rbswap_ctrl(id) ((id) ? (((id) & 1) ? LCD_TVIF_CTRL : \
> + PN2_IOPAD_CONTROL) : LCD_TOP_CTRL)
>
> /* dither configure */
> #ifdef CONFIG_CPU_PXA988
> @@ -615,6 +617,8 @@ struct lcd_regs {
> #define LCD_SPU_DUMB_CTRL 0x01B8
> #define CFG_DUMBMODE(mode) ((mode)<<28)
> #define CFG_DUMBMODE_MASK 0xF0000000
> +#define CFG_INTFRBSWAP(mode) ((mode)<<24)
> +#define CFG_INTFRBSWAP_MASK 0x0F000000
> #define CFG_LCDGPIO_O(data) ((data)<<20)
> #define CFG_LCDGPIO_O_MASK 0x0FF00000
> #define CFG_LCDGPIO_ENA(gpio) ((gpio)<<12)
> @@ -1427,6 +1431,7 @@ struct mmphw_path_plat {
> struct mmp_path *path;
> u32 path_config;
> u32 link_config;
> + u32 dsi_rbswap;
> };
>
> /* mmp ctrl describes mmp controller related info */
> diff --git a/include/video/mmp_disp.h b/include/video/mmp_disp.h
> index b9dd1fb..32094c0 100644
> --- a/include/video/mmp_disp.h
> +++ b/include/video/mmp_disp.h
> @@ -334,6 +334,7 @@ struct mmp_mach_path_config {
> int output_type;
> u32 path_config;
> u32 link_config;
> + u32 dsi_rbswap;
> };
>
> struct mmp_mach_plat_info {
> --
> 1.7.0.4
>
--
----------------------------------
Best Regards
Jett Zhou
^ permalink raw reply
* About buffer sychronization mechanism and cache operation
From: Inki Dae @ 2013-08-12 9:55 UTC (permalink / raw)
To: linux-fbdev
Hello all,
The purpose of this email is to get other opinions and advices to buffer synchronization mechanism, and coupling cache operation feature with the buffer synchronization mechanism. First of all, I am not a native English speaker so I'm not sure that I can convey my intention to you. And I'm not a specialist in Linux than other people so also there might be my missing points.
I had posted the buffer synchronization mechanism called dmabuf sync framework like below,
http://lists.infradead.org/pipermail/linux-arm-kernel/2013-June/177045.html
And I'm sending this email before posting next version with more stable patch set and features. The purpose of this framework is to provide not only buffer access control to CPU and DMA but also easy-to-use interfaces for device drivers and user application. This framework can be used for all DMA devices using system memory as DMA buffer, especially for most ARM based SoCs.
There are two cases we are using this buffer synchronization framework. One is to primarily enhance GPU rendering performance on Tizen platform in case of 3d app with compositing mode that the 3d app draws something in off-screen buffer.
And other is to couple buffer access control and cache operation between CPU and DMA; the cache operation is done by the dmabuf sync framework in kernel side.
Why do we need buffer access control between CPU and DMA?
---------------------------------------------------------
The below shows simple 3D software layers,
3D Application
-----------------------------------------
Standard OpenGL ES and EGL Interfaces ------- [A]
-----------------------------------------
MALI OpenGL ES and EGL Native modules --------- [B]
-----------------------------------------
Exynos DRM Driver | GPU Driver ---------- [C]
3d application requests 3d rendering through A. And then B fills a 3d command buffer with the requests from A. And then 3D application calls glFlush so that the 3d command buffer can be submitted to C, and rendered by GPU hardware. Internally, glFlush(also glFinish) will call a function of native module[B] glFinish blocks caller's task until all GL execution is complete. On the other hand, glFlush forces execution of GL commands but doesn't block the caller's task until the completion.
In composting mode, in case of using glFinish, the 3d rendering performance is quite lower than using glFlush because glFinish makes CPU blocked until the execution of the 3d commands is completed. However, the use of glFlush has one issue that the shared buffer with GPU could be broken when CPU accesses the shared buffer at once after glFlush because CPU cannot be aware of the completion of GPU access to the shared buffer: actually, Tizen platform internally calls only eglSwapBuffer instead of glFlush and glFinish, and whether flushing or finishing is decided according to compositing mode or not. So in such case, we would need buffer access control between CPU and DMA for more performance.
About cache operation
---------------------
The dmabuf sync framework can include cache operation feature, and the below shows how the cache operation based on dmabuf sync framework is performed,
device driver in kernel or fctrl in user land
dmabuf_sync_lock or dmabuf_sync_single_lock
check before and after buffer access
dma_buf_begin_cpu_access or dma_buf_end_cpu_access
begin_cpu_access or end_cpu_access of exporter
dma_sync_sg_for_device or dma_sync_sg_for_cpu
In case that using dmabuf sync framework, kernel can be aware of when CPU and DMA access to a shared buffer is completed so we can do cache operation in kernel so that way, we can couple buffer access control and cache operation. So with this, we can avoid that user land overuses cache operation.
I guess most Linux based platforms are using cachable mapped buffer for more performance: in case that CPU frequently accesses the shared buffer which is shared with DMA, the use of cachable mapped buffer is more fast than the use of non-cachable. However, this way could make cache operation overused by user land because only user land can be aware of the completion of CPU or DMA access to the shared buffer so user land could request cache operations every time it wants even the cache operation is unnecessary. That is how user land could overuse cache operations.
To Android, Chrome OS, and other forks,
Are there other cases that buffer access control between CPU and DMA is needed? I know Android sync driver and KDS are already being used for Android, Chrome OS, and so on.
How does your platform do cache operation? And How do you think about coupling buffer access control and cache operation between CPU and DMA?.
Lastly, I think we may need Linux generic buffer synchronization mechanism that uses only Linux standard interfaces (dmabuf) including user land interfaces (fcntl and select system calls), and the dmabuf sync framework could meet it.
Thanks,
Inki Dae
^ permalink raw reply
* Re: EDID modes unavailable when no connector/crtc available at boot
From: Dave Airlie @ 2013-08-11 10:06 UTC (permalink / raw)
To: Tony Prisk; +Cc: linux-fbdev@vger.kernel.org, dri-devel
In-Reply-To: <52075C9E.6040901@prisktech.co.nz>
On Sun, Aug 11, 2013 at 7:42 PM, Tony Prisk <linux@prisktech.co.nz> wrote:
> On 11/08/13 20:42, Dave Airlie wrote:
>>
>> On Sun, Aug 11, 2013 at 2:41 PM, Tony Prisk <linux@prisktech.co.nz> wrote:
>>>
>>> I am working on the HDMI driver for the i.MX6 as part of the larger DRM
>>> driver written by Sascha Hauer and need a little advice. I seem to be
>>> missing one important part of the subsystem that I haven't been able to
>>> resolve.
>>>
>> fbcon is limited by boot sizes as at least with dynamic memory
>> management and how fbdev works resizing the allocation is nearly
>> impossible to do race free, since fbdev will hand out mmaps to
>> userspace and that stops you from ever moving anything once the device
>> is open.
>>
>> But this is only for the fbdev emulation, a real kms application
>> should be able to use a larger size no problems.
>>
>> Dave.
>
> It seems to be worse than just a fbcon issue as far as I can tell.
>
> I am making an assumption, but I believe
> '/sys/class/drm/card0-HDMI-A-1/modes' should list all the supported modes of
> the connector (regardless of fbcon).
> Using 'cat /sys/class/drm/card0-HDMI-A-1/modes', it appears the supported
> modes are being limited by fbcon
>
> 1) HDMI Cable connected at bootup (fb @ 1920x1080)
> cat /sys/class/drm/card0-HDMI-A-1/modes
> 1920x1080
> 1280x720
> 1280x720
> 720x576
> 720x480
> 640x480
>
> 2) HDMI Cable NOT connected at bootup (fb @ 1024x768), cable is then
> connected after userspace has started (still in console)
> cat /sys/class/drm/card0-HDMI-A-1/modes
> 720x576
> 720x480
> 640x480
>
>
> Following back through the source:
>
> static struct drm_connector_funcs imx_hdmi_connector_funcs = {
> .fill_modes = drm_helper_probe_single_connector_modes,
> ...
> };
>
> static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = {
> .get_modes = imx_hdmi_connector_get_modes,
> .mode_valid = imx_hdmi_connector_mode_valid,
> ...
> };
>
>
> It appears that only drm_helper_probe_single_connector_modes() calls
> .get_modes() and .mode_valid()
> .fill_modes() is called from drm_fb_helper_probe_connector_modes(), which is
> called from drm_fb_helper_hotplug_event()
> drm_fb_helper_hotplug_event() sets max_width to fb_helper->fb->width, and
> max_height to fb_helper->fb->height.
> fb->width is 1024 if booted without the cable connected, hence the clipping
> of the values.
fill_modes is also called from the drm_crtc.c userspace interface, all
the functions in drm_fb_helper are for fbdev/con use, the fact sysfs
is wrong is only a side effect.
Userspace should get a full list of modes, try using the modetest
application which I think should work.
Dave.
^ permalink raw reply
* Re: EDID modes unavailable when no connector/crtc available at boot
From: Tony Prisk @ 2013-08-11 9:42 UTC (permalink / raw)
To: Dave Airlie; +Cc: linux-fbdev@vger.kernel.org, dri-devel
In-Reply-To: <CAPM=9tzShWxZ-f=641r+-uc21JkY3kTeM2K=tsiB+OotNCnrQA@mail.gmail.com>
On 11/08/13 20:42, Dave Airlie wrote:
> On Sun, Aug 11, 2013 at 2:41 PM, Tony Prisk <linux@prisktech.co.nz> wrote:
>> I am working on the HDMI driver for the i.MX6 as part of the larger DRM
>> driver written by Sascha Hauer and need a little advice. I seem to be
>> missing one important part of the subsystem that I haven't been able to
>> resolve.
>>
> fbcon is limited by boot sizes as at least with dynamic memory
> management and how fbdev works resizing the allocation is nearly
> impossible to do race free, since fbdev will hand out mmaps to
> userspace and that stops you from ever moving anything once the device
> is open.
>
> But this is only for the fbdev emulation, a real kms application
> should be able to use a larger size no problems.
>
> Dave.
It seems to be worse than just a fbcon issue as far as I can tell.
I am making an assumption, but I believe
'/sys/class/drm/card0-HDMI-A-1/modes' should list all the supported
modes of the connector (regardless of fbcon).
Using 'cat /sys/class/drm/card0-HDMI-A-1/modes', it appears the
supported modes are being limited by fbcon
1) HDMI Cable connected at bootup (fb @ 1920x1080)
cat /sys/class/drm/card0-HDMI-A-1/modes
1920x1080
1280x720
1280x720
720x576
720x480
640x480
2) HDMI Cable NOT connected at bootup (fb @ 1024x768), cable is then
connected after userspace has started (still in console)
cat /sys/class/drm/card0-HDMI-A-1/modes
720x576
720x480
640x480
Following back through the source:
static struct drm_connector_funcs imx_hdmi_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
...
};
static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = {
.get_modes = imx_hdmi_connector_get_modes,
.mode_valid = imx_hdmi_connector_mode_valid,
...
};
It appears that only drm_helper_probe_single_connector_modes() calls
.get_modes() and .mode_valid()
.fill_modes() is called from drm_fb_helper_probe_connector_modes(),
which is called from drm_fb_helper_hotplug_event()
drm_fb_helper_hotplug_event() sets max_width to fb_helper->fb->width,
and max_height to fb_helper->fb->height.
fb->width is 1024 if booted without the cable connected, hence the
clipping of the values.
Regards
Tony Prisk
^ permalink raw reply
* Re: EDID modes unavailable when no connector/crtc available at boot
From: Dave Airlie @ 2013-08-11 8:42 UTC (permalink / raw)
To: Tony Prisk; +Cc: linux-fbdev@vger.kernel.org, dri-devel
In-Reply-To: <5207160A.6070803@prisktech.co.nz>
On Sun, Aug 11, 2013 at 2:41 PM, Tony Prisk <linux@prisktech.co.nz> wrote:
> I am working on the HDMI driver for the i.MX6 as part of the larger DRM
> driver written by Sascha Hauer and need a little advice. I seem to be
> missing one important part of the subsystem that I haven't been able to
> resolve.
>
fbcon is limited by boot sizes as at least with dynamic memory
management and how fbdev works resizing the allocation is nearly
impossible to do race free, since fbdev will hand out mmaps to
userspace and that stops you from ever moving anything once the device
is open.
But this is only for the fbdev emulation, a real kms application
should be able to use a larger size no problems.
Dave.
^ permalink raw reply
* EDID modes unavailable when no connector/crtc available at boot
From: Tony Prisk @ 2013-08-11 4:41 UTC (permalink / raw)
To: dri-devel, David Airlie, linux-fbdev@vger.kernel.org
I am working on the HDMI driver for the i.MX6 as part of the larger DRM
driver written by Sascha Hauer and need a little advice. I seem to be
missing one important part of the subsystem that I haven't been able to
resolve.
In my testing, powering on the system with only a HDMI cable connected
works perfectly. Resolution is set to 1920x1080 as requested (with the
framebuffer console at the same resolution).
If I boot up the system without an HDMI cable connected (or any other
cable), I get the expected:
[ 1.470273] imx_hdmi_connector_detect
[ 1.470276] returned cable DISCONNECTED
[ 1.470289] imx-drm imx-drm: No connectors reported connected with modes
[ 1.470297] [drm] Cannot find any crtc or sizes - going 1024x768
When I connect the cable at a later time, all EDID detected modes are
'truncated' to '<= 1024x768', but because I have a video=xxx in
bootargs, the driver still seems to initialize to 1920x1080, but with a
framebuffer of 1024x768.
I assume this is a mistake on my part, but I don't know what I'm missing
that would cause it.
I see in drm_crtc_helper.c:drm_helper_mode_fill_fb_struct that the
width/height are set to the initialized mode.
Later in drm_fb_helper.c:drm_fb_helper_hotplug_event, max_width if set
to fb->width.
This is what causes the modes to be truncated when the HDMI connector is
finally connected (max_width is now limited to 1024).
Suggestions appreciated.
Regards
Tony Prisk
^ permalink raw reply
* Re: [RFC 1/1] drm/pl111: Initial drm/kms driver for pl111
From: Rob Clark @ 2013-08-10 12:30 UTC (permalink / raw)
To: Tom Cooksey; +Cc: dri-devel, linux-fbdev, Pawel Moll, linux-arm-kernel
In-Reply-To: <5205277e.84320f0a.1cdf.ffff8816SMTPIN_ADDED_BROKEN@mx.google.com>
On Fri, Aug 9, 2013 at 1:31 PM, Tom Cooksey <tom.cooksey@arm.com> wrote:
>> > > So in the above, after X receives the second DRI2SwapBuffers, it
>> > > doesn't need to get scheduled again for the next frame to be both
>> > > rendered by the GPU and issued to the display for scanout.
>> >
>> > well, this is really only an issue if you are so loaded that you
>> > don't get a chance to schedule for ~16ms.. which is pretty long time.
>
> Yes - it really is 16ms (minus interrupt/workqueue latency) isn't it?
> Hmmm, that does sound very long. Will try out some experiments and see.
>
yeah
>
>> > If you are triple buffering, it should not end up in the critical
>> > path (since the gpu already has the 3rd buffer to start on the next
>> > frame). And, well, if you do it all in the kernel you probably need
>> > to toss things over to a workqueue anyways.
>>
>> Just a quick comment on the kernel flip queue issue.
>>
>> 16 ms scheduling latency sounds awful but totally doable with a less
>> than stellar ddx driver going into limbo land and so preventing your
>> single threaded X from doing more useful stuff. Is this really the
>> linux scheduler being stupid?
>
> Ahahhaaa!! Yes!!! Really good point. We generally don't have 2D HW and
> so rely on pixman to perform all 2D operations which does indeed tie
> up that thread for fairly long periods of time.
>
> We've had internal discussions about introducing a thread (gulp) in
> the DDX to off-load drawing operations to. I think we were all a bit
> scared by that idea though.
>
thread does sound a bit scary.. it probably could be done if you treat
it like a virtual cpu and have WaitMarker or PrepareAccess for sw
fallbacks synchronize properly..
I bet you'd be much better off just making non-scanout pixmaps cached
and doing cache sync ops when needed for dri2 buffers. Sw fallbacks
on uncached buffers probably aren't exactly the hot ticket.
>
> BTW: I wasn't suggesting it was the linux scheduler being stupid, just
> that there is sometimes lots of contention over the CPU cores and X
> is just one thread among many wanting to run.
>
>
>> At least my impression was that the hw/kernel flip queue is to save
>> power so that you can queue up a few frames and everything goes to
>> sleep for half a second or so (at 24fps or whatever movie your
>> showing). Needing to schedule 5 frames ahead with pageflips under
>> load is just guaranteed to result in really horrible interactivity
>> and so awful user experience
>
> Agreed. There's always a tradeoff between tolerance to variable frame
> rendering time/system latency (lot of buffers) and UI latency (few
> buffers).
>
> As a side note, video playback is one use-case for explicit sync
> objects which implicit/buffer-based sync doesn't handle: Queue up lots
> of video frames for display, but mark those "display buffer"
> operations as depending on explicit sync objects which get signalled
> by the audio clock. Not sure Android actually does that yet though.
> Anyway, off topic.
>
w/ dmafence, rather than explicit fences, I suppose you could add some
way to queue the buffer to the audio device and have the audio device
signal the fence. I suppose it does sound a bit funny for ALSA to
have a DMA_BUF_AV_SYNC ioctl for this sort of case?
I don't think there is anything like it in EGL, but there is
oml_sync_control extension for more precise control of presentation
time. But this is all implemented in userspace and doesn't really
work out w/ >double buffering. This is part of the reason for the
timing information in vblank events. Of course it doesn't have any
tie in to audio subsystem, but in practice this really shouldn't be
needed. Audio samples are either rendered at a very predictable rate,
or sound like sh** with lots of pops and cut outs.
BR,
-R
>
> Cheers,
>
> Tom
>
>
>
>
>
^ permalink raw reply
* [PATCH v2] video: imxfb: Fix retrieve values from DT
From: Alexander Shiyan @ 2013-08-10 7:33 UTC (permalink / raw)
To: linux-arm-kernel
Display settings should be retrieved from "display" node, not from
root fb node. This patch fix this bug.
Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
---
drivers/video/imxfb.c | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
index 38733ac..8e104c4 100644
--- a/drivers/video/imxfb.c
+++ b/drivers/video/imxfb.c
@@ -753,12 +753,12 @@ static int imxfb_resume(struct platform_device *dev)
#define imxfb_resume NULL
#endif
-static int imxfb_init_fbinfo(struct platform_device *pdev)
+static int imxfb_init_fbinfo(struct platform_device *pdev,
+ struct device_node *np)
{
struct imx_fb_platform_data *pdata = pdev->dev.platform_data;
struct fb_info *info = dev_get_drvdata(&pdev->dev);
struct imxfb_info *fbi = info->par;
- struct device_node *np;
pr_debug("%s\n",__func__);
@@ -799,7 +799,6 @@ static int imxfb_init_fbinfo(struct platform_device *pdev)
fbi->lcd_power = pdata->lcd_power;
fbi->backlight_power = pdata->backlight_power;
} else {
- np = pdev->dev.of_node;
info->var.grayscale = of_property_read_bool(np,
"cmap-greyscale");
fbi->cmap_inverse = of_property_read_bool(np, "cmap-inverse");
@@ -858,6 +857,7 @@ static int imxfb_of_read_mode(struct device *dev, struct device_node *np,
static int imxfb_probe(struct platform_device *pdev)
{
+ struct device_node *display_np = NULL;
struct imxfb_info *fbi;
struct fb_info *info;
struct imx_fb_platform_data *pdata;
@@ -887,7 +887,17 @@ static int imxfb_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, info);
- ret = imxfb_init_fbinfo(pdev);
+ if (pdev->dev.of_node) {
+ display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);
+ if (!display_np) {
+ dev_err(&pdev->dev,
+ "No display defined in devicetree\n");
+ ret = -EINVAL;
+ goto failed_init;
+ }
+ }
+
+ ret = imxfb_init_fbinfo(pdev, display_np);
if (ret < 0)
goto failed_init;
@@ -898,16 +908,8 @@ static int imxfb_probe(struct platform_device *pdev)
fbi->mode = pdata->mode;
fbi->num_modes = pdata->num_modes;
} else {
- struct device_node *display_np;
fb_mode = NULL;
- display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);
- if (!display_np) {
- dev_err(&pdev->dev, "No display defined in devicetree\n");
- ret = -EINVAL;
- goto failed_of_parse;
- }
-
/*
* imxfb does not support more modes, we choose only the native
* mode.
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 19/19] drm/rcar-du: Port to the Common Display Framework
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
drivers/gpu/drm/rcar-du/Kconfig | 3 +-
drivers/gpu/drm/rcar-du/Makefile | 7 +-
drivers/gpu/drm/rcar-du/rcar_du_connector.c | 164 ++++++++++++++++
drivers/gpu/drm/rcar-du/rcar_du_connector.h | 36 ++++
drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 2 +-
drivers/gpu/drm/rcar-du/rcar_du_drv.c | 279 ++++++++++++++++++++++++----
drivers/gpu/drm/rcar-du/rcar_du_drv.h | 28 ++-
drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 87 ++++-----
drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 22 +--
drivers/gpu/drm/rcar-du/rcar_du_kms.c | 116 ++++++++++--
drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | 131 -------------
drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h | 25 ---
drivers/gpu/drm/rcar-du/rcar_du_vgacon.c | 96 ----------
drivers/gpu/drm/rcar-du/rcar_du_vgacon.h | 23 ---
include/linux/platform_data/rcar-du.h | 55 +-----
15 files changed, 611 insertions(+), 463 deletions(-)
create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_connector.c
create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_connector.h
delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vgacon.h
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index c590cd9..a54eeba 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -1,9 +1,10 @@
config DRM_RCAR_DU
tristate "DRM Support for R-Car Display Unit"
- depends on DRM && ARM
+ depends on DRM && ARM && OF
select DRM_KMS_HELPER
select DRM_KMS_CMA_HELPER
select DRM_GEM_CMA_HELPER
+ select VIDEOMODE_HELPERS
help
Choose this option if you have an R-Car chipset.
If M is selected the module will be called rcar-du-drm.
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
index 12b8d44..3ac8566 100644
--- a/drivers/gpu/drm/rcar-du/Makefile
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -1,11 +1,10 @@
-rcar-du-drm-y := rcar_du_crtc.o \
+rcar-du-drm-y := rcar_du_connector.o \
+ rcar_du_crtc.o \
rcar_du_drv.o \
rcar_du_encoder.o \
rcar_du_group.o \
rcar_du_kms.o \
- rcar_du_lvdscon.o \
- rcar_du_plane.o \
- rcar_du_vgacon.o
+ rcar_du_plane.o
rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_connector.c b/drivers/gpu/drm/rcar-du/rcar_du_connector.c
new file mode 100644
index 0000000..a09aada
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_connector.c
@@ -0,0 +1,164 @@
+/*
+ * rcar_du_connector.c -- R-Car Display Unit Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/export.h>
+#include <video/videomode.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_connector.h"
+#include "rcar_du_encoder.h"
+#include "rcar_du_kms.h"
+
+static int rcar_du_connector_get_modes(struct drm_connector *connector)
+{
+ struct rcar_du_connector *rcon = to_rcar_connector(connector);
+ struct display_entity *entity = to_display_entity(rcon->pad->entity);
+ const struct videomode *modes;
+ int ret;
+ int i;
+
+ ret = display_entity_get_modes(entity, rcon->pad->index, &modes);
+ if (ret <= 0)
+ return drm_add_modes_noedid(connector, 1280, 768);
+
+ for (i = 0; i < ret; ++i) {
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_create(connector->dev);
+ if (mode = NULL)
+ break;
+
+ mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
+ drm_display_mode_from_videomode(&modes[i], mode);
+ drm_mode_probed_add(connector, mode);
+ }
+
+ return i;
+}
+
+static int rcar_du_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static struct drm_encoder *
+rcar_du_connector_best_encoder(struct drm_connector *connector)
+{
+ struct rcar_du_connector *rcon = to_rcar_connector(connector);
+
+ return &rcon->encoder->encoder;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+ .get_modes = rcar_du_connector_get_modes,
+ .mode_valid = rcar_du_connector_mode_valid,
+ .best_encoder = rcar_du_connector_best_encoder,
+};
+
+static void rcar_du_connector_destroy(struct drm_connector *connector)
+{
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+rcar_du_connector_detect(struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = rcar_du_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = rcar_du_connector_destroy,
+};
+
+struct rcar_du_connector *
+rcar_du_connector_create(struct rcar_du_device *rcdu,
+ struct rcar_du_encoder *renc, struct media_pad *pad)
+{
+ struct display_entity *entity = to_display_entity(pad->entity);
+ struct display_entity_interface_params params;
+ struct rcar_du_connector *rcon;
+ struct drm_connector *connector;
+ int connector_type;
+ unsigned int width;
+ unsigned int height;
+ int ret;
+
+ rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
+ if (rcon = NULL)
+ return ERR_PTR(-ENOMEM);
+
+ rcon->encoder = renc;
+ rcon->pad = pad;
+
+ connector = &rcon->connector;
+
+ ret = display_entity_get_size(entity, &width, &height);
+ if (ret < 0) {
+ connector->display_info.width_mm = 0;
+ connector->display_info.height_mm = 0;
+ } else {
+ connector->display_info.width_mm = width;
+ connector->display_info.height_mm = height;
+ }
+
+ ret = display_entity_get_params(entity, pad->index, ¶ms);
+ if (ret < 0) {
+ dev_dbg(rcdu->dev,
+ "failed to retrieve connector %s parameters\n",
+ entity->name);
+ return ERR_PTR(ret);
+ }
+
+ switch (params.type) {
+ case DISPLAY_ENTITY_INTERFACE_VGA:
+ connector_type = DRM_MODE_CONNECTOR_VGA;
+ break;
+ case DISPLAY_ENTITY_INTERFACE_LVDS:
+ connector_type = DRM_MODE_CONNECTOR_LVDS;
+ break;
+ default:
+ connector_type = DRM_MODE_CONNECTOR_Unknown;
+ break;
+ }
+
+ ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
+ connector_type);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ drm_connector_helper_add(connector, &connector_helper_funcs);
+ ret = drm_sysfs_connector_add(connector);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ drm_object_property_set_value(&connector->base,
+ rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+
+ ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ connector->encoder = &renc->encoder;
+
+ return rcon;
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_connector.h b/drivers/gpu/drm/rcar-du/rcar_du_connector.h
new file mode 100644
index 0000000..b9a0833
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_connector.h
@@ -0,0 +1,36 @@
+/*
+ * rcar_du_connector.h -- R-Car Display Unit Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_CONNECTOR_H__
+#define __RCAR_DU_CONNECTOR_H__
+
+#include <drm/drm_crtc.h>
+
+struct media_pad;
+struct rcar_du_device;
+struct rcar_du_encoder;
+
+struct rcar_du_connector {
+ struct drm_connector connector;
+ struct rcar_du_encoder *encoder;
+ struct media_pad *pad;
+};
+
+#define to_rcar_connector(c) \
+ container_of(c, struct rcar_du_connector, connector)
+
+struct rcar_du_connector *
+rcar_du_connector_create(struct rcar_du_device *rcdu,
+ struct rcar_du_encoder *renc, struct media_pad *pad);
+
+#endif /* __RCAR_DU_CONNECTOR_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index 43e7575..4e8bfff 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -15,11 +15,11 @@
#define __RCAR_DU_CRTC_H__
#include <linux/mutex.h>
-#include <linux/platform_data/rcar-du.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
+enum rcar_du_output;
struct rcar_du_group;
struct rcar_du_plane;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 0a9f1bb..eda9ca9 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -15,6 +15,8 @@
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_data/rcar-du.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/slab.h>
@@ -30,6 +32,153 @@
#include "rcar_du_regs.h"
/* -----------------------------------------------------------------------------
+ * Display Entities
+ */
+
+static int rcdu_entity_set_stream(struct display_entity *ent, unsigned int port,
+ enum display_entity_stream_state state)
+{
+ return 0;
+}
+
+static const struct display_entity_video_ops rcdu_entity_video_ops = {
+ .set_stream = rcdu_entity_set_stream,
+};
+
+static const struct display_entity_ops rcdu_entity_ops = {
+ .video = &rcdu_entity_video_ops,
+};
+
+static struct media_pad *rcar_du_find_remote_pad(struct rcar_du_device *rcdu,
+ unsigned int local_pad)
+{
+ struct display_entity *entity;
+ unsigned int i;
+
+ list_for_each_entry(entity, &rcdu->notifier.done, list) {
+ const struct display_entity_graph_data *graph + entity->match->data;
+ const struct display_entity_source_data *source + graph->sources;
+
+ for (i = 0; i < entity->entity.num_pads; ++i) {
+ struct media_pad *pad = &entity->entity.pads[i];
+
+ if (pad->flags & MEDIA_PAD_FL_SOURCE)
+ continue;
+
+ if (!strcmp(source->name, "rcar-du") &&
+ source->port = local_pad)
+ return pad;
+ }
+ }
+
+ return NULL;
+}
+
+static int rcar_du_create_local_links(struct rcar_du_device *rcdu)
+{
+ u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+ unsigned int i;
+
+ for (i = 0; i < rcdu->entity.entity.num_pads; ++i) {
+ struct media_pad *pad;
+ int ret;
+
+ pad = rcar_du_find_remote_pad(rcdu, i);
+ if (pad = NULL)
+ continue;
+
+ ret = media_entity_create_link(&rcdu->entity.entity, i,
+ pad->entity, pad->index,
+ link_flags);
+ if (ret < 0) {
+ dev_err(rcdu->dev,
+ "failed to create %s:%u -> %s:%u link\n",
+ rcdu->entity.entity.name, i,
+ pad->entity->name, pad->index);
+ return ret;
+ }
+ }
+
+ return 0;
+};
+
+static int rcar_du_graph_init(struct rcar_du_device *rcdu)
+{
+ struct display_entity *entity;
+ unsigned int num_pads;
+ unsigned int i;
+ int ret;
+
+ /* Count the number of output pads and initialize the DU entity. */
+ for (i = 0, num_pads = 0; rcdu->info->routes[i].output; ++i)
+ num_pads++;
+
+ rcdu->entity.dev = rcdu->dev;
+ rcdu->entity.ops = &rcdu_entity_ops;
+ strcpy(rcdu->entity.name, "R-Car DU");
+
+ ret = display_entity_init(&rcdu->entity, 0, num_pads);
+ if (ret < 0)
+ return ret;
+
+ /* Create links between all entities. In the non-DT case, this is a two
+ * steps process, we first create links between all external entities,
+ * and then between the DU entity and the external entities.
+ */
+ if (rcdu->dev->of_node) {
+ ret = display_of_entity_link_graph(rcdu->dev,
+ &rcdu->notifier.done,
+ &rcdu->entity);
+ } else {
+ ret = display_entity_link_graph(rcdu->dev,
+ &rcdu->notifier.done);
+ if (!ret)
+ ret = rcar_du_create_local_links(rcdu);
+ }
+
+ if (ret < 0) {
+ dev_err(rcdu->dev, "unable to link graph\n");
+ return ret;
+ }
+
+ /* Register the media device and all entities. */
+ rcdu->mdev.dev = rcdu->dev;
+ strlcpy(rcdu->mdev.model, dev_name(rcdu->dev),
+ sizeof(rcdu->mdev.model));
+
+ ret = media_device_register(&rcdu->mdev);
+ if (ret < 0)
+ return ret;
+
+ list_for_each_entry(entity, &rcdu->notifier.done, list) {
+ ret = display_entity_register(&rcdu->mdev, entity);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = display_entity_register(&rcdu->mdev, &rcdu->entity);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void rcar_du_graph_cleanup(struct rcar_du_device *rcdu)
+{
+ struct display_entity *entity;
+
+ list_for_each_entry(entity, &rcdu->notifier.done, list)
+ display_entity_unregister(entity);
+
+ display_entity_unregister(&rcdu->entity);
+ display_entity_cleanup(&rcdu->entity);
+
+ media_device_unregister(&rcdu->mdev);
+}
+
+/* -----------------------------------------------------------------------------
* DRM operations
*/
@@ -44,6 +193,8 @@ static int rcar_du_unload(struct drm_device *dev)
drm_mode_config_cleanup(dev);
drm_vblank_cleanup(dev);
+ rcar_du_graph_cleanup(rcdu);
+
dev->irq_enabled = 0;
dev->dev_private = NULL;
@@ -53,25 +204,10 @@ static int rcar_du_unload(struct drm_device *dev)
static int rcar_du_load(struct drm_device *dev, unsigned long flags)
{
struct platform_device *pdev = dev->platformdev;
- struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
- struct rcar_du_device *rcdu;
+ struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
struct resource *mem;
int ret;
- if (pdata = NULL) {
- dev_err(dev->dev, "no platform data\n");
- return -ENODEV;
- }
-
- rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
- if (rcdu = NULL) {
- dev_err(dev->dev, "failed to allocate private data\n");
- return -ENOMEM;
- }
-
- rcdu->dev = &pdev->dev;
- rcdu->pdata = pdata;
- rcdu->info = (struct rcar_du_device_info *)pdev->id_entry->driver_data;
rcdu->ddev = dev;
dev->dev_private = rcdu;
@@ -81,6 +217,13 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
if (IS_ERR(rcdu->mmio))
return PTR_ERR(rcdu->mmio);
+ /* Display Entities */
+ ret = rcar_du_graph_init(rcdu);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to initialize display entities\n");
+ goto done;
+ }
+
/* DRM/KMS objects */
ret = rcar_du_modeset_init(rcdu);
if (ret < 0) {
@@ -97,8 +240,6 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
dev->irq_enabled = 1;
- platform_set_drvdata(pdev, rcdu);
-
done:
if (ret)
rcar_du_unload(dev);
@@ -218,33 +359,23 @@ static const struct dev_pm_ops rcar_du_pm_ops = {
* Platform driver
*/
-static int rcar_du_probe(struct platform_device *pdev)
-{
- return drm_platform_init(&rcar_du_driver, pdev);
-}
-
-static int rcar_du_remove(struct platform_device *pdev)
-{
- drm_platform_exit(&rcar_du_driver, pdev);
-
- return 0;
-}
-
static const struct rcar_du_device_info rcar_du_r8a7779_info = {
.features = 0,
.num_crtcs = 2,
- .routes = {
+ .routes = (const struct rcar_du_output_routing[]) {
/* R8A7779 has two RGB outputs and one (currently unsupported)
* TCON output.
*/
- [RCAR_DU_OUTPUT_DPAD0] = {
+ {
+ .output = RCAR_DU_OUTPUT_DPAD0,
.possible_crtcs = BIT(0),
.encoder_type = DRM_MODE_ENCODER_NONE,
- },
- [RCAR_DU_OUTPUT_DPAD1] = {
+ }, {
+ .output = RCAR_DU_OUTPUT_DPAD1,
.possible_crtcs = BIT(1) | BIT(0),
.encoder_type = DRM_MODE_ENCODER_NONE,
},
+ { },
},
.num_lvds = 0,
};
@@ -253,22 +384,24 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = {
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_ALIGN_128B
| RCAR_DU_FEATURE_DEFR8,
.num_crtcs = 3,
- .routes = {
+ .routes = (const struct rcar_du_output_routing[]) {
/* R8A7790 has one RGB output, two LVDS outputs and one
* (currently unsupported) TCON output.
*/
- [RCAR_DU_OUTPUT_DPAD0] = {
+ {
+ .output = RCAR_DU_OUTPUT_DPAD0,
.possible_crtcs = BIT(2) | BIT(1) | BIT(0),
.encoder_type = DRM_MODE_ENCODER_NONE,
- },
- [RCAR_DU_OUTPUT_LVDS0] = {
+ }, {
+ .output = RCAR_DU_OUTPUT_LVDS0,
.possible_crtcs = BIT(0),
.encoder_type = DRM_MODE_ENCODER_LVDS,
- },
- [RCAR_DU_OUTPUT_LVDS1] = {
+ }, {
+ .output = RCAR_DU_OUTPUT_LVDS1,
.possible_crtcs = BIT(2) | BIT(1),
.encoder_type = DRM_MODE_ENCODER_LVDS,
},
+ { },
},
.num_lvds = 2,
};
@@ -278,9 +411,72 @@ static const struct platform_device_id rcar_du_id_table[] = {
{ "rcar-du-r8a7790", (kernel_ulong_t)&rcar_du_r8a7790_info },
{ }
};
-
MODULE_DEVICE_TABLE(platform, rcar_du_id_table);
+static const struct of_device_id rcar_du_of_table[] = {
+ { .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
+ { .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rcar_du_of_table);
+
+static int rcar_du_notifier_complete(struct display_entity_notifier *notifier)
+{
+ struct platform_device *pdev = to_platform_device(notifier->dev);
+
+ return drm_platform_init(&rcar_du_driver, pdev);
+}
+
+static int rcar_du_probe(struct platform_device *pdev)
+{
+ struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *np = pdev->dev.of_node;
+ struct rcar_du_device *rcdu;
+ int ret;
+
+ rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
+ if (rcdu = NULL)
+ return -ENOMEM;
+
+ rcdu->dev = &pdev->dev;
+
+ if (np)
+ rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data;
+ else
+ rcdu->info = (void *)platform_get_device_id(pdev)->driver_data;
+
+ platform_set_drvdata(pdev, rcdu);
+
+ rcdu->notifier.dev = rcdu->dev;
+ rcdu->notifier.complete = rcar_du_notifier_complete;
+
+ if (np) {
+ ret = display_of_entity_build_notifier(&rcdu->notifier, np);
+ } else if (pdata) {
+ ret = display_entity_build_notifier(&rcdu->notifier,
+ pdata->graph);
+ } else {
+ dev_err(&pdev->dev, "no platform data");
+ ret = -ENXIO;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return display_entity_register_notifier(&rcdu->notifier);
+}
+
+static int rcar_du_remove(struct platform_device *pdev)
+{
+ struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
+
+ display_entity_unregister_notifier(&rcdu->notifier);
+
+ drm_platform_exit(&rcar_du_driver, pdev);
+
+ return 0;
+}
+
static struct platform_driver rcar_du_platform_driver = {
.probe = rcar_du_probe,
.remove = rcar_du_remove,
@@ -288,6 +484,7 @@ static struct platform_driver rcar_du_platform_driver = {
.owner = THIS_MODULE,
.name = "rcar-du",
.pm = &rcar_du_pm_ops,
+ .of_match_table = of_match_ptr(rcar_du_of_table),
},
.id_table = rcar_du_id_table,
};
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
index 65d2d63..49338e1 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -15,7 +15,8 @@
#define __RCAR_DU_DRV_H__
#include <linux/kernel.h>
-#include <linux/platform_data/rcar-du.h>
+#include <media/media-device.h>
+#include <video/display.h>
#include "rcar_du_crtc.h"
#include "rcar_du_group.h"
@@ -31,16 +32,28 @@ struct rcar_du_lvdsenc;
#define RCAR_DU_FEATURE_ALIGN_128B (1 << 1) /* Align pitches to 128 bytes */
#define RCAR_DU_FEATURE_DEFR8 (1 << 2) /* Has DEFR8 register */
+enum rcar_du_output {
+ RCAR_DU_OUTPUT_NONE,
+ RCAR_DU_OUTPUT_DPAD0,
+ RCAR_DU_OUTPUT_DPAD1,
+ RCAR_DU_OUTPUT_LVDS0,
+ RCAR_DU_OUTPUT_LVDS1,
+ RCAR_DU_OUTPUT_TCON,
+ RCAR_DU_OUTPUT_MAX,
+};
+
/*
* struct rcar_du_output_routing - Output routing specification
+ * @output: DU output
* @possible_crtcs: bitmask of possible CRTCs for the output
* @encoder_type: DRM type of the internal encoder associated with the output
*
* The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data
- * specify the valid SoC outputs, which CRTCs can drive the output, and the type
- * of in-SoC encoder for the output.
+ * specify an SoC output, which CRTCs can drive it, and the type of in-SoC
+ * encoder for the output.
*/
struct rcar_du_output_routing {
+ enum rcar_du_output output;
unsigned int possible_crtcs;
unsigned int encoder_type;
};
@@ -49,23 +62,26 @@ struct rcar_du_output_routing {
* struct rcar_du_device_info - DU model-specific information
* @features: device features (RCAR_DU_FEATURE_*)
* @num_crtcs: total number of CRTCs
- * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*)
+ * @routes: array of CRTC to output routes, indexed by port number
* @num_lvds: number of internal LVDS encoders
*/
struct rcar_du_device_info {
unsigned int features;
unsigned int num_crtcs;
- struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
+ const struct rcar_du_output_routing *routes;
unsigned int num_lvds;
};
struct rcar_du_device {
struct device *dev;
- const struct rcar_du_platform_data *pdata;
const struct rcar_du_device_info *info;
void __iomem *mmio;
+ struct media_device mdev;
+ struct display_entity entity;
+ struct display_entity_notifier notifier;
+
struct drm_device *ddev;
struct drm_fbdev_cma *fbdev;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
index 3daa7a1..9991fb0 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -20,25 +20,7 @@
#include "rcar_du_drv.h"
#include "rcar_du_encoder.h"
#include "rcar_du_kms.h"
-#include "rcar_du_lvdscon.h"
#include "rcar_du_lvdsenc.h"
-#include "rcar_du_vgacon.h"
-
-/* -----------------------------------------------------------------------------
- * Common connector functions
- */
-
-struct drm_encoder *
-rcar_du_connector_best_encoder(struct drm_connector *connector)
-{
- struct rcar_du_connector *rcon = to_rcar_connector(connector);
-
- return &rcon->encoder->encoder;
-}
-
-/* -----------------------------------------------------------------------------
- * Encoder
- */
static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode)
{
@@ -139,10 +121,9 @@ static const struct drm_encoder_funcs encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
-int rcar_du_encoder_init(struct rcar_du_device *rcdu,
- enum rcar_du_encoder_type type,
- enum rcar_du_output output,
- const struct rcar_du_encoder_data *data)
+struct rcar_du_encoder *
+rcar_du_encoder_create(struct rcar_du_device *rcdu, unsigned int port,
+ struct media_pad *pad)
{
struct rcar_du_encoder *renc;
unsigned int encoder_type;
@@ -150,11 +131,13 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
if (renc = NULL)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
- renc->output = output;
+ renc->port = port;
+ renc->output = rcdu->info->routes[port].output;
- switch (output) {
+ /* Do we have an internal LVDS encoder? */
+ switch (renc->output) {
case RCAR_DU_OUTPUT_LVDS0:
renc->lvds = rcdu->lvds[0];
break;
@@ -167,36 +150,44 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
break;
}
- switch (type) {
- case RCAR_DU_ENCODER_VGA:
- encoder_type = DRM_MODE_ENCODER_DAC;
- break;
- case RCAR_DU_ENCODER_LVDS:
- encoder_type = DRM_MODE_ENCODER_LVDS;
- break;
- case RCAR_DU_ENCODER_NONE:
- default:
+ /* Find the encoder type. */
+ if (pad) {
+ struct display_entity *entity = to_display_entity(pad->entity);
+ struct display_entity_interface_params params;
+
+ ret = display_entity_get_params(entity, pad->index, ¶ms);
+ if (ret < 0) {
+ dev_dbg(rcdu->dev,
+ "failed to retrieve encoder %s parameters\n",
+ entity->name);
+ return ERR_PTR(ret);
+ }
+
+ switch (params.type) {
+ case DISPLAY_ENTITY_INTERFACE_DPI:
+ case DISPLAY_ENTITY_INTERFACE_DBI:
+ default:
+ encoder_type = DRM_MODE_ENCODER_NONE;
+ break;
+ case DISPLAY_ENTITY_INTERFACE_LVDS:
+ encoder_type = DRM_MODE_ENCODER_DAC;
+ break;
+ case DISPLAY_ENTITY_INTERFACE_VGA:
+ encoder_type = DRM_MODE_ENCODER_DAC;
+ break;
+ }
+ } else {
/* No external encoder, use the internal encoder type. */
- encoder_type = rcdu->info->routes[output].encoder_type;
- break;
+ encoder_type = rcdu->info->routes[port].encoder_type;
}
+ /* Initialize the encoder. */
ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
encoder_type);
if (ret < 0)
- return ret;
+ return ERR_PTR(ret);
drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
- switch (encoder_type) {
- case DRM_MODE_ENCODER_LVDS:
- return rcar_du_lvds_connector_init(rcdu, renc,
- &data->connector.lvds.panel);
-
- case DRM_MODE_ENCODER_DAC:
- return rcar_du_vga_connector_init(rcdu, renc);
-
- default:
- return -EINVAL;
- }
+ return renc;
}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
index 0e5a65e..7a19d5b 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
@@ -14,15 +14,15 @@
#ifndef __RCAR_DU_ENCODER_H__
#define __RCAR_DU_ENCODER_H__
-#include <linux/platform_data/rcar-du.h>
-
#include <drm/drm_crtc.h>
+struct media_pad;
struct rcar_du_device;
struct rcar_du_lvdsenc;
struct rcar_du_encoder {
struct drm_encoder encoder;
+ unsigned int port;
enum rcar_du_output output;
struct rcar_du_lvdsenc *lvds;
};
@@ -30,20 +30,8 @@ struct rcar_du_encoder {
#define to_rcar_encoder(e) \
container_of(e, struct rcar_du_encoder, encoder)
-struct rcar_du_connector {
- struct drm_connector connector;
- struct rcar_du_encoder *encoder;
-};
-
-#define to_rcar_connector(c) \
- container_of(c, struct rcar_du_connector, connector)
-
-struct drm_encoder *
-rcar_du_connector_best_encoder(struct drm_connector *connector);
-
-int rcar_du_encoder_init(struct rcar_du_device *rcdu,
- enum rcar_du_encoder_type type,
- enum rcar_du_output output,
- const struct rcar_du_encoder_data *data);
+struct rcar_du_encoder *
+rcar_du_encoder_create(struct rcar_du_device *rcdu, unsigned int port,
+ struct media_pad *pad);
#endif /* __RCAR_DU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index b31ac08..2dc9e80 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -17,6 +17,7 @@
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
+#include "rcar_du_connector.h"
#include "rcar_du_crtc.h"
#include "rcar_du_drv.h"
#include "rcar_du_encoder.h"
@@ -179,6 +180,89 @@ static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
.output_poll_changed = rcar_du_output_poll_changed,
};
+/* -----------------------------------------------------------------------------
+ * Pipeline and Mode Setting Initialization
+ */
+
+static int rcar_du_pipe_init(struct rcar_du_device *rcdu, unsigned int port,
+ struct media_pad *remote)
+{
+ struct media_pad *pad_encoder = NULL;
+ struct media_pad *pad_connector = remote;
+ struct rcar_du_encoder *renc;
+ struct rcar_du_connector *rcon;
+ struct media_entity *entity;
+
+ /* We need to expose one KMS encoder and one KMS connector for an
+ * arbitrarily long chain of external entities. Walk the chain and map
+ * the last entity to the connector, and the next-to-last entity to the
+ * encoder.
+ */
+
+ /* Start at the entity connected to the DU output. */
+ entity = remote->entity;
+
+ dev_dbg(rcdu->dev, "%s: starting at entity %s pad %u\n", __func__,
+ entity->name, remote->index);
+
+ while (1) {
+ struct media_link *next = NULL;
+ unsigned int i;
+
+ /* Search the entity for an output link. As we only support
+ * linear pipelines, return an error if multiple output links
+ * are found.
+ */
+ dev_dbg(rcdu->dev,
+ "%s: searching for an output link on entity %s\n",
+ __func__, entity->name);
+
+ for (i = 0; i < entity->num_links; ++i) {
+ struct media_link *link = &entity->links[i];
+
+ if (link->source->entity != entity)
+ continue;
+
+ if (next)
+ return -EPIPE;
+
+ next = link;
+ }
+
+ if (next = NULL) {
+ dev_dbg(rcdu->dev, "%s: not output link found\n",
+ __func__);
+ break;
+ }
+
+ dev_dbg(rcdu->dev,
+ "%s: output link %s:%u -> %s:%u found\n", __func__,
+ next->source->entity->name, next->source->index,
+ next->sink->entity->name, next->sink->index);
+
+ pad_encoder = next->source;
+ pad_connector = next->sink;
+ entity = pad_connector->entity;
+ }
+
+ dev_dbg(rcdu->dev,
+ "%s: encoder pad %s/%u connector pad %s/%u\n", __func__,
+ pad_encoder ? pad_encoder->entity->name : NULL,
+ pad_encoder ? pad_encoder->index : 0,
+ pad_connector->entity->name, pad_connector->index);
+
+ /* Create the encoder and connector. */
+ renc = rcar_du_encoder_create(rcdu, port, pad_encoder);
+ if (IS_ERR(renc))
+ return PTR_ERR(renc);
+
+ rcon = rcar_du_connector_create(rcdu, renc, pad_connector);
+ if (IS_ERR(rcon))
+ return PTR_ERR(rcon);
+
+ return 0;
+}
+
int rcar_du_modeset_init(struct rcar_du_device *rcdu)
{
static const unsigned int mmio_offsets[] = {
@@ -188,6 +272,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
struct drm_device *dev = rcdu->ddev;
struct drm_encoder *encoder;
struct drm_fbdev_cma *fbdev;
+ unsigned int num_encoders = 0;
unsigned int num_groups;
unsigned int i;
int ret;
@@ -226,29 +311,26 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
return ret;
}
- /* Initialize the encoders. */
+ /* Initialize the internal encoders. */
ret = rcar_du_lvdsenc_init(rcdu);
if (ret < 0)
return ret;
- for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
- const struct rcar_du_encoder_data *pdata - &rcdu->pdata->encoders[i];
- const struct rcar_du_output_routing *route - &rcdu->info->routes[pdata->output];
+ /* Create an encoder and a connector for each output connected to
+ * external entities.
+ */
+ for (i = 0; i < rcdu->entity.entity.num_pads; ++i) {
+ struct media_pad *pad;
- if (pdata->type = RCAR_DU_ENCODER_UNUSED)
+ pad = media_entity_remote_pad(&rcdu->entity.entity.pads[i]);
+ if (pad = NULL)
continue;
- if (pdata->output >= RCAR_DU_OUTPUT_MAX ||
- route->possible_crtcs = 0) {
- dev_warn(rcdu->dev,
- "encoder %u references unexisting output %u, skipping\n",
- i, pdata->output);
- continue;
- }
+ ret = rcar_du_pipe_init(rcdu, i, pad);
+ if (ret < 0)
+ return ret;
- rcar_du_encoder_init(rcdu, pdata->type, pdata->output, pdata);
+ num_encoders++;
}
/* Set the possible CRTCs and possible clones. There's always at least
@@ -258,10 +340,10 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
const struct rcar_du_output_routing *route - &rcdu->info->routes[renc->output];
+ &rcdu->info->routes[renc->port];
encoder->possible_crtcs = route->possible_crtcs;
- encoder->possible_clones = (1 << rcdu->pdata->num_encoders) - 1;
+ encoder->possible_clones = (1 << num_encoders) - 1;
}
/* Now that the CRTCs have been initialized register the planes. */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
deleted file mode 100644
index 4f3ba93..0000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * rcar_du_lvdscon.c -- R-Car Display Unit LVDS Connector
- *
- * Copyright (C) 2013 Renesas Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <drm/drmP.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_crtc_helper.h>
-
-#include "rcar_du_drv.h"
-#include "rcar_du_encoder.h"
-#include "rcar_du_kms.h"
-#include "rcar_du_lvdscon.h"
-
-struct rcar_du_lvds_connector {
- struct rcar_du_connector connector;
-
- const struct rcar_du_panel_data *panel;
-};
-
-#define to_rcar_lvds_connector(c) \
- container_of(c, struct rcar_du_lvds_connector, connector.connector)
-
-static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
-{
- struct rcar_du_lvds_connector *lvdscon - to_rcar_lvds_connector(connector);
- struct drm_display_mode *mode;
-
- mode = drm_mode_create(connector->dev);
- if (mode = NULL)
- return 0;
-
- mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
- mode->clock = lvdscon->panel->mode.clock;
- mode->hdisplay = lvdscon->panel->mode.hdisplay;
- mode->hsync_start = lvdscon->panel->mode.hsync_start;
- mode->hsync_end = lvdscon->panel->mode.hsync_end;
- mode->htotal = lvdscon->panel->mode.htotal;
- mode->vdisplay = lvdscon->panel->mode.vdisplay;
- mode->vsync_start = lvdscon->panel->mode.vsync_start;
- mode->vsync_end = lvdscon->panel->mode.vsync_end;
- mode->vtotal = lvdscon->panel->mode.vtotal;
- mode->flags = lvdscon->panel->mode.flags;
-
- drm_mode_set_name(mode);
- drm_mode_probed_add(connector, mode);
-
- return 1;
-}
-
-static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- return MODE_OK;
-}
-
-static const struct drm_connector_helper_funcs connector_helper_funcs = {
- .get_modes = rcar_du_lvds_connector_get_modes,
- .mode_valid = rcar_du_lvds_connector_mode_valid,
- .best_encoder = rcar_du_connector_best_encoder,
-};
-
-static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
-{
- drm_sysfs_connector_remove(connector);
- drm_connector_cleanup(connector);
-}
-
-static enum drm_connector_status
-rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force)
-{
- return connector_status_connected;
-}
-
-static const struct drm_connector_funcs connector_funcs = {
- .dpms = drm_helper_connector_dpms,
- .detect = rcar_du_lvds_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = rcar_du_lvds_connector_destroy,
-};
-
-int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
- struct rcar_du_encoder *renc,
- const struct rcar_du_panel_data *panel)
-{
- struct rcar_du_lvds_connector *lvdscon;
- struct drm_connector *connector;
- int ret;
-
- lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL);
- if (lvdscon = NULL)
- return -ENOMEM;
-
- lvdscon->panel = panel;
-
- connector = &lvdscon->connector.connector;
- connector->display_info.width_mm = panel->width_mm;
- connector->display_info.height_mm = panel->height_mm;
-
- ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
- DRM_MODE_CONNECTOR_LVDS);
- if (ret < 0)
- return ret;
-
- drm_connector_helper_add(connector, &connector_helper_funcs);
- ret = drm_sysfs_connector_add(connector);
- if (ret < 0)
- return ret;
-
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
- drm_object_property_set_value(&connector->base,
- rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
-
- ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
- if (ret < 0)
- return ret;
-
- connector->encoder = &renc->encoder;
- lvdscon->connector.encoder = renc;
-
- return 0;
-}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
deleted file mode 100644
index bff8683..0000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * rcar_du_lvdscon.h -- R-Car Display Unit LVDS Connector
- *
- * Copyright (C) 2013 Renesas Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __RCAR_DU_LVDSCON_H__
-#define __RCAR_DU_LVDSCON_H__
-
-struct rcar_du_device;
-struct rcar_du_encoder;
-struct rcar_du_panel_data;
-
-int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
- struct rcar_du_encoder *renc,
- const struct rcar_du_panel_data *panel);
-
-#endif /* __RCAR_DU_LVDSCON_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
deleted file mode 100644
index 72312f7..0000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * rcar_du_vgacon.c -- R-Car Display Unit VGA Connector
- *
- * Copyright (C) 2013 Renesas Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <drm/drmP.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_crtc_helper.h>
-
-#include "rcar_du_drv.h"
-#include "rcar_du_encoder.h"
-#include "rcar_du_kms.h"
-#include "rcar_du_vgacon.h"
-
-static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
-{
- return drm_add_modes_noedid(connector, 1280, 768);
-}
-
-static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- return MODE_OK;
-}
-
-static const struct drm_connector_helper_funcs connector_helper_funcs = {
- .get_modes = rcar_du_vga_connector_get_modes,
- .mode_valid = rcar_du_vga_connector_mode_valid,
- .best_encoder = rcar_du_connector_best_encoder,
-};
-
-static void rcar_du_vga_connector_destroy(struct drm_connector *connector)
-{
- drm_sysfs_connector_remove(connector);
- drm_connector_cleanup(connector);
-}
-
-static enum drm_connector_status
-rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
-{
- return connector_status_connected;
-}
-
-static const struct drm_connector_funcs connector_funcs = {
- .dpms = drm_helper_connector_dpms,
- .detect = rcar_du_vga_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = rcar_du_vga_connector_destroy,
-};
-
-int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
- struct rcar_du_encoder *renc)
-{
- struct rcar_du_connector *rcon;
- struct drm_connector *connector;
- int ret;
-
- rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
- if (rcon = NULL)
- return -ENOMEM;
-
- connector = &rcon->connector;
- connector->display_info.width_mm = 0;
- connector->display_info.height_mm = 0;
-
- ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
- DRM_MODE_CONNECTOR_VGA);
- if (ret < 0)
- return ret;
-
- drm_connector_helper_add(connector, &connector_helper_funcs);
- ret = drm_sysfs_connector_add(connector);
- if (ret < 0)
- return ret;
-
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
- drm_object_property_set_value(&connector->base,
- rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
-
- ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
- if (ret < 0)
- return ret;
-
- connector->encoder = &renc->encoder;
- rcon->encoder = renc;
-
- return 0;
-}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h
deleted file mode 100644
index b12b0cf..0000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * rcar_du_vgacon.h -- R-Car Display Unit VGA Connector
- *
- * Copyright (C) 2013 Renesas Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __RCAR_DU_VGACON_H__
-#define __RCAR_DU_VGACON_H__
-
-struct rcar_du_device;
-struct rcar_du_encoder;
-
-int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
- struct rcar_du_encoder *renc);
-
-#endif /* __RCAR_DU_VGACON_H__ */
diff --git a/include/linux/platform_data/rcar-du.h b/include/linux/platform_data/rcar-du.h
index 1a2e990..b424b75 100644
--- a/include/linux/platform_data/rcar-du.h
+++ b/include/linux/platform_data/rcar-du.h
@@ -14,61 +14,10 @@
#ifndef __RCAR_DU_H__
#define __RCAR_DU_H__
-#include <drm/drm_mode.h>
-
-enum rcar_du_output {
- RCAR_DU_OUTPUT_DPAD0,
- RCAR_DU_OUTPUT_DPAD1,
- RCAR_DU_OUTPUT_LVDS0,
- RCAR_DU_OUTPUT_LVDS1,
- RCAR_DU_OUTPUT_TCON,
- RCAR_DU_OUTPUT_MAX,
-};
-
-enum rcar_du_encoder_type {
- RCAR_DU_ENCODER_UNUSED = 0,
- RCAR_DU_ENCODER_NONE,
- RCAR_DU_ENCODER_VGA,
- RCAR_DU_ENCODER_LVDS,
-};
-
-struct rcar_du_panel_data {
- unsigned int width_mm; /* Panel width in mm */
- unsigned int height_mm; /* Panel height in mm */
- struct drm_mode_modeinfo mode;
-};
-
-struct rcar_du_connector_lvds_data {
- struct rcar_du_panel_data panel;
-};
-
-struct rcar_du_connector_vga_data {
- /* TODO: Add DDC information for EDID retrieval */
-};
-
-/*
- * struct rcar_du_encoder_data - Encoder platform data
- * @type: the encoder type (RCAR_DU_ENCODER_*)
- * @output: the DU output the connector is connected to (RCAR_DU_OUTPUT_*)
- * @connector.lvds: platform data for LVDS connectors
- * @connector.vga: platform data for VGA connectors
- *
- * Encoder platform data describes an on-board encoder, its associated DU SoC
- * output, and the connector.
- */
-struct rcar_du_encoder_data {
- enum rcar_du_encoder_type type;
- enum rcar_du_output output;
-
- union {
- struct rcar_du_connector_lvds_data lvds;
- struct rcar_du_connector_vga_data vga;
- } connector;
-};
+#include <video/display.h>
struct rcar_du_platform_data {
- struct rcar_du_encoder_data *encoders;
- unsigned int num_encoders;
+ const struct display_entity_graph_data *graph;
};
#endif /* __RCAR_DU_H__ */
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 18/19] ARM: shmobile: lager-reference: Add display device nodes to device tree
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
arch/arm/boot/dts/r8a7790-lager-reference.dts | 92 +++++++++++++++++++++++++++
1 file changed, 92 insertions(+)
diff --git a/arch/arm/boot/dts/r8a7790-lager-reference.dts b/arch/arm/boot/dts/r8a7790-lager-reference.dts
index d9a25d5..ba2469b 100644
--- a/arch/arm/boot/dts/r8a7790-lager-reference.dts
+++ b/arch/arm/boot/dts/r8a7790-lager-reference.dts
@@ -42,6 +42,98 @@
gpios = <&gpio5 17 GPIO_ACTIVE_HIGH>;
};
};
+
+ adv7123 {
+ compatible = "adi,adv7123";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ adv7123_in: endpoint {
+ remote-endpoint = <&du_out_rgb>;
+ };
+ };
+ port@1 {
+ reg = <1>;
+ adv7123_out: endpoint {
+ remote-endpoint = <&con_vga_in>;
+ };
+ };
+ };
+ };
+
+ con-vga {
+ compatible = "con-vga";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ con_vga_in: endpoint {
+ remote-endpoint = <&adv7123_out>;
+ };
+ };
+ };
+ };
+
+ panel-dpi {
+ compatible = "panel-dpi";
+
+ width-mm = <210>;
+ height-mm = <158>;
+
+ display-timings {
+ timing {
+ /* 1024x768 @65Hz */
+ clock-frequency = <65000000>;
+ hactive = <1024>;
+ vactive = <768>;
+ hsync-len = <136>;
+ hfront-porch = <20>;
+ hback-porch = <160>;
+ vfront-porch = <3>;
+ vback-porch = <29>;
+ vsync-len = <6>;
+ };
+ };
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ panel_in: endpoint {
+ remote-endpoint = <&du_out_lvds1>;
+ };
+ };
+ };
+ };
+};
+
+&du {
+ pinctrl-0 = <&du_pins>;
+ pinctrl-names = "default";
+};
+
+&du_out_rgb {
+ remote-endpoint = <&adv7123_in>;
+};
+
+&du_out_lvds1 {
+ remote-endpoint = <&panel_in>;
+};
+
+&pfc {
+ du_pins: du {
+ renesas,groups = "du_rgb666", "du_sync_1", "du_clk_out_0";
+ renesas,function = "du";
+ };
};
&pfc {
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 17/19] ARM: shmobile: lager: Port DU platform data to CDF
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
arch/arm/mach-shmobile/board-lager.c | 76 +++++++++++++++++++++++++-----------
1 file changed, 53 insertions(+), 23 deletions(-)
diff --git a/arch/arm/mach-shmobile/board-lager.c b/arch/arm/mach-shmobile/board-lager.c
index 75a01bc..d61b892 100644
--- a/arch/arm/mach-shmobile/board-lager.c
+++ b/arch/arm/mach-shmobile/board-lager.c
@@ -33,6 +33,8 @@
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>
#include <linux/sh_eth.h>
+#include <video/panel-dpi.h>
+#include <video/videomode.h>
#include <mach/common.h>
#include <mach/irqs.h>
#include <mach/r8a7790.h>
@@ -40,35 +42,56 @@
#include <asm/mach/arch.h>
/* DU */
-static struct rcar_du_encoder_data lager_du_encoders[] = {
+static const struct videomode lager_panel_mode = {
+ .pixelclock = 65000000,
+ .hactive = 1024,
+ .hfront_porch = 24,
+ .hback_porch = 160,
+ .hsync_len = 136,
+ .vactive = 768,
+ .vfront_porch = 3,
+ .vback_porch = 29,
+ .vsync_len = 6,
+ .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
+};
+
+static const struct panel_dpi_platform_data lager_panel_data = {
+ .width = 210,
+ .height = 158,
+ .mode = &lager_panel_mode,
+};
+
+static const struct display_entity_graph_data lager_du_entities[] = {
{
- .type = RCAR_DU_ENCODER_VGA,
- .output = RCAR_DU_OUTPUT_DPAD0,
+ .name = "adv7123",
+ .sources = (const struct display_entity_source_data[]) {
+ {
+ .name = "rcar-du",
+ .port = 0,
+ },
+ },
}, {
- .type = RCAR_DU_ENCODER_NONE,
- .output = RCAR_DU_OUTPUT_LVDS1,
- .connector.lvds.panel = {
- .width_mm = 210,
- .height_mm = 158,
- .mode = {
- .clock = 65000,
- .hdisplay = 1024,
- .hsync_start = 1048,
- .hsync_end = 1184,
- .htotal = 1344,
- .vdisplay = 768,
- .vsync_start = 771,
- .vsync_end = 777,
- .vtotal = 806,
- .flags = 0,
+ .name = "con-vga",
+ .sources = (const struct display_entity_source_data[]) {
+ {
+ .name = "adv7123",
+ .port = 1,
},
},
+ }, {
+ .name = "panel-dpi",
+ .sources = (const struct display_entity_source_data[]) {
+ {
+ .name = "rcar-du",
+ .port = 2,
+ },
+ },
+ }, {
},
};
static const struct rcar_du_platform_data lager_du_pdata __initconst = {
- .encoders = lager_du_encoders,
- .num_encoders = ARRAY_SIZE(lager_du_encoders),
+ .graph = lager_du_entities,
};
static const struct resource du_resources[] __initconst = {
@@ -87,8 +110,8 @@ static void __init lager_add_du_device(void)
.id = -1,
.res = du_resources,
.num_res = ARRAY_SIZE(du_resources),
- .data = &du_resources,
- .size_data = sizeof(du_resources),
+ .data = &lager_du_pdata,
+ .size_data = sizeof(lager_du_pdata),
.dma_mask = DMA_BIT_MASK(32),
};
@@ -202,6 +225,7 @@ static void __init lager_add_standard_devices(void)
r8a7790_pinmux_init();
r8a7790_add_standard_devices();
+
platform_device_register_data(&platform_bus, "leds-gpio", -1,
&lager_leds_pdata,
sizeof(lager_leds_pdata));
@@ -220,6 +244,12 @@ static void __init lager_add_standard_devices(void)
ðer_pdata, sizeof(ether_pdata));
lager_add_du_device();
+
+ platform_device_register_simple("adv7123", -1, NULL, 0);
+ platform_device_register_simple("con-vga", -1, NULL, 0);
+ platform_device_register_data(&platform_bus, "panel-dpi", -1,
+ &lager_panel_data,
+ sizeof(lager_panel_data));
}
static const char * const lager_boards_compat_dt[] __initconst = {
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 16/19] ARM: shmobile: marzen: Port DU platform data to CDF
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
arch/arm/mach-shmobile/board-marzen.c | 77 ++++++++++++++++++++++++-----------
1 file changed, 53 insertions(+), 24 deletions(-)
diff --git a/arch/arm/mach-shmobile/board-marzen.c b/arch/arm/mach-shmobile/board-marzen.c
index 6499f1a..be1b7cb 100644
--- a/arch/arm/mach-shmobile/board-marzen.c
+++ b/arch/arm/mach-shmobile/board-marzen.c
@@ -39,6 +39,8 @@
#include <linux/mmc/host.h>
#include <linux/mmc/sh_mobile_sdhi.h>
#include <linux/mfd/tmio.h>
+#include <video/panel-dpi.h>
+#include <video/videomode.h>
#include <mach/r8a7779.h>
#include <mach/common.h>
#include <mach/irqs.h>
@@ -174,35 +176,56 @@ static struct platform_device hspi_device = {
* The panel only specifies the [hv]display and [hv]total values. The position
* and width of the sync pulses don't matter, they're copied from VESA timings.
*/
-static struct rcar_du_encoder_data du_encoders[] = {
+static const struct videomode marzen_panel_mode = {
+ .pixelclock = 65000000,
+ .hactive = 1024,
+ .hfront_porch = 24,
+ .hback_porch = 160,
+ .hsync_len = 136,
+ .vactive = 768,
+ .vfront_porch = 3,
+ .vback_porch = 29,
+ .vsync_len = 6,
+ .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
+};
+
+static const struct panel_dpi_platform_data marzen_panel_data = {
+ .width = 210,
+ .height = 158,
+ .mode = &marzen_panel_mode,
+};
+
+static const struct display_entity_graph_data marzen_du_entities[] = {
{
- .type = RCAR_DU_ENCODER_VGA,
- .output = RCAR_DU_OUTPUT_DPAD0,
+ .name = "adv7123",
+ .sources = (const struct display_entity_source_data[]) {
+ {
+ .name = "rcar-du",
+ .port = 0,
+ },
+ },
}, {
- .type = RCAR_DU_ENCODER_LVDS,
- .output = RCAR_DU_OUTPUT_DPAD1,
- .connector.lvds.panel = {
- .width_mm = 210,
- .height_mm = 158,
- .mode = {
- .clock = 65000,
- .hdisplay = 1024,
- .hsync_start = 1048,
- .hsync_end = 1184,
- .htotal = 1344,
- .vdisplay = 768,
- .vsync_start = 771,
- .vsync_end = 777,
- .vtotal = 806,
- .flags = 0,
+ .name = "con-vga",
+ .sources = (const struct display_entity_source_data[]) {
+ {
+ .name = "adv7123",
+ .port = 1,
},
},
+ }, {
+ .name = "panel-dpi",
+ .sources = (const struct display_entity_source_data[]) {
+ {
+ .name = "rcar-du",
+ .port = 1,
+ },
+ },
+ }, {
},
};
-static const struct rcar_du_platform_data du_pdata __initconst = {
- .encoders = du_encoders,
- .num_encoders = ARRAY_SIZE(du_encoders),
+static const struct rcar_du_platform_data marzen_du_pdata __initconst = {
+ .graph = marzen_du_entities,
};
static const struct resource du_resources[] __initconst = {
@@ -217,8 +240,8 @@ static void __init marzen_add_du_device(void)
.id = -1,
.res = du_resources,
.num_res = ARRAY_SIZE(du_resources),
- .data = &du_pdata,
- .size_data = sizeof(du_pdata),
+ .data = &marzen_du_pdata,
+ .size_data = sizeof(marzen_du_pdata),
.dma_mask = DMA_BIT_MASK(32),
};
@@ -327,6 +350,12 @@ static void __init marzen_init(void)
r8a7779_add_standard_devices();
platform_add_devices(marzen_devices, ARRAY_SIZE(marzen_devices));
marzen_add_du_device();
+
+ platform_device_register_simple("adv7123", -1, NULL, 0);
+ platform_device_register_simple("con-vga", -1, NULL, 0);
+ platform_device_register_data(&platform_bus, "panel-dpi", -1,
+ &marzen_panel_data,
+ sizeof(marzen_panel_data));
}
static const char *marzen_boards_compat_dt[] __initdata = {
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 15/19] ARM: shmobile: r8a7790: Add DU device node to device tree
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
arch/arm/boot/dts/r8a7790.dtsi | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi
index ab0582c..b63f1a6 100644
--- a/arch/arm/boot/dts/r8a7790.dtsi
+++ b/arch/arm/boot/dts/r8a7790.dtsi
@@ -186,4 +186,37 @@
reg = <0 0xe6060000 0 0x250>;
#gpio-range-cells = <3>;
};
+
+ du: display@feb00000 {
+ compatible = "renesas,du-r8a7790";
+ reg = <0 0xfeb00000 0 0x70000>,
+ <0 0xfeb90000 0 0x1c>,
+ <0 0xfeb94000 0 0x1c>;
+ reg-names = "core", "lvds.0", "lvds.1";
+ interrupt-parent = <&gic>;
+ interrupts = <0 256 4
+ 0 268 4
+ 0 269 4>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ du_out_rgb: endpoint {
+ };
+ };
+ port@1 {
+ reg = <1>;
+ du_out_lvds0: endpoint {
+ };
+ };
+ port@2 {
+ reg = <2>;
+ du_out_lvds1: endpoint {
+ };
+ };
+ };
+ };
};
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 14/19] ARM: shmobile: r8a7790: Add DU clocks for DT
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
arch/arm/mach-shmobile/clock-r8a7790.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/mach-shmobile/clock-r8a7790.c b/arch/arm/mach-shmobile/clock-r8a7790.c
index d99b87b..7229f96 100644
--- a/arch/arm/mach-shmobile/clock-r8a7790.c
+++ b/arch/arm/mach-shmobile/clock-r8a7790.c
@@ -257,10 +257,15 @@ static struct clk_lookup lookups[] = {
/* MSTP */
CLKDEV_ICK_ID("lvds.0", "rcar-du-r8a7790", &mstp_clks[MSTP726]),
+ CLKDEV_ICK_ID("lvds.0", "feb00000.display", &mstp_clks[MSTP726]),
CLKDEV_ICK_ID("lvds.1", "rcar-du-r8a7790", &mstp_clks[MSTP725]),
+ CLKDEV_ICK_ID("lvds.1", "feb00000.display", &mstp_clks[MSTP725]),
CLKDEV_ICK_ID("du.0", "rcar-du-r8a7790", &mstp_clks[MSTP724]),
+ CLKDEV_ICK_ID("du.0", "feb00000.display", &mstp_clks[MSTP724]),
CLKDEV_ICK_ID("du.1", "rcar-du-r8a7790", &mstp_clks[MSTP723]),
+ CLKDEV_ICK_ID("du.1", "feb00000.display", &mstp_clks[MSTP723]),
CLKDEV_ICK_ID("du.2", "rcar-du-r8a7790", &mstp_clks[MSTP722]),
+ CLKDEV_ICK_ID("du.2", "feb00000.display", &mstp_clks[MSTP722]),
CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP204]),
CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP203]),
CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP206]),
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 13/19] video: display: Add VGA connector support
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
This driver exposes VGA connectors as display entity devices. The
connectors are passive devices that pass analog VGA signals though. They
optionally cary DDC signals for bidirectional control communications
with the devices connected to the connectors.
EDID retrieval isn't supported yet.
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
drivers/video/display/Kconfig | 11 +++
drivers/video/display/Makefile | 1 +
drivers/video/display/con-vga.c | 148 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 160 insertions(+)
create mode 100644 drivers/video/display/con-vga.c
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 32ce08d..9b482a8 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -5,6 +5,17 @@ menuconfig DISPLAY_CORE
if DISPLAY_CORE
+
+config DISPLAY_CONNECTOR_VGA
+ tristate "VGA Connector"
+ ---help---
+ Support for simple digital (parallel) pixel interface panels. Those
+ panels receive pixel data through a parallel bus and have no control
+ bus.
+
+ If you are in doubt, say N. To compile this driver as a module, choose
+ M here; the module will be called con-vga.
+
config DISPLAY_MIPI_DBI
tristate
default n
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 43cd78d..d03c64a 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1,6 +1,7 @@
display-y := display-core.o \
display-notifier.o
obj-$(CONFIG_DISPLAY_CORE) += display.o
+obj-$(CONFIG_DISPLAY_CONNECTOR_VGA) += con-vga.o
obj-$(CONFIG_DISPLAY_MIPI_DBI) += mipi-dbi-bus.o
obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o
obj-$(CONFIG_DISPLAY_PANEL_R61505) += panel-r61505.o
diff --git a/drivers/video/display/con-vga.c b/drivers/video/display/con-vga.c
new file mode 100644
index 0000000..798ac9e
--- /dev/null
+++ b/drivers/video/display/con-vga.c
@@ -0,0 +1,148 @@
+/*
+ * VGA Connector
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <video/display.h>
+
+struct con_vga {
+ struct display_entity entity;
+};
+
+static int con_vga_set_state(struct display_entity *entity,
+ enum display_entity_state state)
+{
+ struct media_pad *source;
+
+ source = media_entity_remote_pad(&entity->entity.pads[0]);
+ if (source = NULL)
+ return -EPIPE;
+
+ switch (state) {
+ case DISPLAY_ENTITY_STATE_OFF:
+ case DISPLAY_ENTITY_STATE_STANDBY:
+ display_entity_set_stream(to_display_entity(source->entity),
+ source->index,
+ DISPLAY_ENTITY_STREAM_STOPPED);
+ break;
+
+ case DISPLAY_ENTITY_STATE_ON:
+ display_entity_set_stream(to_display_entity(source->entity),
+ source->index,
+ DISPLAY_ENTITY_STREAM_CONTINUOUS);
+ break;
+ }
+
+ return 0;
+}
+
+static int con_vga_get_modes(struct display_entity *entity, unsigned int port,
+ const struct videomode **modes)
+{
+ /* TODO: Add EDID retrieval support. */
+ return 0;
+}
+
+static int con_vga_get_params(struct display_entity *entity, unsigned int port,
+ struct display_entity_interface_params *params)
+{
+ memset(params, 0, sizeof(*params));
+
+ params->type = DISPLAY_ENTITY_INTERFACE_VGA;
+
+ return 0;
+}
+
+static const struct display_entity_control_ops con_vga_control_ops = {
+ .set_state = con_vga_set_state,
+ .get_modes = con_vga_get_modes,
+ .get_params = con_vga_get_params,
+};
+
+static const struct display_entity_ops con_vga_ops = {
+ .ctrl = &con_vga_control_ops,
+};
+
+static int con_vga_remove(struct platform_device *pdev)
+{
+ struct con_vga *con = platform_get_drvdata(pdev);
+
+ display_entity_remove(&con->entity);
+ display_entity_cleanup(&con->entity);
+
+ return 0;
+}
+
+static int con_vga_probe(struct platform_device *pdev)
+{
+ struct con_vga *con;
+ int ret;
+
+ con = devm_kzalloc(&pdev->dev, sizeof(*con), GFP_KERNEL);
+ if (con = NULL)
+ return -ENOMEM;
+
+ con->entity.dev = &pdev->dev;
+ con->entity.ops = &con_vga_ops;
+ strlcpy(con->entity.name, "vga-con", sizeof(con->entity.name));
+
+ ret = display_entity_init(&con->entity, 1, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = display_entity_add(&con->entity);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, con);
+
+ return 0;
+}
+
+static const struct dev_pm_ops con_vga_dev_pm_ops = {
+};
+
+static struct platform_device_id con_vga_id_table[] = {
+ { "con-vga", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, con_vga_id_table);
+
+#ifdef CONFIG_OF
+static struct of_device_id con_vga_of_id_table[] = {
+ { .compatible = "con-vga", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, con_vga_of_id_table);
+#endif
+
+static struct platform_driver con_vga_driver = {
+ .probe = con_vga_probe,
+ .remove = con_vga_remove,
+ .id_table = con_vga_id_table,
+ .driver = {
+ .name = "con-vga",
+ .owner = THIS_MODULE,
+ .pm = &con_vga_dev_pm_ops,
+ .of_match_table = of_match_ptr(con_vga_of_id_table),
+ },
+};
+
+module_platform_driver(con_vga_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("VGA Connector");
+MODULE_LICENSE("GPL");
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 12/19] video: display: Add VGA Digital to Analog Converter support
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
This driver implements support for VGA Digital to Analog Converters
(DACs) that receive pixel data through a DPI interface and have no
control interface (GPIOs- and/or regulators-based control can be
implemented later when needed). It exposes the devices a display
entities.
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
drivers/video/display/Kconfig | 9 +++
drivers/video/display/Makefile | 1 +
drivers/video/display/vga-dac.c | 152 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 162 insertions(+)
create mode 100644 drivers/video/display/vga-dac.c
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 9b44b5f..32ce08d 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -39,4 +39,13 @@ config DISPLAY_PANEL_R61517
If you are in doubt, say N. To compile this driver as a module, choose
M here; the module will be called panel-r61517.
+config DISPLAY_VGA_DAC
+ tristate "VGA Digital to Analog Converters"
+ ---help---
+ Support for simple VGA digital to analog converters. Those converters
+ receive pixel data through a parallel bus and have no control bus.
+
+ If you are in doubt, say N. To compile this driver as a module, choose
+ M here: the module will be called vga-dac.
+
endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 1cdc8d4..43cd78d 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_DISPLAY_MIPI_DBI) += mipi-dbi-bus.o
obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o
obj-$(CONFIG_DISPLAY_PANEL_R61505) += panel-r61505.o
obj-$(CONFIG_DISPLAY_PANEL_R61517) += panel-r61517.o
+obj-$(CONFIG_DISPLAY_VGA_DAC) += vga-dac.o
diff --git a/drivers/video/display/vga-dac.c b/drivers/video/display/vga-dac.c
new file mode 100644
index 0000000..d0256e6
--- /dev/null
+++ b/drivers/video/display/vga-dac.c
@@ -0,0 +1,152 @@
+/*
+ * VGA Digital to Analog Converter
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <video/display.h>
+
+#define VGA_DAC_PORT_SINK 0
+#define VGA_DAC_PORT_SOURCE 1
+
+struct vga_dac {
+ struct display_entity entity;
+};
+
+static inline struct vga_dac *to_vga_dac(struct display_entity *e)
+{
+ return container_of(e, struct vga_dac, entity);
+}
+
+static int vga_dac_set_state(struct display_entity *entity,
+ enum display_entity_state state)
+{
+ struct media_pad *source;
+
+ source = media_entity_remote_pad(&entity->entity.pads[0]);
+ if (source = NULL)
+ return -EPIPE;
+
+ switch (state) {
+ case DISPLAY_ENTITY_STATE_OFF:
+ case DISPLAY_ENTITY_STATE_STANDBY:
+ display_entity_set_stream(to_display_entity(source->entity),
+ source->index,
+ DISPLAY_ENTITY_STREAM_STOPPED);
+ break;
+
+ case DISPLAY_ENTITY_STATE_ON:
+ display_entity_set_stream(to_display_entity(source->entity),
+ source->index,
+ DISPLAY_ENTITY_STREAM_CONTINUOUS);
+ break;
+ }
+
+ return 0;
+}
+
+static int vga_dac_get_params(struct display_entity *entity, unsigned int port,
+ struct display_entity_interface_params *params)
+{
+ memset(params, 0, sizeof(*params));
+
+ if (port = VGA_DAC_PORT_SINK)
+ params->type = DISPLAY_ENTITY_INTERFACE_DPI;
+ else
+ params->type = DISPLAY_ENTITY_INTERFACE_VGA;
+
+ return 0;
+}
+
+static const struct display_entity_control_ops vga_dac_control_ops = {
+ .set_state = vga_dac_set_state,
+ .get_params = vga_dac_get_params,
+};
+
+static const struct display_entity_ops vga_dac_ops = {
+ .ctrl = &vga_dac_control_ops,
+};
+
+static int vga_dac_remove(struct platform_device *pdev)
+{
+ struct vga_dac *dac = platform_get_drvdata(pdev);
+
+ display_entity_remove(&dac->entity);
+ display_entity_cleanup(&dac->entity);
+
+ return 0;
+}
+
+static int vga_dac_probe(struct platform_device *pdev)
+{
+ struct vga_dac *dac;
+ int ret;
+
+ dac = devm_kzalloc(&pdev->dev, sizeof(*dac), GFP_KERNEL);
+ if (dac = NULL)
+ return -ENOMEM;
+
+ dac->entity.dev = &pdev->dev;
+ dac->entity.ops = &vga_dac_ops;
+ strlcpy(dac->entity.name, dev_name(&pdev->dev),
+ sizeof(dac->entity.name));
+
+ ret = display_entity_init(&dac->entity, 1, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = display_entity_add(&dac->entity);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, dac);
+
+ return 0;
+}
+
+static const struct dev_pm_ops vga_dac_dev_pm_ops = {
+};
+
+static struct platform_device_id vga_dac_id_table[] = {
+ { "adv7123", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, vga_dac_id_table);
+
+#ifdef CONFIG_OF
+static struct of_device_id vga_dac_of_id_table[] = {
+ { .compatible = "adi,adv7123", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, vga_dac_of_id_table);
+#endif
+
+static struct platform_driver vga_dac_driver = {
+ .probe = vga_dac_probe,
+ .remove = vga_dac_remove,
+ .id_table = vga_dac_id_table,
+ .driver = {
+ .name = "vga-dac",
+ .owner = THIS_MODULE,
+ .pm = &vga_dac_dev_pm_ops,
+ .of_match_table = of_match_ptr(vga_dac_of_id_table),
+ },
+};
+
+module_platform_driver(vga_dac_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("VGA Digital-to-Analog Converter");
+MODULE_LICENSE("GPL");
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 11/19] video: panel: Add R61517 panel support
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
The R61517 is a MIPI DBI panel controller from Renesas.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/display/Kconfig | 10 +
drivers/video/display/Makefile | 1 +
drivers/video/display/panel-r61517.c | 460 +++++++++++++++++++++++++++++++++++
include/video/panel-r61517.h | 28 +++
4 files changed, 499 insertions(+)
create mode 100644 drivers/video/display/panel-r61517.c
create mode 100644 include/video/panel-r61517.h
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 76729ef..9b44b5f 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -29,4 +29,14 @@ config DISPLAY_PANEL_R61505
If you are in doubt, say N. To compile this driver as a module, choose
M here; the module will be called panel-r61505.
+config DISPLAY_PANEL_R61517
+ tristate "Renesas R61517-based Display Panel"
+ select DISPLAY_MIPI_DBI
+ ---help---
+ Support panels based on the Renesas R61517 panel controller.
+ Those panels are controlled through a MIPI DBI interface.
+
+ If you are in doubt, say N. To compile this driver as a module, choose
+ M here; the module will be called panel-r61517.
+
endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index db8a4c3..1cdc8d4 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_DISPLAY_CORE) += display.o
obj-$(CONFIG_DISPLAY_MIPI_DBI) += mipi-dbi-bus.o
obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o
obj-$(CONFIG_DISPLAY_PANEL_R61505) += panel-r61505.o
+obj-$(CONFIG_DISPLAY_PANEL_R61517) += panel-r61517.o
diff --git a/drivers/video/display/panel-r61517.c b/drivers/video/display/panel-r61517.c
new file mode 100644
index 0000000..ccfec33
--- /dev/null
+++ b/drivers/video/display/panel-r61517.c
@@ -0,0 +1,460 @@
+/*
+ * Renesas R61517-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ * Based on KFR2R09 LCD panel support
+ * Copyright (C) 2009 Magnus Damm
+ * Register settings based on the out-of-tree t33fb.c driver
+ * Copyright (C) 2008 Lineo Solutions, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+
+#include <video/display.h>
+#include <video/mipi-dbi-bus.h>
+#include <video/mipi_display.h>
+#include <video/panel-r61517.h>
+#include <video/videomode.h>
+
+struct r61517 {
+ struct display_entity entity;
+ struct mipi_dbi_device *dbi;
+ const struct panel_r61517_platform_data *pdata;
+};
+
+static inline struct r61517 *to_panel(struct display_entity *e)
+{
+ return container_of(e, struct r61517, entity);
+}
+
+/* -----------------------------------------------------------------------------
+ * Read, write and reset
+ */
+
+static void r61517_write_command(struct r61517 *panel, u16 cmd)
+{
+ mipi_dbi_write_command(panel->dbi, cmd);
+}
+
+static void r61517_write_data(struct r61517 *panel, u8 data)
+{
+ mipi_dbi_write_data(panel->dbi, &data, 1);
+}
+
+static int r61517_read_data(struct r61517 *panel)
+{
+ u8 data;
+ int ret;
+
+ ret = mipi_dbi_read_data(panel->dbi, &data, 1);
+ if (ret < 0)
+ return ret;
+
+ return data;
+}
+
+static void r61517_write(struct r61517 *panel, u8 reg, const u8 *data,
+ size_t len)
+{
+ mipi_dbi_write_command(panel->dbi, reg);
+ mipi_dbi_write_data(panel->dbi, data, len);
+}
+
+static void r61517_write8(struct r61517 *panel, u8 reg, u8 data)
+{
+ r61517_write(panel, reg, &data, 1);
+}
+
+static void r61517_write16(struct r61517 *panel, u8 reg, u16 data)
+{
+ u8 buffer[2] = { (data >> 8) & 0xff, (data >> 0) & 0xff };
+
+ r61517_write(panel, reg, buffer, 2);
+}
+
+static void r61517_write32(struct r61517 *panel, u8 reg, u32 data)
+{
+ u8 buffer[4] = { (data >> 24) & 0xff, (data >> 16) & 0xff,
+ (data >> 8) & 0xff, (data >> 0) & 0xff };
+
+ r61517_write(panel, reg, buffer, 4);
+}
+
+#define r61517_write_array(p, c, a) \
+ r61517_write((p), (c), (a), ARRAY_SIZE(a))
+
+static void r61517_reset(struct r61517 *panel)
+{
+ gpio_set_value(panel->pdata->protect, 0); /* PROTECT/ -> L */
+ gpio_set_value(panel->pdata->reset, 0); /* LCD_RST/ -> L */
+ gpio_set_value(panel->pdata->protect, 1); /* PROTECT/ -> H */
+ usleep_range(1100, 1200);
+ gpio_set_value(panel->pdata->reset, 1); /* LCD_RST/ -> H */
+ usleep_range(10, 100);
+ gpio_set_value(panel->pdata->protect, 0); /* PROTECT/ -> L */
+ msleep(20);
+}
+
+/* -----------------------------------------------------------------------------
+ * Configuration
+ */
+
+static const u8 data_frame_if[] = {
+ 0x02, /* WEMODE: 1=cont, 0=one-shot */
+ 0x00, 0x00,
+ 0x00, /* EPF, DFM */
+ 0x02, /* RIM[1] : 1 (18bpp) */
+};
+
+static const u8 data_panel[] = {
+ 0x0b,
+ 0x63, /* 400 lines */
+ 0x04, 0x00, 0x00, 0x04, 0x11, 0x00, 0x00,
+};
+
+static const u8 data_timing[] = {
+ 0x00, 0x00, 0x13, 0x08, 0x08,
+};
+
+static const u8 data_timing_src[] = {
+ 0x11, 0x01, 0x00, 0x01,
+};
+
+static const u8 data_gamma[] = {
+ 0x01, 0x02, 0x08, 0x23, 0x03, 0x0c, 0x00, 0x06, 0x00, 0x00,
+ 0x01, 0x00, 0x0c, 0x23, 0x03, 0x08, 0x02, 0x06, 0x00, 0x00,
+};
+
+static const u8 data_power[] = {
+ 0x07, 0xc5, 0xdc, 0x02, 0x33, 0x0a,
+};
+
+static const u8 data_vcom[] = {
+ 0x00, 0x0f, 0x02,
+};
+
+static unsigned long r61517_read_device_code(struct r61517 *panel)
+{
+ /* access protect OFF */
+ r61517_write8(panel, 0xb0, 0);
+
+ /* deep standby OFF */
+ r61517_write8(panel, 0xb1, 0);
+
+ /* device code command */
+ r61517_write_command(panel, 0xbf);
+ mdelay(50);
+
+ /* dummy read */
+ r61517_read_data(panel);
+
+ /* read device code */
+ return (r61517_read_data(panel) << 24) |
+ (r61517_read_data(panel) << 16) |
+ (r61517_read_data(panel) << 8) |
+ (r61517_read_data(panel) << 0);
+}
+
+static void r61517_write_memory_start(struct r61517 *panel)
+{
+ r61517_write_command(panel, MIPI_DCS_WRITE_MEMORY_START);
+}
+
+static void r61517_clear_memory(struct r61517 *panel)
+{
+ unsigned int size = panel->pdata->mode->hactive
+ * panel->pdata->mode->vactive;
+ unsigned int i;
+
+ r61517_write_memory_start(panel);
+
+ for (i = 0; i < size; i++)
+ r61517_write_data(panel, 0);
+}
+
+static void r61517_enable_panel(struct r61517 *panel)
+{
+ /* access protect off */
+ r61517_write8(panel, 0xb0, 0);
+
+ /* exit deep standby mode */
+ r61517_write8(panel, 0xb1, 0);
+
+ /* frame memory I/F */
+ r61517_write_array(panel, 0xb3, data_frame_if);
+
+ /* display mode and frame memory write mode */
+ r61517_write8(panel, 0xb4, 0); /* DBI, internal clock */
+
+ /* panel */
+ r61517_write_array(panel, 0xc0, data_panel);
+
+ /* timing (normal) */
+ r61517_write_array(panel, 0xc1, data_timing);
+
+ /* timing (partial) */
+ r61517_write_array(panel, 0xc2, data_timing);
+
+ /* timing (idle) */
+ r61517_write_array(panel, 0xc3, data_timing);
+
+ /* timing (source/VCOM/gate driving) */
+ r61517_write_array(panel, 0xc4, data_timing_src);
+
+ /* gamma (red) */
+ r61517_write_array(panel, 0xc8, data_gamma);
+
+ /* gamma (green) */
+ r61517_write_array(panel, 0xc9, data_gamma);
+
+ /* gamma (blue) */
+ r61517_write_array(panel, 0xca, data_gamma);
+
+ /* power (common) */
+ r61517_write_array(panel, 0xd0, data_power);
+
+ /* VCOM */
+ r61517_write_array(panel, 0xd1, data_vcom);
+
+ /* power (normal) */
+ r61517_write16(panel, 0xd2, 0x6324);
+
+ /* power (partial) */
+ r61517_write16(panel, 0xd3, 0x6324);
+
+ /* power (idle) */
+ r61517_write16(panel, 0xd4, 0x6324);
+
+ r61517_write16(panel, 0xd8, 0x7777);
+
+ /* TE signal */
+ r61517_write8(panel, MIPI_DCS_SET_TEAR_ON, 0);
+
+ /* TE signal line */
+ r61517_write16(panel, MIPI_DCS_SET_TEAR_SCANLINE, 0);
+
+ /* column address */
+ r61517_write32(panel, MIPI_DCS_SET_COLUMN_ADDRESS,
+ panel->pdata->mode->hactive - 1);
+
+ /* page address */
+ r61517_write32(panel, MIPI_DCS_SET_PAGE_ADDRESS,
+ panel->pdata->mode->vactive - 1);
+
+ /* exit sleep mode */
+ r61517_write_command(panel, MIPI_DCS_EXIT_SLEEP_MODE);
+
+ mdelay(120);
+
+ /* clear vram */
+ r61517_clear_memory(panel);
+}
+
+static void r61517_disable_panel(struct r61517 *panel)
+{
+ r61517_reset(panel);
+}
+
+static void r61517_display_on(struct r61517 *panel)
+{
+ r61517_write_command(panel, MIPI_DCS_SET_DISPLAY_ON);
+ mdelay(1);
+}
+
+static void r61517_display_off(struct r61517 *panel)
+{
+ r61517_write_command(panel, MIPI_DCS_SET_DISPLAY_OFF);
+}
+
+/* -----------------------------------------------------------------------------
+ * Panel operations
+ */
+
+static const struct display_entity_interface_params r61517_dbi_params = {
+ .type = DISPLAY_ENTITY_INTERFACE_DBI,
+ .p.dbi = {
+ .type = MIPI_DBI_INTERFACE_TYPE_B,
+ .flags = MIPI_DBI_INTERFACE_TE,
+ .cs_setup = 1,
+ .wr_setup = 1,
+ .wr_cycle = 9,
+ .wr_hold = 4,
+ .rd_setup = 1,
+ .rd_latch = 20,
+ .rd_cycle = 41,
+ .rd_hold = 20,
+ },
+};
+
+static int r61517_set_state(struct display_entity *entity,
+ enum display_entity_state state)
+{
+ struct r61517 *panel = to_panel(entity);
+
+ switch (state) {
+ case DISPLAY_ENTITY_STATE_OFF:
+ r61517_disable_panel(panel);
+ break;
+
+ case DISPLAY_ENTITY_STATE_STANDBY:
+ if (entity->state = DISPLAY_ENTITY_STATE_OFF)
+ r61517_enable_panel(panel);
+ else
+ r61517_display_off(panel);
+ break;
+
+ case DISPLAY_ENTITY_STATE_ON:
+ if (entity->state = DISPLAY_ENTITY_STATE_OFF)
+ r61517_enable_panel(panel);
+
+ r61517_display_on(panel);
+ break;
+ }
+
+ return 0;
+}
+
+static int r61517_update(struct display_entity *entity)
+{
+ struct r61517 *panel = to_panel(entity);
+ struct media_pad *source;
+
+ r61517_write_memory_start(panel);
+
+ source = media_entity_remote_pad(&entity->entity.pads[0]);
+ if (source = NULL)
+ return -EPIPE;
+
+ display_entity_set_stream(to_display_entity(source->entity),
+ source->index,
+ DISPLAY_ENTITY_STREAM_SINGLE_SHOT);
+ return 0;
+}
+
+static int r61517_get_modes(struct display_entity *entity, unsigned int port,
+ const struct videomode **modes)
+{
+ struct r61517 *panel = to_panel(entity);
+
+ *modes = panel->pdata->mode;
+ return 1;
+}
+
+static int r61517_get_size(struct display_entity *entity,
+ unsigned int *width, unsigned int *height)
+{
+ struct r61517 *panel = to_panel(entity);
+
+ *width = panel->pdata->width;
+ *height = panel->pdata->height;
+ return 0;
+}
+
+static int r61517_get_params(struct display_entity *entity, unsigned int port,
+ struct display_entity_interface_params *params)
+{
+ *params = r61517_dbi_params;
+ return 0;
+}
+
+static const struct display_entity_control_ops r61517_control_ops = {
+ .set_state = r61517_set_state,
+ .update = r61517_update,
+ .get_modes = r61517_get_modes,
+ .get_size = r61517_get_size,
+ .get_params = r61517_get_params,
+};
+
+static const struct display_entity_ops r61517_ops = {
+ .ctrl = &r61517_control_ops,
+};
+
+static int r61517_remove(struct mipi_dbi_device *dev)
+{
+ struct r61517 *panel = mipi_dbi_get_drvdata(dev);
+
+ display_entity_remove(&panel->entity);
+ display_entity_cleanup(&panel->entity);
+
+ return 0;
+}
+
+static int r61517_probe(struct mipi_dbi_device *dev)
+{
+ const struct panel_r61517_platform_data *pdata = dev->dev.platform_data;
+ struct r61517 *panel;
+ int ret;
+
+ if (pdata = NULL)
+ return -ENODEV;
+
+ panel = devm_kzalloc(&dev->dev, sizeof(*panel), GFP_KERNEL);
+ if (panel = NULL)
+ return -ENOMEM;
+
+ panel->pdata = pdata;
+ panel->dbi = dev;
+
+ dev->bus_width = pdata->bus_width;
+ mipi_dbi_set_data_width(dev, 8);
+
+ r61517_reset(panel);
+
+ if (r61517_read_device_code(panel) != 0x01221517)
+ return -ENODEV;
+
+ pr_info("R61517 panel controller detected.\n");
+
+ panel->entity.dev = &dev->dev;
+ panel->entity.ops = &r61517_ops;
+
+ ret = display_entity_init(&panel->entity, 1, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = display_entity_add(&panel->entity);
+ if (ret < 0)
+ return ret;
+
+ mipi_dbi_set_drvdata(dev, panel);
+
+ return 0;
+}
+
+static const struct dev_pm_ops r61517_dev_pm_ops = {
+};
+
+static struct mipi_dbi_device_id r61517_id_table[] = {
+ { "panel-r61517", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(mipi_dbi, r61517_id_table);
+
+static struct mipi_dbi_driver r61517_driver = {
+ .probe = r61517_probe,
+ .remove = r61517_remove,
+ .id_table = r61517_id_table,
+ .driver = {
+ .name = "panel-r61517",
+ .owner = THIS_MODULE,
+ .pm = &r61517_dev_pm_ops,
+ },
+};
+
+module_mipi_dbi_driver(r61517_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas R61517-based Display Panel");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-r61517.h b/include/video/panel-r61517.h
new file mode 100644
index 0000000..33f16af
--- /dev/null
+++ b/include/video/panel-r61517.h
@@ -0,0 +1,28 @@
+/*
+ * Renesas R61517-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#ifndef __PANEL_R61517_H__
+#define __PANEL_R61517_H__
+
+struct videomode;
+
+struct panel_r61517_platform_data {
+ unsigned long width; /* Panel width in mm */
+ unsigned long height; /* Panel height in mm */
+ const struct videomode *mode;
+
+ unsigned int bus_width;
+ int protect; /* Protect GPIO */
+ int reset; /* Reset GPIO */
+};
+
+#endif /* __PANEL_R61517_H__ */
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 10/19] video: panel: Add R61505 panel support
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
The R61505 is a SYS-80 bus panel controller from Renesas.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/display/Kconfig | 10 +
drivers/video/display/Makefile | 1 +
drivers/video/display/panel-r61505.c | 567 +++++++++++++++++++++++++++++++++++
include/video/panel-r61505.h | 27 ++
4 files changed, 605 insertions(+)
create mode 100644 drivers/video/display/panel-r61505.c
create mode 100644 include/video/panel-r61505.h
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index bce09d6..76729ef 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -19,4 +19,14 @@ config DISPLAY_PANEL_DPI
If you are in doubt, say N. To compile this driver as a module, choose
M here; the module will be called panel-dpi.
+config DISPLAY_PANEL_R61505
+ tristate "Renesas R61505-based Display Panel"
+ select DISPLAY_MIPI_DBI
+ ---help---
+ Support panels based on the Renesas R61505 panel controller.
+ Those panels are controlled through a MIPI DBI interface.
+
+ If you are in doubt, say N. To compile this driver as a module, choose
+ M here; the module will be called panel-r61505.
+
endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 31c017b..db8a4c3 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -3,3 +3,4 @@ display-y := display-core.o \
obj-$(CONFIG_DISPLAY_CORE) += display.o
obj-$(CONFIG_DISPLAY_MIPI_DBI) += mipi-dbi-bus.o
obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o
+obj-$(CONFIG_DISPLAY_PANEL_R61505) += panel-r61505.o
diff --git a/drivers/video/display/panel-r61505.c b/drivers/video/display/panel-r61505.c
new file mode 100644
index 0000000..c86177e
--- /dev/null
+++ b/drivers/video/display/panel-r61505.c
@@ -0,0 +1,567 @@
+/*
+ * Renesas R61505-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ * Based on SuperH MigoR Quarter VGA LCD Panel
+ * Copyright (C) 2008 Magnus Damm
+ * Based on lcd_powertip.c from Kenati Technologies Pvt Ltd.
+ * Copyright (c) 2007 Ujjwal Pande
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <video/display.h>
+#include <video/mipi-dbi-bus.h>
+#include <video/panel-r61505.h>
+#include <video/videomode.h>
+
+#define R61505_DEVICE_CODE 0x0000
+#define R61505_DEVICE_CODE_VALUE 0x1505
+#define R61505_DRIVER_OUTPUT_CONTROL 0x0001
+#define R61505_DRIVER_OUTPUT_CONTROL_SM (1 << 10)
+#define R61505_DRIVER_OUTPUT_CONTROL_SS (1 << 8)
+#define R61505_LCD_WAVEFORM 0x0002
+#define R61505_LCD_WAVEFORM_BC0 (1 << 9)
+#define R61505_LCD_WAVEFORM_EOR (1 << 8)
+#define R61505_ENTRY_MODE 0x0003
+#define R61505_ENTRY_MODE_TRIREG (1 << 15)
+#define R61505_ENTRY_MODE_DFM (1 << 14)
+#define R61505_ENTRY_MODE_BGR (1 << 12)
+#define R61505_ENTRY_MODE_HWM (1 << 9)
+#define R61505_ENTRY_MODE_ORG (1 << 7)
+#define R61505_ENTRY_MODE_ID1 (1 << 5)
+#define R61505_ENTRY_MODE_ID0 (1 << 4)
+#define R61505_ENTRY_MODE_AM (1 << 3)
+#define R61505_RESIZE_CONTROL 0x0004
+#define R61505_RESIZE_CONTROL_RCV(n) (((n) & 3) << 8)
+#define R61505_RESIZE_CONTROL_RCH(n) (((n) & 3) << 4)
+#define R61505_RESIZE_CONTROL_RSZ_4 (3 << 0)
+#define R61505_RESIZE_CONTROL_RSZ_2 (1 << 0)
+#define R61505_RESIZE_CONTROL_RSZ_1 (0 << 0)
+#define R61505_DISPLAY_CONTROL1 0x0007
+#define R61505_DISPLAY_CONTROL1_PTDE1 (1 << 13)
+#define R61505_DISPLAY_CONTROL1_PTDE0 (1 << 12)
+#define R61505_DISPLAY_CONTROL1_BASEE (1 << 8)
+#define R61505_DISPLAY_CONTROL1_VON (1 << 6)
+#define R61505_DISPLAY_CONTROL1_GON (1 << 5)
+#define R61505_DISPLAY_CONTROL1_DTE (1 << 4)
+#define R61505_DISPLAY_CONTROL1_COL (1 << 3)
+#define R61505_DISPLAY_CONTROL1_D1 (1 << 1)
+#define R61505_DISPLAY_CONTROL1_D0 (1 << 0)
+#define R61505_DISPLAY_CONTROL2 0x0008
+#define R61505_DISPLAY_CONTROL2_FP(n) (((n) & 0xf) << 8)
+#define R61505_DISPLAY_CONTROL2_BP(n) (((n) & 0xf) << 0)
+#define R61505_DISPLAY_CONTROL3 0x0009
+#define R61505_DISPLAY_CONTROL3_PTS(n) (((n) & 7) << 8)
+#define R61505_DISPLAY_CONTROL3_PTG(n) (((n) & 3) << 3)
+#define R61505_DISPLAY_CONTROL3_ICS(n) (((n) & 0xf) << 0)
+#define R61505_DISPLAY_CONTROL4 0x000a
+#define R61505_DISPLAY_CONTROL4_FMARKOE (1 << 3)
+#define R61505_DISPLAY_CONTROL4_FMI_6 (5 << 0)
+#define R61505_DISPLAY_CONTROL4_FMI_4 (3 << 0)
+#define R61505_DISPLAY_CONTROL4_FMI_2 (1 << 0)
+#define R61505_DISPLAY_CONTROL4_FMI_1 (0 << 0)
+#define R61505_EXT_DISPLAY_IF_CONTROL1 0x000c
+#define R61505_EXT_DISPLAY_IF_CONTROL1_ENC(n) (((n) & 7) << 12)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_RM (1 << 8)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_DM_VSYNC (2 << 4)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_DM_RGB (1 << 4)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_DM_ICLK (0 << 4)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_RIM_6 (2 << 0)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_RIM_16 (1 << 0)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_RIM_18 (0 << 0)
+#define R61505_FRAME_MARKER_CONTROL 0x000d
+#define R61505_FRAME_MARKER_CONTROL_FMP(n) (((n) & 0x1ff) << 0)
+#define R61505_EXT_DISPLAY_IF_CONTROL2 0x000f
+#define R61505_POWER_CONTROL1 0x0010
+#define R61505_POWER_CONTROL1_SAP (1 << 12)
+#define R61505_POWER_CONTROL1_BT(n) (((n) & 0xf) << 8)
+#define R61505_POWER_CONTROL1_APE (1 << 7)
+#define R61505_POWER_CONTROL1_AP_100 (3 << 4)
+#define R61505_POWER_CONTROL1_AP_075 (2 << 4)
+#define R61505_POWER_CONTROL1_AP_050 (1 << 4)
+#define R61505_POWER_CONTROL1_AP_HALT (0 << 4)
+#define R61505_POWER_CONTROL1_DSTB (1 << 2)
+#define R61505_POWER_CONTROL1_SLP (1 << 1)
+#define R61505_POWER_CONTROL2 0x0011
+#define R61505_POWER_CONTROL2_DC1_HALT (6 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_256 (4 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_128 (3 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_64 (2 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_32 (1 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_16 (0 << 8)
+#define R61505_POWER_CONTROL2_DC0_HALT (6 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC_16 (4 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC_8 (3 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC_4 (2 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC_2 (1 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC (0 << 4)
+#define R61505_POWER_CONTROL2_VC_100 (7 << 0)
+#define R61505_POWER_CONTROL2_VC_076 (4 << 0)
+#define R61505_POWER_CONTROL2_VC_089 (1 << 0)
+#define R61505_POWER_CONTROL2_VC_094 (0 << 0)
+#define R61505_POWER_CONTROL3 0x0012
+#define R61505_POWER_CONTROL3_VCMR (1 << 8)
+#define R61505_POWER_CONTROL3_PSON (1 << 5)
+#define R61505_POWER_CONTROL3_PON (1 << 4)
+#define R61505_POWER_CONTROL3_VRH(n) (((n) & 0xf) << 0)
+#define R61505_POWER_CONTROL4 0x0013
+#define R61505_POWER_CONTROL4_VDV(n) (((n) & 0xf) << 8)
+#define R61505_POWER_CONTROL5 0x0015
+#define R61505_POWER_CONTROL5_BLDM (1 << 12)
+#define R61505_POWER_CONTROL6 0x0017
+#define R61505_POWER_CONTROL6_PSE (1 << 0)
+#define R61505_RAM_ADDR_HORZ 0x0020
+#define R61505_RAM_ADDR_VERT 0x0021
+#define R61505_RAM_DATA 0x0022
+#define R61505_POWER_CONTROL7 0x0029
+#define R61505_POWER_CONTROL7_VCM1(n) (((n) & 0x1f) << 0)
+#define R61505_GAMMA_CONTROL1 0x0030
+#define R61505_GAMMA_CONTROL2 0x0031
+#define R61505_GAMMA_CONTROL3 0x0032
+#define R61505_GAMMA_CONTROL4 0x0033
+#define R61505_GAMMA_CONTROL5 0x0034
+#define R61505_GAMMA_CONTROL6 0x0035
+#define R61505_GAMMA_CONTROL7 0x0036
+#define R61505_GAMMA_CONTROL8 0x0037
+#define R61505_GAMMA_CONTROL9 0x0038
+#define R61505_GAMMA_CONTROL10 0x0039
+#define R61505_GAMMA_CONTROL11 0x003a
+#define R61505_GAMMA_CONTROL12 0x003b
+#define R61505_GAMMA_CONTROL13 0x003c
+#define R61505_GAMMA_CONTROL14 0x003d
+#define R61505_WINDOW_HORZ_START 0x0050
+#define R61505_WINDOW_HORZ_END 0x0051
+#define R61505_WINDOW_VERT_START 0x0052
+#define R61505_WINDOW_VERT_END 0x0053
+#define R61505_DRIVER_OUTPUT_CONTROL2 0x0060
+#define R61505_DRIVER_OUTPUT_CONTROL2_GS (1 << 15)
+#define R61505_DRIVER_OUTPUT_CONTROL2_NL(n) (((n) & 0x3f) << 8)
+#define R61505_DRIVER_OUTPUT_CONTROL2_SCN(n) (((n) & 0x3f) << 0)
+#define R61505_BASE_IMG_DISPLAY_CONTROL 0x0061
+#define R61505_BASE_IMG_DISPLAY_CONTROL_NDL (1 << 2)
+#define R61505_BASE_IMG_DISPLAY_CONTROL_VLE (1 << 1)
+#define R61505_BASE_IMG_DISPLAY_CONTROL_REV (1 << 0)
+#define R61505_VERTICAL_SCROLL_CONTROL 0x006a
+#define R61505_PANEL_IF_CONTROL1 0x0090
+#define R61505_PANEL_IF_CONTROL1_DIVI(n) (((n) & 3) << 8)
+#define R61505_PANEL_IF_CONTROL1_RTNI(n) (((n) & 0x1f) << 0)
+#define R61505_PANEL_IF_CONTROL2 0x0092
+#define R61505_PANEL_IF_CONTROL2_NOWI(n) (((n) & 7) << 8)
+#define R61505_PANEL_IF_CONTROL3 0x0093
+#define R61505_PANEL_IF_CONTROL3_MCP(n) (((n) & 7) << 8)
+#define R61505_PANEL_IF_CONTROL4 0x0095
+#define R61505_PANEL_IF_CONTROL5 0x0097
+#define R61505_PANEL_IF_CONTROL6 0x0098
+#define R61505_OSCILLATION_CONTROL 0x00a4
+#define R61505_OSCILLATION_CONTROL_CALB (1 << 0)
+
+struct r61505 {
+ struct display_entity entity;
+ struct mipi_dbi_device *dbi;
+ const struct panel_r61505_platform_data *pdata;
+};
+
+static inline struct r61505 *to_panel(struct display_entity *e)
+{
+ return container_of(e, struct r61505, entity);
+}
+
+/* -----------------------------------------------------------------------------
+ * Read, write and reset
+ */
+
+static void r61505_write(struct r61505 *panel, u16 reg, u16 data)
+{
+ u8 buffer[2] = { data >> 8, data & 0xff };
+
+ mipi_dbi_write_command(panel->dbi, reg);
+ mipi_dbi_write_data(panel->dbi, buffer, 2);
+}
+
+static u16 r61505_read(struct r61505 *panel, u16 reg)
+{
+ u8 buffer[2];
+ int ret;
+
+ mipi_dbi_write_command(panel->dbi, reg);
+ ret = mipi_dbi_read_data(panel->dbi, buffer, 2);
+ if (ret < 0)
+ return ret;
+
+ return (buffer[0] << 8) | buffer[1];
+}
+
+static void r61505_write_array(struct r61505 *panel,
+ const u16 *data, unsigned int len)
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i += 2)
+ r61505_write(panel, data[i], data[i + 1]);
+}
+
+static void r61505_reset(struct r61505 *panel)
+{
+ if (panel->pdata->reset < 0)
+ return;
+
+ gpio_set_value(panel->pdata->reset, 0);
+ usleep_range(2000, 2500);
+ gpio_set_value(panel->pdata->reset, 1);
+ usleep_range(1000, 1500);
+}
+
+/* -----------------------------------------------------------------------------
+ * Configuration
+ */
+
+static const unsigned short sync_data[] = {
+ 0x0000, 0x0000,
+ 0x0000, 0x0000,
+ 0x0000, 0x0000,
+ 0x0000, 0x0000,
+};
+
+static const unsigned short magic0_data[] = {
+ R61505_DISPLAY_CONTROL2, R61505_DISPLAY_CONTROL2_FP(8) |
+ R61505_DISPLAY_CONTROL2_BP(8),
+ R61505_PANEL_IF_CONTROL1, R61505_PANEL_IF_CONTROL1_RTNI(26),
+ R61505_DISPLAY_CONTROL1, R61505_DISPLAY_CONTROL1_D0,
+ R61505_POWER_CONTROL6, R61505_POWER_CONTROL6_PSE,
+ 0x0019, 0x0000,
+ R61505_POWER_CONTROL1, R61505_POWER_CONTROL1_SAP |
+ R61505_POWER_CONTROL1_BT(7) |
+ R61505_POWER_CONTROL1_APE |
+ R61505_POWER_CONTROL1_AP_100,
+ R61505_POWER_CONTROL2, R61505_POWER_CONTROL2_DC1_FOSC_32 |
+ R61505_POWER_CONTROL2_DC0_FOSC_2 | 6,
+ R61505_POWER_CONTROL3, R61505_POWER_CONTROL3_VCMR | 0x80 |
+ R61505_POWER_CONTROL3_PON |
+ R61505_POWER_CONTROL3_VRH(8),
+ R61505_POWER_CONTROL4, 0x1000 | R61505_POWER_CONTROL4_VDV(4),
+ R61505_POWER_CONTROL7, R61505_POWER_CONTROL7_VCM1(12),
+ R61505_POWER_CONTROL3, R61505_POWER_CONTROL3_VCMR | 0x80 |
+ R61505_POWER_CONTROL3_PSON |
+ R61505_POWER_CONTROL3_PON |
+ R61505_POWER_CONTROL3_VRH(8),
+};
+
+static const unsigned short magic1_data[] = {
+ R61505_GAMMA_CONTROL1, 0x0307,
+ R61505_GAMMA_CONTROL2, 0x0303,
+ R61505_GAMMA_CONTROL3, 0x0603,
+ R61505_GAMMA_CONTROL4, 0x0202,
+ R61505_GAMMA_CONTROL5, 0x0202,
+ R61505_GAMMA_CONTROL6, 0x0202,
+ R61505_GAMMA_CONTROL7, 0x1f1f,
+ R61505_GAMMA_CONTROL8, 0x0303,
+ R61505_GAMMA_CONTROL9, 0x0303,
+ R61505_GAMMA_CONTROL10, 0x0603,
+ R61505_GAMMA_CONTROL11, 0x0202,
+ R61505_GAMMA_CONTROL12, 0x0102,
+ R61505_GAMMA_CONTROL13, 0x0204,
+ R61505_GAMMA_CONTROL14, 0x0000,
+ R61505_DRIVER_OUTPUT_CONTROL, R61505_DRIVER_OUTPUT_CONTROL_SS,
+ R61505_LCD_WAVEFORM, R61505_LCD_WAVEFORM_BC0 |
+ R61505_LCD_WAVEFORM_EOR,
+ R61505_ENTRY_MODE, R61505_ENTRY_MODE_DFM |
+ R61505_ENTRY_MODE_BGR |
+ R61505_ENTRY_MODE_ID1 |
+ R61505_ENTRY_MODE_AM,
+ R61505_RAM_ADDR_HORZ, 239,
+ R61505_RAM_ADDR_VERT, 0,
+ R61505_RESIZE_CONTROL, R61505_RESIZE_CONTROL_RCV(0) |
+ R61505_RESIZE_CONTROL_RCH(0) |
+ R61505_RESIZE_CONTROL_RSZ_1,
+ R61505_DISPLAY_CONTROL3, R61505_DISPLAY_CONTROL3_PTS(0) |
+ R61505_DISPLAY_CONTROL3_PTG(0) |
+ R61505_DISPLAY_CONTROL3_ICS(0),
+ R61505_DISPLAY_CONTROL4, R61505_DISPLAY_CONTROL4_FMARKOE |
+ R61505_DISPLAY_CONTROL4_FMI_1,
+ R61505_EXT_DISPLAY_IF_CONTROL1, R61505_EXT_DISPLAY_IF_CONTROL1_ENC(0) |
+ R61505_EXT_DISPLAY_IF_CONTROL1_DM_ICLK |
+ R61505_EXT_DISPLAY_IF_CONTROL1_RIM_18,
+ R61505_FRAME_MARKER_CONTROL, R61505_FRAME_MARKER_CONTROL_FMP(0),
+ R61505_POWER_CONTROL5, 0x8000,
+};
+
+static const unsigned short magic2_data[] = {
+ R61505_BASE_IMG_DISPLAY_CONTROL, R61505_BASE_IMG_DISPLAY_CONTROL_REV,
+ R61505_PANEL_IF_CONTROL2, R61505_PANEL_IF_CONTROL2_NOWI(1),
+ R61505_PANEL_IF_CONTROL3, R61505_PANEL_IF_CONTROL3_MCP(1),
+ R61505_DISPLAY_CONTROL1, R61505_DISPLAY_CONTROL1_GON |
+ R61505_DISPLAY_CONTROL1_D0,
+};
+
+static const unsigned short magic3_data[] = {
+ R61505_POWER_CONTROL1, R61505_POWER_CONTROL1_SAP |
+ R61505_POWER_CONTROL1_BT(6) |
+ R61505_POWER_CONTROL1_APE |
+ R61505_POWER_CONTROL1_AP_100,
+ R61505_POWER_CONTROL2, R61505_POWER_CONTROL2_DC1_FOSC_32 |
+ R61505_POWER_CONTROL2_DC0_FOSC_2 |
+ R61505_POWER_CONTROL2_VC_089,
+ R61505_DISPLAY_CONTROL1, R61505_DISPLAY_CONTROL1_VON |
+ R61505_DISPLAY_CONTROL1_GON |
+ R61505_DISPLAY_CONTROL1_D0,
+};
+
+static void r61505_enable_panel(struct r61505 *panel)
+{
+ unsigned long hactive = panel->pdata->mode->hactive;
+ unsigned long vactive = panel->pdata->mode->vactive;
+ unsigned int i;
+
+ r61505_write_array(panel, sync_data, ARRAY_SIZE(sync_data));
+
+ r61505_write(panel, R61505_OSCILLATION_CONTROL,
+ R61505_OSCILLATION_CONTROL_CALB);
+ usleep_range(10000, 11000);
+
+ r61505_write(panel, R61505_DRIVER_OUTPUT_CONTROL2,
+ R61505_DRIVER_OUTPUT_CONTROL2_NL((hactive / 8) - 1));
+ r61505_write_array(panel, magic0_data, ARRAY_SIZE(magic0_data));
+ usleep_range(100000, 101000);
+
+ r61505_write_array(panel, magic1_data, ARRAY_SIZE(magic1_data));
+
+ r61505_write(panel, R61505_WINDOW_HORZ_START, 239 - (vactive - 1));
+ r61505_write(panel, R61505_WINDOW_HORZ_END, 239);
+ r61505_write(panel, R61505_WINDOW_VERT_START, 0);
+ r61505_write(panel, R61505_WINDOW_VERT_END, hactive - 1);
+
+ r61505_write_array(panel, magic2_data, ARRAY_SIZE(magic2_data));
+ usleep_range(10000, 11000);
+
+ r61505_write_array(panel, magic3_data, ARRAY_SIZE(magic3_data));
+ usleep_range(40000, 41000);
+
+ /* Clear GRAM to avoid displaying garbage. */
+ r61505_write(panel, R61505_RAM_ADDR_HORZ, 0);
+ r61505_write(panel, R61505_RAM_ADDR_VERT, 0);
+
+ for (i = 0; i < (hactive * 256); i++) /* yes, 256 words per line */
+ r61505_write(panel, R61505_RAM_DATA, 0);
+
+ r61505_write(panel, R61505_RAM_ADDR_HORZ, 0);
+ r61505_write(panel, R61505_RAM_ADDR_VERT, 0);
+}
+
+static void r61505_disable_panel(struct r61505 *panel)
+{
+ r61505_reset(panel);
+}
+
+static void r61505_display_on(struct r61505 *panel)
+{
+ r61505_write(panel, R61505_DISPLAY_CONTROL1,
+ R61505_DISPLAY_CONTROL1_BASEE |
+ R61505_DISPLAY_CONTROL1_VON |
+ R61505_DISPLAY_CONTROL1_GON |
+ R61505_DISPLAY_CONTROL1_DTE |
+ R61505_DISPLAY_CONTROL1_D1 |
+ R61505_DISPLAY_CONTROL1_D0);
+ usleep_range(40000, 41000);
+}
+
+static void r61505_display_off(struct r61505 *panel)
+{
+ r61505_write(panel, R61505_DISPLAY_CONTROL1,
+ R61505_DISPLAY_CONTROL1_VON |
+ R61505_DISPLAY_CONTROL1_GON |
+ R61505_DISPLAY_CONTROL1_D0);
+}
+
+/* -----------------------------------------------------------------------------
+ * Panel operations
+ */
+
+static const struct display_entity_interface_params r61505_dbi_params = {
+ .type = DISPLAY_ENTITY_INTERFACE_DBI,
+ .p.dbi = {
+ .type = MIPI_DBI_INTERFACE_TYPE_B,
+ .cs_setup = 1,
+ .wr_setup = 0,
+ .wr_cycle = 10,
+ .wr_hold = 9,
+ .rd_setup = 14,
+ .rd_latch = 24,
+ .rd_cycle = 52,
+ .rd_hold = 24,
+ },
+};
+
+static int r61505_set_state(struct display_entity *entity,
+ enum display_entity_state state)
+{
+ struct r61505 *panel = to_panel(entity);
+
+ switch (state) {
+ case DISPLAY_ENTITY_STATE_OFF:
+ r61505_disable_panel(panel);
+ break;
+
+ case DISPLAY_ENTITY_STATE_STANDBY:
+ if (entity->state = DISPLAY_ENTITY_STATE_OFF)
+ r61505_enable_panel(panel);
+ else
+ r61505_display_off(panel);
+ break;
+
+ case DISPLAY_ENTITY_STATE_ON:
+ if (entity->state = DISPLAY_ENTITY_STATE_OFF)
+ r61505_enable_panel(panel);
+
+ r61505_display_on(panel);
+ break;
+ }
+
+ return 0;
+}
+
+static int r61505_update(struct display_entity *entity)
+{
+ struct r61505 *panel = to_panel(entity);
+ struct media_pad *source;
+
+ mipi_dbi_write_command(panel->dbi, R61505_RAM_DATA);
+ usleep_range(100000, 101000);
+
+ source = media_entity_remote_pad(&entity->entity.pads[0]);
+ if (source = NULL)
+ return -EPIPE;
+
+ display_entity_set_stream(to_display_entity(source->entity),
+ source->index,
+ DISPLAY_ENTITY_STREAM_SINGLE_SHOT);
+ return 0;
+}
+
+static int r61505_get_modes(struct display_entity *entity, unsigned int port,
+ const struct videomode **modes)
+{
+ struct r61505 *panel = to_panel(entity);
+
+ *modes = panel->pdata->mode;
+ return 1;
+}
+
+static int r61505_get_size(struct display_entity *entity,
+ unsigned int *width, unsigned int *height)
+{
+ struct r61505 *panel = to_panel(entity);
+
+ *width = panel->pdata->width;
+ *height = panel->pdata->height;
+ return 0;
+}
+
+static int r61505_get_params(struct display_entity *entity, unsigned int port,
+ struct display_entity_interface_params *params)
+{
+ *params = r61505_dbi_params;
+ return 0;
+}
+
+static const struct display_entity_control_ops r61505_control_ops = {
+ .set_state = r61505_set_state,
+ .update = r61505_update,
+ .get_modes = r61505_get_modes,
+ .get_size = r61505_get_size,
+ .get_params = r61505_get_params,
+};
+
+static const struct display_entity_ops r61505_ops = {
+ .ctrl = &r61505_control_ops,
+};
+
+static int r61505_remove(struct mipi_dbi_device *dev)
+{
+ struct r61505 *panel = mipi_dbi_get_drvdata(dev);
+
+ display_entity_remove(&panel->entity);
+ display_entity_cleanup(&panel->entity);
+
+ return 0;
+}
+
+static int r61505_probe(struct mipi_dbi_device *dev)
+{
+ const struct panel_r61505_platform_data *pdata = dev->dev.platform_data;
+ struct r61505 *panel;
+ int ret;
+
+ if (pdata = NULL)
+ return -ENODEV;
+
+ panel = devm_kzalloc(&dev->dev, sizeof(*panel), GFP_KERNEL);
+ if (panel = NULL)
+ return -ENOMEM;
+
+ panel->pdata = pdata;
+ panel->dbi = dev;
+
+ dev->flags = MIPI_DBI_FLAG_ALIGN_LEFT;
+ dev->bus_width = pdata->bus_width;
+ mipi_dbi_set_data_width(dev, 16);
+
+ r61505_reset(panel);
+ r61505_write_array(panel, sync_data, ARRAY_SIZE(sync_data));
+
+ if (r61505_read(panel, 0) != R61505_DEVICE_CODE_VALUE)
+ return -ENODEV;
+
+ panel->entity.dev = &dev->dev;
+ panel->entity.ops = &r61505_ops;
+
+ ret = display_entity_init(&panel->entity, 1, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = display_entity_add(&panel->entity);
+ if (ret < 0)
+ return ret;
+
+ mipi_dbi_set_drvdata(dev, panel);
+
+ return 0;
+}
+
+static const struct dev_pm_ops r61505_dev_pm_ops = {
+};
+
+static struct mipi_dbi_device_id r61505_id_table[] = {
+ { "panel-r61505", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(mipi_dbi, r61505_id_table);
+
+static struct mipi_dbi_driver r61505_driver = {
+ .probe = r61505_probe,
+ .remove = r61505_remove,
+ .id_table = r61505_id_table,
+ .driver = {
+ .name = "panel-r61505",
+ .owner = THIS_MODULE,
+ .pm = &r61505_dev_pm_ops,
+ },
+};
+
+module_mipi_dbi_driver(r61505_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas R61505-based Display Panel");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-r61505.h b/include/video/panel-r61505.h
new file mode 100644
index 0000000..ee71f29
--- /dev/null
+++ b/include/video/panel-r61505.h
@@ -0,0 +1,27 @@
+/*
+ * Renesas R61505-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#ifndef __PANEL_R61505_H__
+#define __PANEL_R61505_H__
+
+struct videomode;
+
+struct panel_r61505_platform_data {
+ unsigned long width; /* Panel width in mm */
+ unsigned long height; /* Panel height in mm */
+ const struct videomode *mode;
+
+ unsigned int bus_width;
+ int reset; /* Reset GPIO */
+};
+
+#endif /* __PANEL_R61505_H__ */
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 09/19] video: panel: Add DPI panel support
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
The Display Pixel Interface is a configurable-width video-only
unidirectional parallel bus standard that defines video formats and
signaling for panel devices.
This driver implements support for simple DPI panels with no runtime
configuration capabilities (GPIOs- and/or regulators-based control can
be implemented later when needed) and exposes it as a display entity.
The panel native video mode is passed to the driver through platform
data or device tree properties.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/display/Kconfig | 10 ++
drivers/video/display/Makefile | 1 +
drivers/video/display/panel-dpi.c | 207 ++++++++++++++++++++++++++++++++++++++
include/video/panel-dpi.h | 24 +++++
4 files changed, 242 insertions(+)
create mode 100644 drivers/video/display/panel-dpi.c
create mode 100644 include/video/panel-dpi.h
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index f7532c1..bce09d6 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -9,4 +9,14 @@ config DISPLAY_MIPI_DBI
tristate
default n
+config DISPLAY_PANEL_DPI
+ tristate "DPI (Parallel) Display Panels"
+ ---help---
+ Support for simple digital (parallel) pixel interface panels. Those
+ panels receive pixel data through a parallel bus and have no control
+ bus.
+
+ If you are in doubt, say N. To compile this driver as a module, choose
+ M here; the module will be called panel-dpi.
+
endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 59022d2..31c017b 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -2,3 +2,4 @@ display-y := display-core.o \
display-notifier.o
obj-$(CONFIG_DISPLAY_CORE) += display.o
obj-$(CONFIG_DISPLAY_MIPI_DBI) += mipi-dbi-bus.o
+obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o
diff --git a/drivers/video/display/panel-dpi.c b/drivers/video/display/panel-dpi.c
new file mode 100644
index 0000000..b1ecf6d
--- /dev/null
+++ b/drivers/video/display/panel-dpi.c
@@ -0,0 +1,207 @@
+/*
+ * DPI Display Panel
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <video/display.h>
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+#include <video/panel-dpi.h>
+#include <video/videomode.h>
+
+struct panel_dpi {
+ struct display_entity entity;
+
+ unsigned int width;
+ unsigned int height;
+ struct videomode mode;
+};
+
+static inline struct panel_dpi *to_panel_dpi(struct display_entity *e)
+{
+ return container_of(e, struct panel_dpi, entity);
+}
+
+static const struct display_entity_interface_params panel_dpi_params = {
+ .type = DISPLAY_ENTITY_INTERFACE_DPI,
+};
+
+static int panel_dpi_set_state(struct display_entity *entity,
+ enum display_entity_state state)
+{
+ struct media_pad *source;
+
+ source = media_entity_remote_pad(&entity->entity.pads[0]);
+ if (source = NULL)
+ return -EPIPE;
+
+ switch (state) {
+ case DISPLAY_ENTITY_STATE_OFF:
+ case DISPLAY_ENTITY_STATE_STANDBY:
+ display_entity_set_stream(to_display_entity(source->entity),
+ source->index,
+ DISPLAY_ENTITY_STREAM_STOPPED);
+ break;
+
+ case DISPLAY_ENTITY_STATE_ON:
+ display_entity_set_stream(to_display_entity(source->entity),
+ source->index,
+ DISPLAY_ENTITY_STREAM_CONTINUOUS);
+ break;
+ }
+
+ return 0;
+}
+
+static int panel_dpi_get_modes(struct display_entity *entity, unsigned int port,
+ const struct videomode **modes)
+{
+ struct panel_dpi *panel = to_panel_dpi(entity);
+
+ *modes = &panel->mode;
+ return 1;
+}
+
+static int panel_dpi_get_size(struct display_entity *entity,
+ unsigned int *width, unsigned int *height)
+{
+ struct panel_dpi *panel = to_panel_dpi(entity);
+
+ *width = panel->width;
+ *height = panel->height;
+ return 0;
+}
+
+static int panel_dpi_get_params(struct display_entity *entity,
+ unsigned int port,
+ struct display_entity_interface_params *params)
+{
+ *params = panel_dpi_params;
+ return 0;
+}
+
+static const struct display_entity_control_ops panel_dpi_control_ops = {
+ .set_state = panel_dpi_set_state,
+ .get_modes = panel_dpi_get_modes,
+ .get_size = panel_dpi_get_size,
+ .get_params = panel_dpi_get_params,
+};
+
+static const struct display_entity_ops panel_dpi_ops = {
+ .ctrl = &panel_dpi_control_ops,
+};
+
+static int panel_dpi_remove(struct platform_device *pdev)
+{
+ struct panel_dpi *panel = platform_get_drvdata(pdev);
+
+ display_entity_remove(&panel->entity);
+ display_entity_cleanup(&panel->entity);
+
+ return 0;
+}
+
+static int panel_dpi_parse_pdata(struct panel_dpi *panel,
+ struct platform_device *pdev)
+{
+ const struct panel_dpi_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ if (pdata) {
+ panel->width = pdata->width;
+ panel->height = pdata->height;
+ panel->mode = *pdata->mode;
+ } else if (IS_ENABLED(CONFIG_OF) && np) {
+ /* Width and height are optional. */
+ of_property_read_u32(np, "width-mm", &panel->width);
+ of_property_read_u32(np, "height-mm", &panel->height);
+
+ ret = of_get_videomode(np, &panel->mode, OF_USE_NATIVE_MODE);
+ if (ret < 0)
+ return ret;
+ } else {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int panel_dpi_probe(struct platform_device *pdev)
+{
+ struct panel_dpi *panel;
+ int ret;
+
+ panel = devm_kzalloc(&pdev->dev, sizeof(*panel), GFP_KERNEL);
+ if (panel = NULL)
+ return -ENOMEM;
+
+ ret = panel_dpi_parse_pdata(panel, pdev);
+ if (ret < 0)
+ return ret;
+
+ panel->entity.dev = &pdev->dev;
+ panel->entity.ops = &panel_dpi_ops;
+ strlcpy(panel->entity.name, "panel-dpi", sizeof(panel->entity.name));
+
+ ret = display_entity_init(&panel->entity, 1, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = display_entity_add(&panel->entity);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, panel);
+
+ return 0;
+}
+
+static const struct dev_pm_ops panel_dpi_dev_pm_ops = {
+};
+
+static struct platform_device_id panel_dpi_id_table[] = {
+ { "panel-dpi", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, panel_dpi_id_table);
+
+#ifdef CONFIG_OF
+static struct of_device_id panel_dpi_of_id_table[] = {
+ { .compatible = "panel-dpi", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, panel_dpi_of_id_table);
+#endif
+
+static struct platform_driver panel_dpi_driver = {
+ .probe = panel_dpi_probe,
+ .remove = panel_dpi_remove,
+ .id_table = panel_dpi_id_table,
+ .driver = {
+ .name = "panel-dpi",
+ .owner = THIS_MODULE,
+ .pm = &panel_dpi_dev_pm_ops,
+ .of_match_table = of_match_ptr(panel_dpi_of_id_table),
+ },
+};
+
+module_platform_driver(panel_dpi_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("DPI Display Panel");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-dpi.h b/include/video/panel-dpi.h
new file mode 100644
index 0000000..af85d5b
--- /dev/null
+++ b/include/video/panel-dpi.h
@@ -0,0 +1,24 @@
+/*
+ * DPI Display Panel
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#ifndef __PANEL_DPI_H__
+#define __PANEL_DPI_H__
+
+struct videomode;
+
+struct panel_dpi_platform_data {
+ unsigned long width; /* Panel width in mm */
+ unsigned long height; /* Panel height in mm */
+ const struct videomode *mode;
+};
+
+#endif /* __PANEL_DPI_H__ */
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 08/19] video: display: Add MIPI DBI bus support
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
MIPI DBI is a configurable-width parallel display bus that transmits
commands and data.
Add a new DBI Linux bus type that implements the usual bus
infrastructure (including devices and drivers (un)registration and
matching, and bus configuration and access functions).
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/display/Kconfig | 8 ++
drivers/video/display/Makefile | 1 +
drivers/video/display/mipi-dbi-bus.c | 234 +++++++++++++++++++++++++++++++++++
include/video/display.h | 4 +
include/video/mipi-dbi-bus.h | 125 +++++++++++++++++++
5 files changed, 372 insertions(+)
create mode 100644 drivers/video/display/mipi-dbi-bus.c
create mode 100644 include/video/mipi-dbi-bus.h
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 1d533e7..f7532c1 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -2,3 +2,11 @@ menuconfig DISPLAY_CORE
tristate "Display Core"
---help---
Support common display framework for graphics devices.
+
+if DISPLAY_CORE
+
+config DISPLAY_MIPI_DBI
+ tristate
+ default n
+
+endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index b907aad..59022d2 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1,3 +1,4 @@
display-y := display-core.o \
display-notifier.o
obj-$(CONFIG_DISPLAY_CORE) += display.o
+obj-$(CONFIG_DISPLAY_MIPI_DBI) += mipi-dbi-bus.o
diff --git a/drivers/video/display/mipi-dbi-bus.c b/drivers/video/display/mipi-dbi-bus.c
new file mode 100644
index 0000000..791fb4d
--- /dev/null
+++ b/drivers/video/display/mipi-dbi-bus.c
@@ -0,0 +1,234 @@
+/*
+ * MIPI DBI Bus
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+#include <video/mipi-dbi-bus.h>
+
+/* -----------------------------------------------------------------------------
+ * Bus operations
+ */
+
+int mipi_dbi_set_data_width(struct mipi_dbi_device *dev, unsigned int width)
+{
+ if (width != 8 && width != 16)
+ return -EINVAL;
+
+ dev->data_width = width;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_set_data_width);
+
+int mipi_dbi_write_command(struct mipi_dbi_device *dev, u16 cmd)
+{
+ return dev->bus->ops->write_command(dev->bus, dev, cmd);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_write_command);
+
+int mipi_dbi_write_data(struct mipi_dbi_device *dev, const u8 *data,
+ size_t len)
+{
+ return dev->bus->ops->write_data(dev->bus, dev, data, len);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_write_data);
+
+int mipi_dbi_read_data(struct mipi_dbi_device *dev, u8 *data, size_t len)
+{
+ return dev->bus->ops->read_data(dev->bus, dev, data, len);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_read_data);
+
+/* -----------------------------------------------------------------------------
+ * Bus type
+ */
+
+static const struct mipi_dbi_device_id *
+mipi_dbi_match_id(const struct mipi_dbi_device_id *id,
+ struct mipi_dbi_device *dev)
+{
+ while (id->name[0]) {
+ if (strcmp(dev->name, id->name) = 0) {
+ dev->id_entry = id;
+ return id;
+ }
+ id++;
+ }
+ return NULL;
+}
+
+static int mipi_dbi_match(struct device *_dev, struct device_driver *_drv)
+{
+ struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+ struct mipi_dbi_driver *drv = to_mipi_dbi_driver(_drv);
+
+ if (drv->id_table)
+ return mipi_dbi_match_id(drv->id_table, dev) != NULL;
+
+ return (strcmp(dev->name, _drv->name) = 0);
+}
+
+static ssize_t modalias_show(struct device *_dev, struct device_attribute *a,
+ char *buf)
+{
+ struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+ int len = snprintf(buf, PAGE_SIZE, MIPI_DBI_MODULE_PREFIX "%s\n",
+ dev->name);
+
+ return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute mipi_dbi_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
+static int mipi_dbi_uevent(struct device *_dev, struct kobj_uevent_env *env)
+{
+ struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+
+ add_uevent_var(env, "MODALIAS=%s%s", MIPI_DBI_MODULE_PREFIX,
+ dev->name);
+ return 0;
+}
+
+static const struct dev_pm_ops mipi_dbi_dev_pm_ops = {
+ .runtime_suspend = pm_generic_runtime_suspend,
+ .runtime_resume = pm_generic_runtime_resume,
+ .suspend = pm_generic_suspend,
+ .resume = pm_generic_resume,
+ .freeze = pm_generic_freeze,
+ .thaw = pm_generic_thaw,
+ .poweroff = pm_generic_poweroff,
+ .restore = pm_generic_restore,
+};
+
+static struct bus_type mipi_dbi_bus_type = {
+ .name = "mipi-dbi",
+ .dev_attrs = mipi_dbi_dev_attrs,
+ .match = mipi_dbi_match,
+ .uevent = mipi_dbi_uevent,
+ .pm = &mipi_dbi_dev_pm_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Device and driver (un)registration
+ */
+
+/**
+ * mipi_dbi_device_register - register a DBI device
+ * @dev: DBI device we're registering
+ */
+int mipi_dbi_device_register(struct mipi_dbi_device *dev,
+ struct mipi_dbi_bus *bus)
+{
+ device_initialize(&dev->dev);
+
+ dev->bus = bus;
+ dev->dev.bus = &mipi_dbi_bus_type;
+ dev->dev.parent = bus->dev;
+
+ if (dev->id != -1)
+ dev_set_name(&dev->dev, "%s.%d", dev->name, dev->id);
+ else
+ dev_set_name(&dev->dev, "%s", dev->name);
+
+ return device_add(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_device_register);
+
+/**
+ * mipi_dbi_device_unregister - unregister a DBI device
+ * @dev: DBI device we're unregistering
+ */
+void mipi_dbi_device_unregister(struct mipi_dbi_device *dev)
+{
+ device_del(&dev->dev);
+ put_device(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_device_unregister);
+
+static int mipi_dbi_drv_probe(struct device *_dev)
+{
+ struct mipi_dbi_driver *drv = to_mipi_dbi_driver(_dev->driver);
+ struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+
+ return drv->probe(dev);
+}
+
+static int mipi_dbi_drv_remove(struct device *_dev)
+{
+ struct mipi_dbi_driver *drv = to_mipi_dbi_driver(_dev->driver);
+ struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+ int ret;
+
+ ret = drv->remove(dev);
+ if (ret < 0)
+ return ret;
+
+ mipi_dbi_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+/**
+ * mipi_dbi_driver_register - register a driver for DBI devices
+ * @drv: DBI driver structure
+ */
+int mipi_dbi_driver_register(struct mipi_dbi_driver *drv)
+{
+ drv->driver.bus = &mipi_dbi_bus_type;
+ if (drv->probe)
+ drv->driver.probe = mipi_dbi_drv_probe;
+ if (drv->remove)
+ drv->driver.remove = mipi_dbi_drv_remove;
+
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_driver_register);
+
+/**
+ * mipi_dbi_driver_unregister - unregister a driver for DBI devices
+ * @drv: DBI driver structure
+ */
+void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_driver_unregister);
+
+/* -----------------------------------------------------------------------------
+ * Init/exit
+ */
+
+static int __init mipi_dbi_init(void)
+{
+ return bus_register(&mipi_dbi_bus_type);
+}
+
+static void __exit mipi_dbi_exit(void)
+{
+ bus_unregister(&mipi_dbi_bus_type);
+}
+
+module_init(mipi_dbi_init);
+module_exit(mipi_dbi_exit)
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("MIPI DBI Bus");
+MODULE_LICENSE("GPL");
diff --git a/include/video/display.h b/include/video/display.h
index ba319d6..3138401 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -17,6 +17,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <media/media-entity.h>
+#include <video/mipi-dbi-bus.h>
#define DISPLAY_PIXEL_CODING(option, type, from, to, variant) \
(((option) << 17) | ((type) << 13) | ((variant) << 10) | \
@@ -189,6 +190,9 @@ enum display_entity_interface_type {
struct display_entity_interface_params {
enum display_entity_interface_type type;
+ union {
+ struct mipi_dbi_interface_params dbi;
+ } p;
};
struct display_entity_control_ops {
diff --git a/include/video/mipi-dbi-bus.h b/include/video/mipi-dbi-bus.h
new file mode 100644
index 0000000..876b69d
--- /dev/null
+++ b/include/video/mipi-dbi-bus.h
@@ -0,0 +1,125 @@
+/*
+ * MIPI DBI Bus
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#ifndef __MIPI_DBI_BUS_H__
+#define __MIPI_DBI_BUS_H__
+
+#include <linux/device.h>
+
+struct mipi_dbi_bus;
+struct mipi_dbi_device;
+
+struct mipi_dbi_bus_ops {
+ int (*write_command)(struct mipi_dbi_bus *bus,
+ struct mipi_dbi_device *dev, u16 cmd);
+ int (*write_data)(struct mipi_dbi_bus *bus, struct mipi_dbi_device *dev,
+ const u8 *data, size_t len);
+ int (*read_data)(struct mipi_dbi_bus *bus, struct mipi_dbi_device *dev,
+ u8 *data, size_t len);
+};
+
+struct mipi_dbi_bus {
+ struct device *dev;
+ const struct mipi_dbi_bus_ops *ops;
+};
+
+#define MIPI_DBI_MODULE_PREFIX "mipi-dbi:"
+#define MIPI_DBI_NAME_SIZE 32
+
+struct mipi_dbi_device_id {
+ char name[MIPI_DBI_NAME_SIZE];
+ __kernel_ulong_t driver_data /* Data private to the driver */
+ __aligned(sizeof(__kernel_ulong_t));
+};
+
+enum mipi_dbi_interface_type {
+ MIPI_DBI_INTERFACE_TYPE_A,
+ MIPI_DBI_INTERFACE_TYPE_B,
+};
+
+#define MIPI_DBI_INTERFACE_TE (1 << 0)
+
+struct mipi_dbi_interface_params {
+ enum mipi_dbi_interface_type type;
+ unsigned int flags;
+
+ unsigned int cs_setup;
+ unsigned int rd_setup;
+ unsigned int rd_latch;
+ unsigned int rd_cycle;
+ unsigned int rd_hold;
+ unsigned int wr_setup;
+ unsigned int wr_cycle;
+ unsigned int wr_hold;
+};
+
+#define MIPI_DBI_FLAG_ALIGN_LEFT (1 << 0)
+
+struct mipi_dbi_device {
+ const char *name;
+ int id;
+ struct device dev;
+
+ const struct mipi_dbi_device_id *id_entry;
+ struct mipi_dbi_bus *bus;
+
+ unsigned int flags;
+ unsigned int bus_width;
+ unsigned int data_width;
+};
+
+#define to_mipi_dbi_device(d) container_of(d, struct mipi_dbi_device, dev)
+
+int mipi_dbi_device_register(struct mipi_dbi_device *dev,
+ struct mipi_dbi_bus *bus);
+void mipi_dbi_device_unregister(struct mipi_dbi_device *dev);
+
+struct mipi_dbi_driver {
+ int(*probe)(struct mipi_dbi_device *);
+ int(*remove)(struct mipi_dbi_device *);
+ struct device_driver driver;
+ const struct mipi_dbi_device_id *id_table;
+};
+
+#define to_mipi_dbi_driver(d) container_of(d, struct mipi_dbi_driver, driver)
+
+int mipi_dbi_driver_register(struct mipi_dbi_driver *drv);
+void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv);
+
+static inline void *mipi_dbi_get_drvdata(const struct mipi_dbi_device *dev)
+{
+ return dev_get_drvdata(&dev->dev);
+}
+
+static inline void mipi_dbi_set_drvdata(struct mipi_dbi_device *dev,
+ void *data)
+{
+ dev_set_drvdata(&dev->dev, data);
+}
+
+/* module_mipi_dbi_driver() - Helper macro for drivers that don't do
+ * anything special in module init/exit. This eliminates a lot of
+ * boilerplate. Each module may only use this macro once, and
+ * calling it replaces module_init() and module_exit()
+ */
+#define module_mipi_dbi_driver(__mipi_dbi_driver) \
+ module_driver(__mipi_dbi_driver, mipi_dbi_driver_register, \
+ mipi_dbi_driver_unregister)
+
+int mipi_dbi_set_data_width(struct mipi_dbi_device *dev, unsigned int width);
+
+int mipi_dbi_write_command(struct mipi_dbi_device *dev, u16 cmd);
+int mipi_dbi_read_data(struct mipi_dbi_device *dev, u8 *data, size_t len);
+int mipi_dbi_write_data(struct mipi_dbi_device *dev, const u8 *data,
+ size_t len);
+
+#endif /* __MIPI_DBI_BUS__ */
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 07/19] video: display: Add pixel coding definitions
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
Pixel codings describe how pixels are transmitted on a physical bus. The
information can be communicated between drivers to configure devices.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
include/video/display.h | 120 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 120 insertions(+)
diff --git a/include/video/display.h b/include/video/display.h
index 36ff637..ba319d6 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -18,6 +18,126 @@
#include <linux/module.h>
#include <media/media-entity.h>
+#define DISPLAY_PIXEL_CODING(option, type, from, to, variant) \
+ (((option) << 17) | ((type) << 13) | ((variant) << 10) | \
+ ((to) << 5) | (from))
+
+#define DISPLAY_PIXEL_CODING_FROM(coding) ((coding) & 0x1f)
+#define DISPLAY_PIXEL_CODING_TO(coding) (((coding) >> 5) & 0x1f)
+#define DISPLAY_PIXEL_CODING_VARIANT(coding) (((coding) >> 10) & 7)
+#define DISPLAY_PIXEL_CODING_TYPE(coding) (((coding) >> 13) & 0xf)
+
+#define DISPLAY_PIXEL_CODING_TYPE_DBI 0
+#define DISPLAY_PIXEL_CODING_TYPE_DPI 1
+
+/* DBI pixel codings. */
+#define DISPLAY_PIXEL_CODING_DBI(from, to, variant) \
+ DISPLAY_PIXEL_CODING_TYPE(DISPLAY_PIXEL_CODING_TYPE_DBI, \
+ from, to, variant, 0)
+
+/* Standard DBI codings, defined in the DBI specification. */
+/* 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 */
+/* - - - - - - - - - - R0,2 R0,1 R0,0 G0,2 G0,1 G0,0 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_8TO8 DISPLAY_PIXEL_CODING_DBI(8, 8, 0)
+/* - - - - - - - - - - R0,3 R0,2 R0,1 R0,0 G0,3 G0,2 G0,1 G0,0 */
+/* - - - - - - - - - - B0,3 B0,2 B0,1 B0,0 R1,3 R1,2 R1,1 R1,0 */
+/* - - - - - - - - - - G1,3 G1,2 G1,1 G1,0 B1,3 B1,2 B1,1 b1,0 */
+#define DISPLAY_PIXEL_CODING_DBI_12TO8 DISPLAY_PIXEL_CODING_DBI(12, 8, 0)
+/* - - - - - - - - - - R0,4 R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,3 */
+/* - - - - - - - - - - G0,2 G0,1 G0,0 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_16TO8 DISPLAY_PIXEL_CODING_DBI(16, 8, 0)
+/* - - - - - - - - - - R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 - - */
+/* - - - - - - - - - - G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 - - */
+/* - - - - - - - - - - B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 - - */
+#define DISPLAY_PIXEL_CODING_DBI_18TO8 DISPLAY_PIXEL_CODING_DBI(18, 8, 0)
+/* - - - - - - - - - - R0,7 R0,6 R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 */
+/* - - - - - - - - - - G0,7 G0,6 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 */
+/* - - - - - - - - - - B0,7 B0,6 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_24TO8 DISPLAY_PIXEL_CODING_DBI(24, 8, 0)
+/* - - - - - - - - - R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,4 */
+/* - - - - - - - - - G0,2 G0,1 G0,0 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_18TO9 DISPLAY_PIXEL_CODING_DBI(18, 9, 0)
+/* - - R1,2 R1,1 R1,0 G1,2 G1,1 G1,0 B1,1 B1,0 R0,2 R0,1 R0,0 G0,2 G0,1 G0,0 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_8TO16 DISPLAY_PIXEL_CODING_DBI(8, 16, 0)
+/* - - - - - - R0,3 R0,2 R0,1 R0,0 G0,3 G0,2 G0,1 G0,0 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_12TO16 DISPLAY_PIXEL_CODING_DBI(12, 16, 0)
+/* - - R0,4 R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_16TO16 DISPLAY_PIXEL_CODING_DBI(16, 16, 0)
+/* - - R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 - - G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 - - */
+/* - - B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 - - R1,5 R1,4 R1,3 R1,2 R1,1 R1,0 - - */
+/* - - G1,5 G1,4 G1,3 G1,2 G1,1 G1,0 - - B1,5 B1,4 B1,3 B1,2 B1,1 B1,0 - - */
+#define DISPLAY_PIXEL_CODING_DBI_18TO16_A DISPLAY_PIXEL_CODING_DBI(18, 16, 0)
+/* - - - - - - - - - - R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 - - */
+/* - - G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 - - B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 - - */
+#define DISPLAY_PIXEL_CODING_DBI_18TO16_B DISPLAY_PIXEL_CODING_DBI(18, 16, 1)
+/* - - R0,7 R0,6 R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 G0,7 G0,6 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 */
+/* - - B0,7 B0,6 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 R1,7 R1,6 R1,5 R1,4 R1,3 R1,2 R1,1 R1,0 */
+/* - - G1,7 G1,6 G1,5 G1,4 G1,3 G1,2 G1,1 G1,0 B1,7 B1,6 B1,5 B1,4 B1,3 B1,2 B1,1 B1,0 */
+#define DISPLAY_PIXEL_CODING_DBI_24TO16_A DISPLAY_PIXEL_CODING_DBI(24, 16, 0)
+/* - - - - - - - - - - R0,7 R0,6 R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 */
+/* - - G0,7 G0,6 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 B0,7 B0,6 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_24TO16_B DISPLAY_PIXEL_CODING_DBI(24, 16, 1)
+
+/* Non-standard DBI pixel codings. */
+/* 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 */
+/* - - - - - - - - - - R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 */
+/* - - - - - - - - - - G0,3 G0,2 G0,1 G0,0 B0,5 B0,4 B0,3 B0,2 */
+/* - - - - - - - - - - - - - - - - B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_18TO8_B DISPLAY_PIXEL_CODING_DBI(18, 8, 1)
+/* - - - - - - - - - - - - - - - - R0,5 R0,4 */
+/* - - - - - - - - - - R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,3 G0,2 */
+/* - - - - - - - - - - G0,1 G0,0 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_18TO8_C DISPLAY_PIXEL_CODING_DBI(18, 8, 2)
+/* - - R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 B0,5 B0,4 B0,3 B0,2 */
+/* - - B0,1 B0,0 - - - - - - - - - - - - - - */
+#define DISPLAY_PIXEL_CODING_DBI_18TO16_C DISPLAY_PIXEL_CODING_DBI(18, 16, 2)
+/* - - R0,5 R0,4 - - - - - - - - - - - - - - */
+/* - - R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_18TO16_D DISPLAY_PIXEL_CODING_DBI(18, 16, 3)
+/* - - - - - - R0,7 R0,6 R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 G0,7 G0,6 G0,5 G0,4 */
+/* - - - - - - G0,3 G0,2 G0,1 G0,0 B0,7 B0,6 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_24TO12 DISPLAY_PIXEL_CODING_DBI(24, 12, 0)
+/* R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_18TO18 DISPLAY_PIXEL_CODING_DBI(18, 18, 0)
+
+/* DPI pixel codings. */
+#define DISPLAY_PIXEL_CODING_DPI_RGB(from, to, variant) \
+ DISPLAY_PIXEL_CODING_TYPE(DISPLAY_PIXEL_CODING_TYPE_DPI, \
+ from, to, variant, 0)
+#define DISPLAY_PIXEL_CODING_DPI_YUV(from, to, variant) \
+ DISPLAY_PIXEL_CODING_TYPE(DISPLAY_PIXEL_CODING_TYPE_DPI, \
+ from, to, variant, 1)
+
+/* Standard DPI codings, defined in the DPI specification. */
+/* 23 22 21 20 19 28 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 */
+/* - - - - - - - - R4 R3 R2 R1 R0 G5 G4 G3 G2 G1 G0 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_16TO16 DISPLAY_PIXEL_CODING_DPI_RGB(16, 16, 0)
+/* - - - - - - R5 R4 R3 R2 R1 R0 G5 G4 G3 G2 G1 G0 B5 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_18TO18 DISPLAY_PIXEL_CODING_DPI_RGB(18, 18, 0)
+/* - - - R4 R3 R2 R1 R0 - - G5 G4 G3 G2 G1 G0 - - - B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_16TO20 DISPLAY_PIXEL_CODING_DPI_RGB(16, 20, 0)
+/* - - R4 R3 R2 R1 R0 - - - G5 G4 G3 G2 G1 G0 - - B4 B3 B2 B1 B0 - */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_16TO22 DISPLAY_PIXEL_CODING_DPI_RGB(16, 22, 0)
+/* - - R5 R4 R3 R2 R1 R0 - - G5 G4 G3 G2 G1 G0 - - B5 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_18TO22 DISPLAY_PIXEL_CODING_DPI_RGB(18, 22, 0)
+/* R7 R6 R5 R4 R3 R2 R1 R0 G7 G6 G5 G4 G3 G2 G1 G0 B7 B6 B5 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_24TO24 DISPLAY_PIXEL_CODING_DPI_RGB(24, 24, 0)
+
+/* Non-standard DPI pixel codings. */
+/* 23 22 21 20 19 28 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 */
+/* - - - - - - - - - - - - - - - - R7 R6 R5 R4 R3 R2 R1 R0 */
+/* - - - - - - - - - - - - - - - - G7 G6 G5 G4 G3 G2 G1 G0 */
+/* - - - - - - - - - - - - - - - - B7 B6 B5 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_24TO8 DISPLAY_PIXEL_CODING_DPI_RGB(24, 8, 0)
+/* - - - - - - - - - - - - - - - R5 R4 R3 R2 R1 R0 G5 G4 G3 */
+/* - - - - - - - - - - - - - - - G2 G1 G0 B5 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_18TO9 DISPLAY_PIXEL_CODING_DPI_RGB(18, 9, 0)
+/* - - - - - - - - - - - - R3 R2 R1 R0 G3 G2 G1 G0 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_12TO12 DISPLAY_PIXEL_CODING_DPI_RGB(12, 12, 0)
+/* - - - - - - - - - - - - R7 R6 R5 R4 R3 R2 R1 R0 G7 G6 G5 G4 */
+/* - - - - - - - - - - - - G3 G2 G1 G0 B7 B6 B5 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_24TO12 DISPLAY_PIXEL_CODING_DPI_RGB(24, 12, 0)
+
/* -----------------------------------------------------------------------------
* Display Entity
*/
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 06/19] video: display: OF support
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
Extend the notifier with DT node matching support, and add helper functions to
build the notifier and link entities based on a graph representation in
DT.
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
drivers/video/display/display-core.c | 334 +++++++++++++++++++++++++++++++
drivers/video/display/display-notifier.c | 187 +++++++++++++++++
include/video/display.h | 45 +++++
3 files changed, 566 insertions(+)
diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
index c3b47d3..328ead7 100644
--- a/drivers/video/display/display-core.c
+++ b/drivers/video/display/display-core.c
@@ -14,6 +14,7 @@
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/slab.h>
#include <media/media-device.h>
@@ -315,6 +316,184 @@ void display_entity_unregister(struct display_entity *entity)
EXPORT_SYMBOL_GPL(display_entity_unregister);
/* -----------------------------------------------------------------------------
+ * OF Helpers
+ */
+
+#ifdef CONFIG_OF
+
+/**
+ * display_of_get_next_endpoint() - get next endpoint node
+ * @parent: pointer to the parent device node
+ * @prev: previous endpoint node, or NULL to get first
+ *
+ * Return: An 'endpoint' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is not decremented, the caller have to use
+ * of_node_put() on it when done.
+ */
+struct device_node *
+display_of_get_next_endpoint(const struct device_node *parent,
+ struct device_node *prev)
+{
+ struct device_node *endpoint;
+ struct device_node *port = NULL;
+
+ if (!parent)
+ return NULL;
+
+ if (!prev) {
+ struct device_node *node;
+ /*
+ * It's the first call, we have to find a port subnode
+ * within this node or within an optional 'ports' node.
+ */
+ node = of_get_child_by_name(parent, "ports");
+ if (node)
+ parent = node;
+
+ port = of_get_child_by_name(parent, "port");
+
+ if (port) {
+ /* Found a port, get an endpoint. */
+ endpoint = of_get_next_child(port, NULL);
+ of_node_put(port);
+ } else {
+ endpoint = NULL;
+ }
+
+ if (!endpoint)
+ pr_err("%s(): no endpoint nodes specified for %s\n",
+ __func__, parent->full_name);
+ of_node_put(node);
+ } else {
+ port = of_get_parent(prev);
+ if (!port)
+ /* Hm, has someone given us the root node ?... */
+ return NULL;
+
+ /* Avoid dropping prev node refcount to 0. */
+ of_node_get(prev);
+ endpoint = of_get_next_child(port, prev);
+ if (endpoint) {
+ of_node_put(port);
+ return endpoint;
+ }
+
+ /* No more endpoints under this port, try the next one. */
+ do {
+ port = of_get_next_child(parent, port);
+ if (!port)
+ return NULL;
+ } while (of_node_cmp(port->name, "port"));
+
+ /* Pick up the first endpoint in this port. */
+ endpoint = of_get_next_child(port, NULL);
+ of_node_put(port);
+ }
+
+ return endpoint;
+}
+
+/**
+ * display_of_get_remote_port_parent() - get remote port's parent node
+ * @node: pointer to a local endpoint device_node
+ *
+ * Return: Remote device node associated with remote endpoint node linked
+ * to @node. Use of_node_put() on it when done.
+ */
+struct device_node *
+display_of_get_remote_port_parent(const struct device_node *node)
+{
+ struct device_node *np;
+ unsigned int depth;
+
+ /* Get remote endpoint node. */
+ np = of_parse_phandle(node, "remote-endpoint", 0);
+
+ /* Walk 3 levels up only if there is 'ports' node. */
+ for (depth = 3; depth && np; depth--) {
+ np = of_get_next_parent(np);
+ if (depth = 2 && of_node_cmp(np->name, "ports"))
+ break;
+ }
+ return np;
+}
+
+/**
+ * struct display_of_link - a link between two endpoints
+ * @local_node: pointer to device_node of this endpoint
+ * @local_port: identifier of the port this endpoint belongs to
+ * @remote_node: pointer to device_node of the remote endpoint
+ * @remote_port: identifier of the port the remote endpoint belongs to
+ */
+struct display_of_link {
+ struct device_node *local_node;
+ unsigned int local_port;
+ struct device_node *remote_node;
+ unsigned int remote_port;
+};
+
+/**
+ * display_of_parse_link() - parse a link between two endpoints
+ * @node: pointer to the endpoint at the local end of the link
+ * @link: pointer to the display OF link data structure
+ *
+ * Fill the link structure with the local and remote nodes and port numbers.
+ * The local_node and remote_node fields are set to point to the local and
+ * remote port parent nodes respectively (the port parent node being the parent
+ * node of the port node if that node isn't a 'ports' node, or the grand-parent
+ * node of the port node otherwise).
+ *
+ * A reference is taken to both the local and remote nodes, the caller must use
+ * display_of_put_link() to drop the references when done with the link.
+ *
+ * Return: 0 on success, or -ENOLINK if the remote endpoint can't be found.
+ */
+static int display_of_parse_link(const struct device_node *node,
+ struct display_of_link *link)
+{
+ struct device_node *np;
+
+ memset(link, 0, sizeof(*link));
+
+ np = of_get_parent(node);
+ of_property_read_u32(np, "reg", &link->local_port);
+ np = of_get_next_parent(np);
+ if (of_node_cmp(np->name, "ports") = 0)
+ np = of_get_next_parent(np);
+ link->local_node = np;
+
+ np = of_parse_phandle(node, "remote-endpoint", 0);
+ if (!np) {
+ of_node_put(link->local_node);
+ return -ENOLINK;
+ }
+
+ np = of_get_parent(np);
+ of_property_read_u32(np, "reg", &link->remote_port);
+ np = of_get_next_parent(np);
+ if (of_node_cmp(np->name, "ports") = 0)
+ np = of_get_next_parent(np);
+ link->remote_node = np;
+
+ return 0;
+}
+
+/**
+ * display_of_put_link() - drop references to nodes in a link
+ * @link: pointer to the display OF link data structure
+ *
+ * Drop references to the local and remote nodes in the link. This function must
+ * be called on every link parsed with display_of_parse_link().
+ */
+static void display_of_put_link(struct display_of_link *link)
+{
+ of_node_put(link->local_node);
+ of_node_put(link->remote_node);
+}
+
+#endif /* CONFIG_OF */
+
+/* -----------------------------------------------------------------------------
* Graph Helpers
*/
@@ -420,6 +599,161 @@ int display_entity_link_graph(struct device *dev, struct list_head *entities)
}
EXPORT_SYMBOL_GPL(display_entity_link_graph);
+#ifdef CONFIG_OF
+
+static int display_of_entity_link_entity(struct device *dev,
+ struct display_entity *entity,
+ struct list_head *entities,
+ struct display_entity *root)
+{
+ u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+ const struct device_node *node = entity->dev->of_node;
+ struct media_entity *local = &entity->entity;
+ struct device_node *ep = NULL;
+ int ret = 0;
+
+ dev_dbg(dev, "creating links for entity %s\n", local->name);
+
+ while (1) {
+ struct media_entity *remote = NULL;
+ struct media_pad *remote_pad;
+ struct media_pad *local_pad;
+ struct display_of_link link;
+ struct display_entity *ent;
+ struct device_node *next;
+
+ /* Get the next endpoint and parse its link. */
+ next = display_of_get_next_endpoint(node, ep);
+ if (next = NULL)
+ break;
+
+ of_node_put(ep);
+ ep = next;
+
+ dev_dbg(dev, "processing endpoint %s\n", ep->full_name);
+
+ ret = display_of_parse_link(ep, &link);
+ if (ret < 0) {
+ dev_err(dev, "failed to parse link for %s\n",
+ ep->full_name);
+ continue;
+ }
+
+ /* Skip source pads, they will be processed from the other end of
+ * the link.
+ */
+ if (link.local_port >= local->num_pads) {
+ dev_err(dev, "invalid port number %u on %s\n",
+ link.local_port, link.local_node->full_name);
+ display_of_put_link(&link);
+ ret = -EINVAL;
+ break;
+ }
+
+ local_pad = &local->pads[link.local_port];
+
+ if (local_pad->flags & MEDIA_PAD_FL_SOURCE) {
+ dev_dbg(dev, "skipping source port %s:%u\n",
+ link.local_node->full_name, link.local_port);
+ display_of_put_link(&link);
+ continue;
+ }
+
+ /* Find the remote entity. If not found, just skip the link as
+ * it goes out of scope of the entities handled by the notifier.
+ */
+ list_for_each_entry(ent, entities, list) {
+ if (ent->dev->of_node = link.remote_node) {
+ remote = &ent->entity;
+ break;
+ }
+ }
+
+ if (root->dev->of_node = link.remote_node)
+ remote = &root->entity;
+
+ if (remote = NULL) {
+ dev_dbg(dev, "no entity found for %s\n",
+ link.remote_node->full_name);
+ display_of_put_link(&link);
+ continue;
+ }
+
+ if (link.remote_port >= remote->num_pads) {
+ dev_err(dev, "invalid port number %u on %s\n",
+ link.remote_port, link.remote_node->full_name);
+ display_of_put_link(&link);
+ ret = -EINVAL;
+ break;
+ }
+
+ remote_pad = &remote->pads[link.remote_port];
+
+ display_of_put_link(&link);
+
+ /* Create the media link. */
+ dev_dbg(dev, "creating %s:%u -> %s:%u link\n",
+ remote->name, remote_pad->index,
+ local->name, local_pad->index);
+
+ ret = media_entity_create_link(remote, remote_pad->index,
+ local, local_pad->index,
+ link_flags);
+ if (ret < 0) {
+ dev_err(dev,
+ "failed to create %s:%u -> %s:%u link\n",
+ remote->name, remote_pad->index,
+ local->name, local_pad->index);
+ break;
+ }
+ }
+
+ of_node_put(ep);
+ return ret;
+}
+
+/**
+ * display_of_entity_link_graph - Link all entities in a graph
+ * @dev: device used to print debugging and error messages
+ * @root: optional root display entity
+ * @entities: list of display entities in the graph
+ *
+ * This function creates media controller links for all entities in a graph
+ * based on the device tree graph representation. It relies on all entities
+ * having been instantiated from the device tree.
+ *
+ * The list of entities is typically taken directly from a display notifier
+ * done list. It will thus not include any display entity not handled by the
+ * notifier, such as entities directly accessible by the caller without going
+ * through the notification process. The optional root entity parameter can be
+ * used to pass such a display entity and include it in the graph. For all
+ * practical purpose the root entity is handled is if it was part of the
+ * entities list.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_of_entity_link_graph(struct device *dev, struct list_head *entities,
+ struct display_entity *root)
+{
+ struct display_entity *entity;
+ int ret;
+
+ list_for_each_entry(entity, entities, list) {
+ if (WARN_ON(entity->match->type != DISPLAY_ENTITY_BUS_DT))
+ return -EINVAL;
+
+ ret = display_of_entity_link_entity(dev, entity, entities,
+ root);
+ if (ret < 0)
+ return ret;
+ }
+
+ return display_of_entity_link_entity(dev, root, entities, root);
+}
+EXPORT_SYMBOL_GPL(display_of_entity_link_graph);
+
+#endif
+
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_DESCRIPTION("Display Core");
MODULE_LICENSE("GPL");
diff --git a/drivers/video/display/display-notifier.c b/drivers/video/display/display-notifier.c
index 2d752b3..6bede03 100644
--- a/drivers/video/display/display-notifier.c
+++ b/drivers/video/display/display-notifier.c
@@ -16,6 +16,7 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/of.h>
+#include <linux/slab.h>
#include <video/display.h>
@@ -36,6 +37,14 @@ static bool match_platform(struct device *dev,
return !strcmp(match->match.platform.name, dev_name(dev));
}
+static bool match_dt(struct device *dev, struct display_entity_match *match)
+{
+ pr_debug("%s: matching device node '%s' with node '%s'\n", __func__,
+ dev->of_node->full_name, match->match.dt.node->full_name);
+
+ return match->match.dt.node = dev->of_node;
+}
+
static struct display_entity_match *
display_entity_notifier_match(struct display_entity_notifier *notifier,
struct display_entity *entity)
@@ -52,6 +61,9 @@ display_entity_notifier_match(struct display_entity_notifier *notifier,
case DISPLAY_ENTITY_BUS_PLATFORM:
match_func = match_platform;
break;
+ case DISPLAY_ENTITY_BUS_DT:
+ match_func = match_dt;
+ break;
}
if (match_func(entity->dev, match))
@@ -158,6 +170,7 @@ int display_entity_register_notifier(struct display_entity_notifier *notifier)
switch (match->type) {
case DISPLAY_ENTITY_BUS_PLATFORM:
+ case DISPLAY_ENTITY_BUS_DT:
break;
default:
dev_err(notifier->dev,
@@ -272,6 +285,180 @@ int display_entity_build_notifier(struct display_entity_notifier *notifier,
EXPORT_SYMBOL_GPL(display_entity_build_notifier);
/* -----------------------------------------------------------------------------
+ * OF Support
+ */
+
+#ifdef CONFIG_OF
+
+struct display_entity_of {
+ struct list_head list;
+ struct device_node *node;
+};
+
+static struct display_entity_of *
+display_of_find_entity(struct list_head *entities,
+ const struct device_node *node)
+{
+ struct display_entity_of *entity;
+
+ list_for_each_entry(entity, entities, list) {
+ if (entity->node = node)
+ return entity;
+ }
+
+ return NULL;
+}
+
+static int display_of_parse_dt(struct display_entity_notifier *notifier,
+ struct list_head *entities,
+ struct device_node *node)
+{
+ struct display_entity_of *entity;
+ struct device_node *remote;
+ struct device_node *ep = NULL;
+ struct device_node *next;
+ unsigned int num_entities = 0;
+ int ret = 0;
+
+ /* Walk the device tree and build a list of nodes. */
+ dev_dbg(notifier->dev, "parsing node %s\n", node->full_name);
+
+ while (1) {
+ next = display_of_get_next_endpoint(node, ep);
+ if (next = NULL)
+ break;
+
+ of_node_put(ep);
+ ep = next;
+
+ dev_dbg(notifier->dev, "handling endpoint %s\n", ep->full_name);
+
+ remote = display_of_get_remote_port_parent(ep);
+ if (remote = NULL)
+ continue;
+
+ /* Skip entities that we have already processed. */
+ if (display_of_find_entity(entities, remote) || remote = node) {
+ dev_dbg(notifier->dev,
+ "entity %s already in list, skipping\n",
+ remote->full_name);
+ continue;
+ }
+
+ entity = kzalloc(sizeof(*entity), GFP_KERNEL);
+ if (entity = NULL) {
+ of_node_put(remote);
+ ret = -ENOMEM;
+ break;
+ }
+
+ dev_dbg(notifier->dev, "adding remote entity %s to list\n",
+ remote->full_name);
+
+ entity->node = remote;
+ list_add_tail(&entity->list, entities);
+ num_entities++;
+ }
+
+ of_node_put(ep);
+
+ if (ret < 0)
+ return ret;
+
+ return num_entities;
+}
+
+/**
+ * display_of_entity_build_notifier - build a notifier from device tree
+ * @notifier: display entity notifier to be built
+ * @node: device tree node
+ *
+ * Before registering a notifier drivers must initialize the notifier's list of
+ * entities. This helper function simplifies building the list of entities for
+ * drivers that use a device tree representation of the graph.
+ *
+ * The function allocates an array of struct display_entity_match, initialize it
+ * from the device tree, and sets the notifier entities and num_entities fields.
+ *
+ * The entities array is allocated using the managed memory allocation API on
+ * the notifier device, which must be initialized before calling this function.
+ *
+ * Return 0 on success or a negative error code on error.
+ */
+int display_of_entity_build_notifier(struct display_entity_notifier *notifier,
+ struct device_node *node)
+{
+ struct display_entity_match *matches;
+ struct display_entity_of *entity;
+ struct display_entity_of *next;
+ unsigned int num_entities = 0;
+ LIST_HEAD(entities);
+ unsigned int i;
+ int ret;
+
+ /* Add an initial entity that stores the device tree node pointer to the
+ * list.
+ */
+ entity = kzalloc(sizeof(*entity), GFP_KERNEL);
+ if (entity = NULL)
+ return -ENOMEM;
+
+ entity->node = node;
+ list_add_tail(&entity->list, &entities);
+
+ /* Parse all entities in the list. New entities will be added at the
+ * tail when parsing the device tree and will just be processed by the
+ * next iterations.
+ */
+ list_for_each_entry(entity, &entities, list) {
+ ret = display_of_parse_dt(notifier, &entities, entity->node);
+ if (ret < 0)
+ goto error;
+
+ num_entities += ret;
+ }
+
+ /* Allocate the entity matches array and fill it. */
+ matches = devm_kzalloc(notifier->dev, sizeof(*notifier->entities) *
+ num_entities, GFP_KERNEL);
+ if (matches = NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ i = 0;
+ list_for_each_entry_safe(entity, next, &entities, list) {
+ struct display_entity_match *match;
+
+ /* Don't add the initial node to the matches array. */
+ if (entity->node != node) {
+ match = &matches[i++];
+ match->type = DISPLAY_ENTITY_BUS_DT;
+ match->match.dt.node = entity->node;
+ }
+
+ list_del(&entity->list);
+ kfree(entity);
+ }
+
+ notifier->num_entities = num_entities;
+ notifier->entities = matches;
+
+ return 0;
+
+error:
+ list_for_each_entry_safe(entity, next, &entities, list) {
+ list_del(&entity->list);
+ kfree(entity);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(display_of_entity_build_notifier);
+
+#endif /* CONFIG_OF */
+
+/* -----------------------------------------------------------------------------
* Entity Registration
*/
diff --git a/include/video/display.h b/include/video/display.h
index 58ff0d1..36ff637 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -22,6 +22,7 @@
* Display Entity
*/
+struct device_node;
struct display_entity;
struct display_entity_match;
struct display_entity_notify;
@@ -145,12 +146,33 @@ int display_entity_get_params(struct display_entity *entity, unsigned int port,
int display_entity_set_stream(struct display_entity *entity, unsigned int port,
enum display_entity_stream_state state);
+#ifdef CONFIG_OF
+struct device_node *
+display_of_get_next_endpoint(const struct device_node *parent,
+ struct device_node *prev);
+struct device_node *
+display_of_get_remote_port_parent(const struct device_node *node);
+#else
+static inline struct device_node *
+display_of_get_next_endpoint(const struct device_node *parent,
+ struct device_node *prev)
+{
+ return NULL;
+}
+static inline struct device_node *
+display_of_get_remote_port_parent(const struct device_node *node)
+{
+ return NULL;
+}
+#endif
+
/* -----------------------------------------------------------------------------
* Notifier
*/
enum display_entity_bus_type {
DISPLAY_ENTITY_BUS_PLATFORM,
+ DISPLAY_ENTITY_BUS_DT,
};
/**
@@ -167,6 +189,9 @@ struct display_entity_match {
struct {
const char *name;
} platform;
+ struct {
+ const struct device_node *node;
+ } dt;
} match;
struct list_head list;
@@ -226,4 +251,24 @@ int display_entity_build_notifier(struct display_entity_notifier *notifier,
const struct display_entity_graph_data *graph);
int display_entity_link_graph(struct device *dev, struct list_head *entities);
+#ifdef CONFIG_OF
+int display_of_entity_build_notifier(struct display_entity_notifier *notifier,
+ struct device_node *node);
+int display_of_entity_link_graph(struct device *dev, struct list_head *entities,
+ struct display_entity *root);
+#else
+static inline int
+display_of_entity_build_notifier(struct display_entity_notifier *notifier,
+ struct device_node *node)
+{
+ return -ENOSYS;
+}
+static inline int
+display_of_entity_link_graph(struct device *dev,struct list_head *entities,
+ struct display_entity *root)
+{
+ return -ENOSYS;
+}
+#endif
+
#endif /* __DISPLAY_H__ */
--
1.8.1.5
^ permalink raw reply related
* [PATCH/RFC v3 05/19] video: display: Graph helpers
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-media
In-Reply-To: <1376089398-13322-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>
Add two graph helper functions. display_entity_build_notifier() builds
an entity notifier from an entities graph represented as a flat array,
typically passed from platform data. display_entity_link_graph() can
then be used to create media controller links between all entities in
the graph.
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
drivers/video/display/display-core.c | 107 +++++++++++++++++++++++++++++++
drivers/video/display/display-notifier.c | 51 +++++++++++++++
include/video/display.h | 20 ++++++
3 files changed, 178 insertions(+)
diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
index bb18723..c3b47d3 100644
--- a/drivers/video/display/display-core.c
+++ b/drivers/video/display/display-core.c
@@ -10,6 +10,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/device.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -313,6 +314,112 @@ void display_entity_unregister(struct display_entity *entity)
}
EXPORT_SYMBOL_GPL(display_entity_unregister);
+/* -----------------------------------------------------------------------------
+ * Graph Helpers
+ */
+
+static int display_entity_link_entity(struct device *dev,
+ struct display_entity *entity,
+ struct list_head *entities)
+{
+ const struct display_entity_graph_data *graph = entity->match->data;
+ u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+ struct media_entity *local = &entity->entity;
+ unsigned int i;
+ int ret = 0;
+
+ dev_dbg(dev, "creating links for entity %s\n", local->name);
+
+ for (i = 0; i < entity->entity.num_pads; ++i) {
+ const struct display_entity_source_data *source;
+ struct media_pad *local_pad = &local->pads[i];
+ struct media_entity *remote = NULL;
+ struct media_pad *remote_pad;
+ struct display_entity *ent;
+
+ dev_dbg(dev, "processing pad %u\n", i);
+
+ /* Skip source pads, they will be processed from the other end
+ * of the link.
+ */
+ if (local_pad->flags & MEDIA_PAD_FL_SOURCE) {
+ dev_dbg(dev, "skipping source pad %s:%u\n",
+ local->name, i);
+ continue;
+ }
+
+ /* Find the remote entity. If not found, just skip the link as
+ * it goes out of scope of the entities handled by the notifier.
+ */
+ source = &graph->sources[i];
+ list_for_each_entry(ent, entities, list) {
+ if (strcmp(source->name, dev_name(ent->dev)) = 0) {
+ remote = &ent->entity;
+ break;
+ }
+ }
+
+ if (remote = NULL) {
+ dev_dbg(dev, "no entity found for %s\n", source->name);
+ continue;
+ }
+
+ if (source->port >= remote->num_pads) {
+ dev_err(dev, "invalid port number %u on %s\n",
+ source->port, source->name);
+ ret = -EINVAL;
+ break;
+ }
+
+ remote_pad = &remote->pads[source->port];
+
+ /* Create the media link. */
+ dev_dbg(dev, "creating %s:%u -> %s:%u link\n",
+ remote->name, remote_pad->index,
+ local->name, local_pad->index);
+
+ ret = media_entity_create_link(remote, remote_pad->index,
+ local, local_pad->index,
+ link_flags);
+ if (ret < 0) {
+ dev_err(dev, "failed to create %s:%u -> %s:%u link\n",
+ remote->name, remote_pad->index,
+ local->name, local_pad->index);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * display_entity_link_graph - Link all entities in a graph
+ * @dev: device used to print debugging and error messages
+ * @entities: list of display entities in the graph
+ *
+ * This function creates media controller links for all entities in a graph
+ * based on graph link data. It relies on the entities match data pointers
+ * having been initialized by the display_entity_build_notifier() function when
+ * building the notifier and thus can't be used when the notifier is built in a
+ * different way.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_link_graph(struct device *dev, struct list_head *entities)
+{
+ struct display_entity *entity;
+ int ret;
+
+ list_for_each_entry(entity, entities, list) {
+ ret = display_entity_link_entity(dev, entity, entities);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_link_graph);
+
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_DESCRIPTION("Display Core");
MODULE_LICENSE("GPL");
diff --git a/drivers/video/display/display-notifier.c b/drivers/video/display/display-notifier.c
index c9210ec..2d752b3 100644
--- a/drivers/video/display/display-notifier.c
+++ b/drivers/video/display/display-notifier.c
@@ -220,6 +220,57 @@ void display_entity_unregister_notifier(struct display_entity_notifier *notifier
}
EXPORT_SYMBOL_GPL(display_entity_unregister_notifier);
+/**
+ * display_entity_build_notifier - build a notifier from graph data
+ * @notifier: display entity notifier to be built
+ * @graph: graph data
+ *
+ * Before registering a notifier drivers must initialize the notifier's list of
+ * entities. This helper function simplifies building the list of entities for
+ * drivers that use an array of struct display_entity_graph_data to describe the
+ * entities graph.
+ *
+ * The function allocates an array of struct display_entity_match, initialize it
+ * from graph data, and sets the notifier entities and num_entities fields.
+ *
+ * The entities array is allocated using the managed memory allocation API on
+ * the notifier device, which must be initialized before calling this function.
+ *
+ * Return 0 on success or a negative error code on error.
+ */
+int display_entity_build_notifier(struct display_entity_notifier *notifier,
+ const struct display_entity_graph_data *graph)
+{
+ struct display_entity_match *entities;
+ unsigned int num_entities;
+ unsigned int i;
+
+ for (num_entities = 0; graph[num_entities].name; ++num_entities) {
+ }
+
+ if (num_entities = 0)
+ return -EINVAL;
+
+ entities = devm_kzalloc(notifier->dev, sizeof(*notifier->entities) *
+ num_entities, GFP_KERNEL);
+ if (entities = NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < num_entities; ++i) {
+ struct display_entity_match *match = &entities[i];
+
+ match->type = DISPLAY_ENTITY_BUS_PLATFORM;
+ match->match.platform.name = graph[i].name;
+ match->data = &graph[i];
+ }
+
+ notifier->num_entities = num_entities;
+ notifier->entities = entities;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_build_notifier);
+
/* -----------------------------------------------------------------------------
* Entity Registration
*/
diff --git a/include/video/display.h b/include/video/display.h
index 2063694..58ff0d1 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -159,6 +159,7 @@ enum display_entity_bus_type {
* @match.platform.name: platform device name
* @match.dt.node: DT node
* @list: link match objects waiting to be matched
+ * @data: driver private data, not touched by the core
*/
struct display_entity_match {
enum display_entity_bus_type type;
@@ -169,6 +170,7 @@ struct display_entity_match {
} match;
struct list_head list;
+ const void *data;
};
/**
@@ -206,4 +208,22 @@ void display_entity_unregister_notifier(struct display_entity_notifier *notifier
int display_entity_add(struct display_entity *entity);
void display_entity_remove(struct display_entity *entity);
+/* -----------------------------------------------------------------------------
+ * Graph Helpers
+ */
+
+struct display_entity_source_data {
+ const char *name;
+ unsigned int port;
+};
+
+struct display_entity_graph_data {
+ const char *name;
+ const struct display_entity_source_data *sources;
+};
+
+int display_entity_build_notifier(struct display_entity_notifier *notifier,
+ const struct display_entity_graph_data *graph);
+int display_entity_link_graph(struct device *dev, struct list_head *entities);
+
#endif /* __DISPLAY_H__ */
--
1.8.1.5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox