Can't get device clock in FB driver.

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”);

`

Patches/diffs are much better for questions like this.

A diff wouldn’t make much sense as I just copied needed functions from board-am335xevm.c and devices.c to the device.c and made it a loadable module.
I haven’t changed anything in da8xx-fb.c except the names to not conflict with the original da8xx-fb.

I’ve replaced

fb_clk = clk_get(&device->dev, NULL);
with

`
fb_clk = clk_get(NULL, “dpll_disp_ck”);

`
and the clock is enabled.
But now the driver crashes when trying to read the LCD_PID_REG. :S

`

[ 357.389283] fb_probe: reading LCD PID
[ 357.393152] Unhandled fault: external abort on non-linefetch (0x1028) at 0xfa30e000
[ 357.401143] Internal error: : 1028 [#1] SMP
[ 357.405506] Modules linked in: edt_driver(+) edt_device autofs4 [last unloaded: scsi_wait_scan]
[ 357.414614] CPU: 0 Not tainted (3.1.10 #2)
[ 357.419268] PC is at fb_probe+0x10c/0x8c8 [edt_driver]
[ 357.424642] LR is at console_unlock+0x1e4/0x200
[ 357.429369] pc : [] lr : [] psr: 60000013
[ 357.429378] sp : c1983d98 ip : c1983cb0 fp : c1983ddc
[ 357.441344] r10: c07eb850 r9 : bf00f180 r8 : bf00f330
[ 357.446791] r7 : bf014b18 r6 : bf014b18 r5 : 00000000 r4 : bf014db0
[ 357.453598] r3 : fa30e000 r2 : c08129cc r1 : 4c100102 r0 : 0000002f
[ 357.460407] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
[ 357.467849] Control: 10c5387d Table: 8e220019 DAC: 00000015
[ 357.473842] Process insmod (pid: 1472, stack limit = 0xc19822f8)
[ 357.480105] Stack: (0xc1983d98 to 0xc1984000)
[ 357.484649] 3d80: 00000000 bf00f190
[ 357.493183] 3da0: 00000000 bf014b18 00000001 00001000 c1983dcc bf00f188 bf00f1bc bf014b18
[ 357.501717] 3dc0: bf014b18 00000001 00000024 0000001c c1983dec c1983de0 c02e3868 bf013ca8
[ 357.510251] 3de0: c1983e0c c1983df0 c02e2428 c02e3850 bf00f188 bf00f1bc bf014b18 00000000
[ 357.518785] 3e00: c1983e2c c1983e10 c02e2554 c02e235c 00000000 bf014b18 c1983e30 c02e24e4
[ 357.527319] 3e20: c1983e54 c1983e30 c02e1538 c02e24f0 cf803e48 cec1ff40 bf014b18 ce32f420
[ 357.535853] 3e40: c0834980 00000000 c1983e64 c1983e58 c02e2070 c02e14e4 c1983e94 c1983e68
[ 357.544386] 3e60: c02e1c50 c02e2054 bf01497f c1983e78 bf014b18 bf017000 00000000 c18cd080
[ 357.552920] 3e80: 00000001 0000001c c1983ebc c1983e98 c02e2ad0 c02e1b9c bf014c38 bf017000
[ 357.561453] 3ea0: 00000000 c18cd080 00000001 0000001c c1983ecc c1983ec0 c02e3cb4 c02e2a24
[ 357.569987] 3ec0: c1983edc c1983ed0 bf01701c c02e3c6c c1983f14 c1983ee0 c00088dc bf01700c
[ 357.578520] 3ee0: bf014c38 bf014c80 00000001 c18cd080 bf014c38 bf014c80 00000001 c18cd080
[ 357.587054] 3f00: 00000001 00000024 c1983fa4 c1983f18 c00895b4 c0008848 bf014c44 c013859c
[ 357.595589] 3f20: c1983f64 c008670c 00000000 0101b008 bf014d9c c0520410 d0904000 000291a9
[ 357.604123] 3f40: d0920b70 d092090b d092cc30 ce629900 00001dc0 00002280 00000000 00000000
[ 357.612655] 3f60: 00000037 00000038 00000021 00000000 0000001a 00000000 c1983fa4 00008ddc
[ 357.621189] 3f80: 0101b008 000291a9 00000080 c000dcc4 c1982000 00000000 00000000 c1983fa8
[ 357.629723] 3fa0: c000da60 c0087e80 00008ddc 0101b008 40300008 000291a9 0101b008 00000001
[ 357.638256] 3fc0: 00008ddc 0101b008 000291a9 00000080 00040000 00000000 00000003 00000000
[ 357.646789] 3fe0: 40294fc0 beed0628 00008c30 40294fd0 60000010 40300008 00000000 00000000
[ 357.655355] [] (fb_probe+0x10c/0x8c8 [edt_driver]) from [] (platform_drv_probe+0x24/0x28)
[ 357.665709] [] (platform_drv_probe+0x24/0x28) from [] (driver_probe_device+0xd8/0x194)
[ 357.675786] [] (driver_probe_device+0xd8/0x194) from [] (__driver_attach+0x70/0x94)
[ 357.685590] [] (__driver_attach+0x70/0x94) from [] (bus_for_each_dev+0x60/0x9c)
[ 357.695033] [] (bus_for_each_dev+0x60/0x9c) from [] (driver_attach+0x28/0x30)
[ 357.704292] [] (driver_attach+0x28/0x30) from [] (bus_add_driver+0xc0/0x23c)
[ 357.713461] [] (bus_add_driver+0xc0/0x23c) from [] (driver_register+0xb8/0x148)
[ 357.722905] [] (driver_register+0xb8/0x148) from [] (platform_driver_register+0x54/0x68)
[ 357.733170] [] (platform_driver_register+0x54/0x68) from [] (da8xx_fb_init+0x1c/0x28 [edt_driver])
[ 357.744344] [] (da8xx_fb_init+0x1c/0x28 [edt_driver]) from [] (do_one_initcall+0xa0/0x170)
[ 357.754800] [] (do_one_initcall+0xa0/0x170) from [] (sys_init_module+0x1740/0x18dc)
[ 357.764614] [] (sys_init_module+0x1740/0x18dc) from [] (ret_fast_syscall+0x0/0x30)
[ 357.774328] Code: e59f0760 eb53db9b e5943000 e59f1758 (e5932000)
[ 357.780974] —[ end trace 20a56052f149967c ]—

`

This is obvious behavior, can you try,

Either,

Change the line from the file arch/arm/mach-omap2/clock33xx_data.c

- CLK("da8xx_lcdc.0", NULL, &lcdc_fck, CK_AM33XX)
+ CLK("edt_lcdc.0", NULL, &lcdc_fck, CK_AM33XX)

OR

Change the driver to

- fb_clk = clk_get(&device->dev, NULL);
+ fb_clk = clk_get(NULL, "lcdc_fck");

Thanks,
Vaibhav

Thanks for the reply!

If I try:

fb_clk = clk_get(NULL,"lcdc_fck"); if(IS_ERR(fb_clk)) { printk(KERN_INFO "Can not get device clock\n"); }
It returns with the error “Can not get device clock”.

I’ve documented a lot of stuff that might help you with the LCDC and the AM335x here: http://www.cemetech.net/forum/viewtopic.php?t=7814

Thanks, Kerm. I’ve already read your post. Really well writing! I just don’t understand that I have to recompile the kernel to test my LCD… Why can’t I access the lcdc_fck?