I’ve created two small device and driver modules to test my LCD. I’ve based them (mostly copied so far) on code from board-am335xevm.c and da8xx-fb.c.
When the fb_probe function is executed it fail to get the clock and exits with the error: “Can not get device clock”
What am I missing?
(I’ve setup the mux using a shell script)
Thanks!
BR, Thomas Fogh
device.c:
`
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <video/da8xx-fb.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/hardware/asp.h>
static const struct display_panel disp_panel = {
QVGA,
24,
24,
COLOR_ACTIVE,
};
static struct lcd_ctrl_config lcd_cfg = {
&disp_panel,
.ac_bias = 255,
.ac_bias_intrpt = 0,
.dma_burst_sz = 16,
.bpp = 24,
.fdd = 0x80,
.tft_alt_mode = 0,
.stn_565_mode = 0,
.mono_8bit_mode = 0,
.invert_line_clock = 1,
.invert_frm_clock = 1,
.sync_edge = 0,
.sync_ctrl = 1,
.raster_order = 0,
};
struct da8xx_lcdc_platform_data ETM035009ADH6_pdata = {
.manu_name = “EDT Corp”,
.controller_data = &lcd_cfg,
.type = “ETM035009ADH6”,
};
#define L4_PER_LCDC_PHYS 0x4830E000
static struct resource am33xx_lcdc_resources[] = {
[0] = { /* registers /
.start = L4_PER_LCDC_PHYS,
.end = L4_PER_LCDC_PHYS + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = { / interrupt */
.start = AM33XX_IRQ_LCD,
.end = AM33XX_IRQ_LCD,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device am33xx_lcdc_device = {
.name = “edt_lcdc”,
.id = 0,
.num_resources = ARRAY_SIZE(am33xx_lcdc_resources),
.resource = am33xx_lcdc_resources,
};
static int __init conf_disp_pll(int rate)
{
struct clk *disp_pll;
int ret = -EINVAL;
disp_pll = clk_get(NULL, “dpll_disp_ck”);
if (IS_ERR(disp_pll)) {
printk(KERN_INFO “Cannot clk_get disp_pll\n”);
} else {
ret = clk_set_rate(disp_pll, rate);
clk_put(disp_pll);
}
return ret;
}
static int am33xx_register_lcdc(struct da8xx_lcdc_platform_data *pdata)
{
int ret;
am33xx_lcdc_device.dev.platform_data = pdata;
ret = platform_device_register(&am33xx_lcdc_device);
if (ret) {
printk(KERN_ALERT “am33xx_register_lcdc: lcdc registration failed: %d\n”, ret);
}
return ret;
}
static int __init edt_device_init(void)
{
int ret = 0;
printk(KERN_INFO “edt_device_init\n”);
if ((ret = conf_disp_pll(300000000))) {
printk(KERN_ALERT “Failed configure display PLL, not attempting to register LCDC\n”);
return ret;
}
if ((ret = am33xx_register_lcdc(&ETM035009ADH6_pdata))) {
printk(KERN_ALERT “Failed to register LCDC device\n”);
}
return ret;
}
static void __exit edt_device_cleanup(void)
{
printk(KERN_INFO “edt_device_cleanup\n”);
platform_device_unregister(&am33xx_lcdc_device);
}
module_init(edt_device_init);
module_exit(edt_device_cleanup);
MODULE_DESCRIPTION(“Framebuffer driver for TI da8xx/omap-l1xx”);
MODULE_AUTHOR(“Texas Instruments”);
MODULE_LICENSE(“GPL”);
`
driver.c:
`
static int __devinit fb_probe(struct platform_device *device)
{
struct da8xx_lcdc_platform_data *fb_pdata =
device->dev.platform_data;
struct lcd_ctrl_config *lcd_cfg;
struct da8xx_panel *lcdc_info;
struct fb_info *da8xx_fb_info;
struct clk *fb_clk = NULL;
struct da8xx_fb_par *par;
resource_size_t len;
int ret, i;
printk(KERN_INFO “fb_probe\n”);
if (fb_pdata == NULL) {
printk(KERN_INFO “Can not get platform data\n”);
return -ENOENT;
}
lcdc_regs = platform_get_resource(device, IORESOURCE_MEM, 0);
if (!lcdc_regs) {
printk(KERN_INFO “Can not get memory resource for LCD controller\n”);
return -ENOENT;
}
len = resource_size(lcdc_regs);
lcdc_regs = request_mem_region(lcdc_regs->start, len, lcdc_regs->name);
if (!lcdc_regs)
return -EBUSY;
da8xx_fb_reg_base = (resource_size_t)ioremap(lcdc_regs->start, len);
if (!da8xx_fb_reg_base) {
ret = -EBUSY;
goto err_request_mem;
}
fb_clk = clk_get(&device->dev, NULL);
if (IS_ERR(fb_clk)) {
printk(KERN_INFO “Can not get device clock\n”);
ret = -ENODEV;
goto err_ioremap;
}
ret = clk_enable(fb_clk);
if (ret)
goto err_clk_put;
/* Determine LCD IP Version */
switch (lcdc_read(LCD_PID_REG)) {
case 0x4C100102:
lcd_revision = LCD_VERSION_1;
break;
case 0x4F200800:
lcd_revision = LCD_VERSION_2;
break;
default:
printk(KERN_INFO "Unknown PID Reg value 0x%x, "
“defaulting to LCD revision 1\n”,
lcdc_read(LCD_PID_REG));
lcd_revision = LCD_VERSION_1;
break;
}
printk(KERN_INFO “panel_name = %s\n”, fb_pdata->type);
for (i = 0, lcdc_info = known_lcd_panels;
i < ARRAY_SIZE(known_lcd_panels);
i++, lcdc_info++) {
if (strcmp(fb_pdata->type, lcdc_info->name) == 0)
break;
}
if (i == ARRAY_SIZE(known_lcd_panels)) {
printk(KERN_INFO “GLCD: No valid panel found [%s]\n”, fb_pdata->type);
ret = -ENODEV;
goto err_clk_disable;
} else
printk(KERN_INFO “GLCD: Found %s panel\n”, fb_pdata->type);
lcd_cfg = (struct lcd_ctrl_config *)fb_pdata->controller_data;
da8xx_fb_info = framebuffer_alloc(sizeof(struct da8xx_fb_par),
&device->dev);
if (!da8xx_fb_info) {
printk(KERN_INFO “Memory allocation failed for fb_info\n”);
ret = -ENOMEM;
goto err_clk_disable;
}
par = da8xx_fb_info->par;
par->lcdc_clk = fb_clk;
par->pxl_clk = lcdc_info->pxl_clk;
if (fb_pdata->panel_power_ctrl) {
par->panel_power_ctrl = fb_pdata->panel_power_ctrl;
par->panel_power_ctrl(1);
}
if (lcd_init(par, lcd_cfg, lcdc_info) < 0) {
printk(KERN_INFO “lcd_init failed\n”);
ret = -EFAULT;
goto err_release_fb;
}
/* allocate frame buffer */
par->vram_size = lcdc_info->width * lcdc_info->height * lcd_cfg->bpp;
par->vram_size = PAGE_ALIGN(par->vram_size/8);
par->vram_size = par->vram_size * LCD_NUM_BUFFERS;
par->vram_virt = dma_alloc_coherent(NULL,
par->vram_size,
(resource_size_t *) &par->vram_phys,
GFP_KERNEL | GFP_DMA);
if (!par->vram_virt) {
printk(KERN_INFO “GLCD: kmalloc for frame buffer failed\n”);
ret = -EINVAL;
goto err_release_fb;
}
da8xx_fb_info->screen_base = (char __iomem *) par->vram_virt;
da8xx_fb_fix.smem_start = par->vram_phys;
da8xx_fb_fix.smem_len = par->vram_size;
da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8;
par->dma_start = par->vram_phys;
par->dma_end = par->dma_start + lcdc_info->height *
da8xx_fb_fix.line_length - 1;
/* allocate palette buffer */
par->v_palette_base = dma_alloc_coherent(NULL,
PALETTE_SIZE,
(resource_size_t *)
&par->p_palette_base,
GFP_KERNEL | GFP_DMA);
if (!par->v_palette_base) {
printk(KERN_INFO “GLCD: kmalloc for palette buffer failed\n”);
ret = -EINVAL;
goto err_release_fb_mem;
}
memset(par->v_palette_base, 0, PALETTE_SIZE);
par->irq = platform_get_irq(device, 0);
if (par->irq < 0) {
ret = -ENOENT;
goto err_release_pl_mem;
}
/* Initialize par */
da8xx_fb_info->var.bits_per_pixel = lcd_cfg->bpp;
da8xx_fb_var.xres = lcdc_info->width;
da8xx_fb_var.xres_virtual = lcdc_info->width;
da8xx_fb_var.yres = lcdc_info->height;
da8xx_fb_var.yres_virtual = lcdc_info->height * LCD_NUM_BUFFERS;
da8xx_fb_var.grayscale =
lcd_cfg->p_disp_panel->panel_shade == MONOCHROME ? 1 : 0;
da8xx_fb_var.bits_per_pixel = lcd_cfg->bpp;
da8xx_fb_var.hsync_len = lcdc_info->hsw;
da8xx_fb_var.vsync_len = lcdc_info->vsw;
/* Initialize fbinfo */
da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT;
da8xx_fb_info->fix = da8xx_fb_fix;
da8xx_fb_info->var = da8xx_fb_var;
da8xx_fb_info->fbops = &da8xx_fb_ops;
da8xx_fb_info->pseudo_palette = par->pseudo_palette;
da8xx_fb_info->fix.visual = (da8xx_fb_info->var.bits_per_pixel <= 8) ?
FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
ret = fb_alloc_cmap(&da8xx_fb_info->cmap, PALETTE_SIZE, 0);
if (ret)
goto err_release_pl_mem;
da8xx_fb_info->cmap.len = par->palette_sz;
/* initialize var_screeninfo */
da8xx_fb_var.activate = FB_ACTIVATE_FORCE;
fb_set_var(da8xx_fb_info, &da8xx_fb_var);
dev_set_drvdata(&device->dev, da8xx_fb_info);
/* initialize the vsync wait queue */
init_waitqueue_head(&par->vsync_wait);
par->vsync_timeout = HZ / 5;
/* Register the Frame Buffer */
if (register_framebuffer(da8xx_fb_info) < 0) {
printk(KERN_INFO “GLCD: Frame Buffer Registration Failed!\n”);
ret = -EINVAL;
goto err_dealloc_cmap;
}
#ifdef CONFIG_CPU_FREQ
ret = lcd_da8xx_cpufreq_register(par);
if (ret) {
printk(KERN_INFO “failed to register cpufreq\n”);
goto err_cpu_freq;
}
#endif
if (lcd_revision == LCD_VERSION_1)
lcdc_irq_handler = lcdc_irq_handler_rev01;
else
lcdc_irq_handler = lcdc_irq_handler_rev02;
ret = request_irq(par->irq, lcdc_irq_handler, 0,
DRIVER_NAME, par);
if (ret)
goto irq_freq;
return 0;
irq_freq:
#ifdef CONFIG_CPU_FREQ
lcd_da8xx_cpufreq_deregister(par);
#endif
err_cpu_freq:
unregister_framebuffer(da8xx_fb_info);
err_dealloc_cmap:
fb_dealloc_cmap(&da8xx_fb_info->cmap);
err_release_pl_mem:
dma_free_coherent(NULL, PALETTE_SIZE, par->v_palette_base,
par->p_palette_base);
err_release_fb_mem:
dma_free_coherent(NULL, par->vram_size, par->vram_virt, par->vram_phys);
err_release_fb:
framebuffer_release(da8xx_fb_info);
err_clk_disable:
clk_disable(fb_clk);
err_clk_put:
clk_put(fb_clk);
err_ioremap:
iounmap((void __iomem *)da8xx_fb_reg_base);
err_request_mem:
release_mem_region(lcdc_regs->start, len);
return ret;
}
static struct platform_driver da8xx_fb_driver = {
.probe = fb_probe,
.remove = __devexit_p(fb_remove),
.suspend = fb_suspend,
.resume = fb_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
};
static int __init da8xx_fb_init(void)
{
printk(KERN_INFO “fb_init\n”);
return platform_driver_register(&da8xx_fb_driver);
}
static void __exit da8xx_fb_cleanup(void)
{
printk(KERN_INFO “fb_cleanup\n”);
platform_driver_unregister(&da8xx_fb_driver);
}
module_init(da8xx_fb_init);
module_exit(da8xx_fb_cleanup);
MODULE_DESCRIPTION(“Framebuffer driver for TI da8xx/omap-l1xx”);
MODULE_AUTHOR(“Texas Instruments”);
MODULE_LICENSE(“GPL”);
`