Skip to content

Commit

Permalink
video: fbdev: pxafb: initial devicetree conversion
Browse files Browse the repository at this point in the history
This patch brings a first support of pxa framebuffer devices to a
devicetree pxa platform, as was before platform data.

There are restrictions with this port, the biggest one being the lack of
support of smart panels. Moreover the conversion doesn't provide a way
to declare multiple framebuffer configurations with different bits per
pixel, only the LCD hardware bus width is used.

The patch was tested on both pxa25x, pxa27x and pxa3xx platform (namely
lubbock, mainstone and zylonite).

Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
  • Loading branch information
Robert Jarzmik authored and Tomi Valkeinen committed Dec 15, 2015
1 parent f3621a6 commit 420a488
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 3 deletions.
2 changes: 2 additions & 0 deletions drivers/video/fbdev/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1885,6 +1885,8 @@ config FB_PXA
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select VIDEOMODE_HELPERS if OF
select FB_MODE_HELPERS if OF
---help---
Frame buffer driver for the built-in LCD controller in the Intel
PXA2x0 processor.
Expand Down
160 changes: 157 additions & 3 deletions drivers/video/fbdev/pxafb.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/console.h>
#include <linux/of_graph.h>
#include <video/of_display_timing.h>
#include <video/videomode.h>

#include <mach/hardware.h>
#include <asm/io.h>
Expand Down Expand Up @@ -2092,6 +2095,148 @@ static void pxafb_check_options(struct device *dev, struct pxafb_mach_info *inf)
#define pxafb_check_options(...) do {} while (0)
#endif

#if defined(CONFIG_OF)
static const char * const lcd_types[] = {
"unknown", "mono-stn", "mono-dstn", "color-stn", "color-dstn",
"color-tft", "smart-panel", NULL
};

static int of_get_pxafb_display(struct device *dev, struct device_node *disp,
struct pxafb_mach_info *info, u32 bus_width)
{
struct display_timings *timings;
struct videomode vm;
int i, ret = -EINVAL;
const char *s;

ret = of_property_read_string(disp, "lcd-type", &s);
if (ret)
s = "color-tft";

for (i = 0; lcd_types[i]; i++)
if (!strcmp(s, lcd_types[i]))
break;
if (!i || !lcd_types[i]) {
dev_err(dev, "lcd-type %s is unknown\n", s);
return -EINVAL;
}
info->lcd_conn |= LCD_CONN_TYPE(i);
info->lcd_conn |= LCD_CONN_WIDTH(bus_width);

timings = of_get_display_timings(disp);
if (!timings)
goto out;

ret = -ENOMEM;
info->modes = kmalloc_array(timings->num_timings,
sizeof(info->modes[0]), GFP_KERNEL);
if (!info->modes)
goto out;
info->num_modes = timings->num_timings;

for (i = 0; i < timings->num_timings; i++) {
ret = videomode_from_timings(timings, &vm, i);
if (ret) {
dev_err(dev, "videomode_from_timings %d failed: %d\n",
i, ret);
goto out;
}
if (vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
info->lcd_conn |= LCD_PCLK_EDGE_RISE;
if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
info->lcd_conn |= LCD_PCLK_EDGE_FALL;
if (vm.flags & DISPLAY_FLAGS_DE_HIGH)
info->lcd_conn |= LCD_BIAS_ACTIVE_HIGH;
if (vm.flags & DISPLAY_FLAGS_DE_LOW)
info->lcd_conn |= LCD_BIAS_ACTIVE_LOW;
if (vm.flags & DISPLAY_FLAGS_HSYNC_HIGH)
info->modes[i].sync |= FB_SYNC_HOR_HIGH_ACT;
if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH)
info->modes[i].sync |= FB_SYNC_VERT_HIGH_ACT;

info->modes[i].pixclock = 1000000000UL / (vm.pixelclock / 1000);
info->modes[i].xres = vm.hactive;
info->modes[i].yres = vm.vactive;
info->modes[i].hsync_len = vm.hsync_len;
info->modes[i].left_margin = vm.hback_porch;
info->modes[i].right_margin = vm.hfront_porch;
info->modes[i].vsync_len = vm.vsync_len;
info->modes[i].upper_margin = vm.vback_porch;
info->modes[i].lower_margin = vm.vfront_porch;
}
ret = 0;

out:
display_timings_release(timings);
return ret;
}

static int of_get_pxafb_mode_info(struct device *dev,
struct pxafb_mach_info *info)
{
struct device_node *display, *np;
u32 bus_width;
int ret, i;

np = of_graph_get_next_endpoint(dev->of_node, NULL);
if (!np) {
dev_err(dev, "could not find endpoint\n");
return -EINVAL;
}
ret = of_property_read_u32(np, "bus-width", &bus_width);
if (ret) {
dev_err(dev, "no bus-width specified: %d\n", ret);
return ret;
}

display = of_graph_get_remote_port_parent(np);
of_node_put(np);
if (!display) {
dev_err(dev, "no display defined\n");
return -EINVAL;
}

ret = of_get_pxafb_display(dev, display, info, bus_width);
of_node_put(display);
if (ret)
return ret;

for (i = 0; i < info->num_modes; i++)
info->modes[i].bpp = bus_width;

return 0;
}

static struct pxafb_mach_info *of_pxafb_of_mach_info(struct device *dev)
{
int ret;
struct pxafb_mach_info *info;

if (!dev->of_node)
return NULL;
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
if (!info)
return ERR_PTR(-ENOMEM);
ret = of_get_pxafb_mode_info(dev, info);
if (ret) {
kfree(info->modes);
return ERR_PTR(ret);
}

/*
* On purpose, neither lccrX registers nor video memory size can be
* specified through device-tree, they are considered more a debug hack
* available through command line.
*/
return info;
}
#else
static struct pxafb_mach_info *of_pxafb_of_mach_info(struct device *dev)
{
return NULL;
}
#endif

static int pxafb_probe(struct platform_device *dev)
{
struct pxafb_info *fbi;
Expand All @@ -2104,8 +2249,7 @@ static int pxafb_probe(struct platform_device *dev)
ret = -ENOMEM;
pdata = dev_get_platdata(&dev->dev);
inf = devm_kmalloc(&dev->dev, sizeof(*inf), GFP_KERNEL);
if (!inf)
goto failed;

if (pdata) {
*inf = *pdata;
inf->modes =
Expand All @@ -2117,8 +2261,9 @@ static int pxafb_probe(struct platform_device *dev)
inf->modes[i] = pdata->modes[i];
}

fbi = NULL;
if (!pdata)
inf = of_pxafb_of_mach_info(&dev->dev);
if (IS_ERR_OR_NULL(inf))
goto failed;

ret = pxafb_parse_options(&dev->dev, g_options, inf);
Expand Down Expand Up @@ -2313,11 +2458,20 @@ static int pxafb_remove(struct platform_device *dev)
return 0;
}

static const struct of_device_id pxafb_of_dev_id[] = {
{ .compatible = "marvell,pxa270-lcdc", },
{ .compatible = "marvell,pxa300-lcdc", },
{ .compatible = "marvell,pxa2xx-lcdc", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, pxafb_of_dev_id);

static struct platform_driver pxafb_driver = {
.probe = pxafb_probe,
.remove = pxafb_remove,
.driver = {
.name = "pxa2xx-fb",
.of_match_table = pxafb_of_dev_id,
#ifdef CONFIG_PM
.pm = &pxafb_pm_ops,
#endif
Expand Down

0 comments on commit 420a488

Please sign in to comment.