/* * linux/drivers/video/pxafb.c * * Copyright (C) 1999 Eric A. Thomas. * Copyright (C) 2004 Jean-Frederic Clere. * Copyright (C) 2004 Ian Campbell. * Copyright (C) 2004 Jeff Lackey. * Based on sa1100fb.c Copyright (C) 1999 Eric A. Thomas * which in turn is * Based on acornfb.c Copyright (C) Russell King. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. * * Intel PXA250/210 LCD Controller Frame Buffer Driver * * Please direct your questions and comments on this driver to the following * email address: * * linux-arm-kernel@lists.arm.linux.org.uk * * Add support for overlay1 and overlay2 based on pxafb_overlay.c: * * Copyright (C) 2004, Intel Corporation * * 2003/08/27: <yu.tang@intel.com> * 2004/03/10: <stanley.cai@intel.com> * 2004/10/28: <yan.yin@intel.com> * * Copyright (C) 2006-2008 Marvell International Ltd. * All Rights Reserved
*/
local_irq_save(flags); /* * We need to handle two requests being made at the same time. * There are two important cases: * 1. When we are changing VT (C_REENABLE) while unblanking * (C_ENABLE) We must perform the unblanking, which will * do our REENABLE for us. * 2. When we are blanking, but immediately unblank before * we have blanked. We do the "REENABLE" thing here as * well, just to be sure.
*/ if (fbi->task_state == C_ENABLE && state == C_REENABLE)
state = (u_int) -1; if (fbi->task_state == C_DISABLE && state == C_ENABLE)
state = C_REENABLE;
/* * If inverse mode was selected, invert all the colours * rather than the register number. The register number * is what you poke into the framebuffer to produce the * colour you requested.
*/ if (fbi->cmap_inverse) {
red = 0xffff - red;
green = 0xffff - green;
blue = 0xffff - blue;
}
/* * If greyscale is true, then we convert the RGB value * to greyscale no matter what visual we are using.
*/ if (fbi->fb.var.grayscale)
red = green = blue = (19595 * red + 38470 * green +
7471 * blue) >> 16;
switch (fbi->fb.fix.visual) { case FB_VISUAL_TRUECOLOR: /* * 16-bit True Colour. We encode the RGB value * according to the RGB bitfield information.
*/ if (regno < 16) {
u32 *pal = fbi->fb.pseudo_palette;
val = chan_to_field(red, &fbi->fb.var.red);
val |= chan_to_field(green, &fbi->fb.var.green);
val |= chan_to_field(blue, &fbi->fb.var.blue);
pal[regno] = val;
ret = 0;
} break;
case FB_VISUAL_STATIC_PSEUDOCOLOR: case FB_VISUAL_PSEUDOCOLOR:
ret = pxafb_setpalettereg(regno, red, green, blue, trans, info); break;
}
/* calculate 4-bit BPP value for LCCR3 and OVLxC1 */ staticint pxafb_var_to_bpp(struct fb_var_screeninfo *var)
{ int bpp = -EINVAL;
switch (var->bits_per_pixel) { case 1: bpp = 0; break; case 2: bpp = 1; break; case 4: bpp = 2; break; case 8: bpp = 3; break; case 16: bpp = 4; break; case 24: switch (var_to_depth(var)) { case 18: bpp = 6; break; /* 18-bits/pixel packed */ case 19: bpp = 8; break; /* 19-bits/pixel packed */ case 24: bpp = 9; break;
} break; case 32: switch (var_to_depth(var)) { case 18: bpp = 5; break; /* 18-bits/pixel unpacked */ case 19: bpp = 7; break; /* 19-bits/pixel unpacked */ case 25: bpp = 10; break;
} break;
} return bpp;
}
/* * pxafb_var_to_lccr3(): * Convert a bits per pixel value to the correct bit pattern for LCCR3 * * NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an * implication of the acutal use of transparency bit, which we handle it * here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel * Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP. * * Transparency for palette pixel formats is not supported at the moment.
*/ static uint32_t pxafb_var_to_lccr3(struct fb_var_screeninfo *var)
{ int bpp = pxafb_var_to_bpp(var);
uint32_t lccr3;
if (bpp < 0) return 0;
lccr3 = LCCR3_BPP(bpp);
switch (var_to_depth(var)) { case 16: lccr3 |= var->transp.length ? LCCR3_PDFOR_3 : 0; break; case 18: lccr3 |= LCCR3_PDFOR_3; break; case 24: lccr3 |= var->transp.length ? LCCR3_PDFOR_2 : LCCR3_PDFOR_3; break; case 19: case 25: lccr3 |= LCCR3_PDFOR_0; break;
} return lccr3;
}
/* set the RGBT bitfields of fb_var_screeninf according to * var->bits_per_pixel and given depth
*/ staticvoid pxafb_set_pixfmt(struct fb_var_screeninfo *var, int depth)
{ if (depth == 0)
depth = var->bits_per_pixel;
#ifdef CONFIG_CPU_FREQ /* * pxafb_display_dma_period() * Calculate the minimum period (in picoseconds) between two DMA * requests for the LCD controller. If we hit this, it means we're * doing nothing but LCD DMA.
*/ staticunsignedint pxafb_display_dma_period(struct fb_var_screeninfo *var)
{ /* * Period = pixclock * bits_per_byte * bytes_per_transfer * / memory_bits_per_pixel;
*/ return var->pixclock * 8 * 16 / var->bits_per_pixel;
} #endif
/* * Select the smallest mode that allows the desired resolution to be * displayed. If desired parameters can be rounded up.
*/ staticstruct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach, struct fb_var_screeninfo *var)
{ struct pxafb_mode_info *mode = NULL; struct pxafb_mode_info *modelist = mach->modes; unsignedint best_x = 0xffffffff, best_y = 0xffffffff; unsignedint i;
/* make sure each line is aligned on word boundary */
line_length = var->xres * var->bits_per_pixel / 8;
line_length = ALIGN(line_length, 4);
var->xres = line_length * 8 / var->bits_per_pixel;
/* we don't support xpan, force xres_virtual to be equal to xres */
var->xres_virtual = var->xres;
/* check for limits */ if (var->xres > MAX_XRES || var->yres > MAX_YRES) return -EINVAL;
if (var->yres > var->yres_virtual) return -EINVAL;
return 0;
}
/* * pxafb_check_var(): * Get the video params out of 'var'. If a value doesn't fit, round it up, * if it's too big, return -EINVAL. * * Round up in the following order: bits_per_pixel, xres, * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, * bitfields, horizontal timing, vertical timing.
*/ staticint pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{ struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); struct pxafb_mach_info *inf = fbi->inf; int err;
if (inf->fixed_modes) { struct pxafb_mode_info *mode;
/* do a test conversion to BPP fields to check the color formats */
err = pxafb_var_to_bpp(var); if (err < 0) return err;
pxafb_set_pixfmt(var, var_to_depth(var));
err = pxafb_adjust_timing(fbi, var); if (err) return err;
#ifdef CONFIG_CPU_FREQ
pr_debug("pxafb: dma period = %d ps\n",
pxafb_display_dma_period(var)); #endif
return 0;
}
/* * pxafb_set_par(): * Set the user defined part of the display for the specified console
*/ staticint pxafb_set_par(struct fb_info *info)
{ struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); struct fb_var_screeninfo *var = &info->var;
if (var->bits_per_pixel >= 16)
fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; elseif (!fbi->cmap_static)
fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; else { /* * Some people have weird ideas about wanting static * pseudocolor maps. I suspect their user space * applications are broken.
*/
fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
}
/* Only take .xoffset, .yoffset and .vmode & FB_VMODE_YWRAP from what * was passed in and copy the rest from the old screeninfo.
*/
memcpy(&newvar, &fbi->fb.var, sizeof(newvar));
newvar.xoffset = var->xoffset;
newvar.yoffset = var->yoffset;
newvar.vmode &= ~FB_VMODE_YWRAP;
newvar.vmode |= var->vmode & FB_VMODE_YWRAP;
/* * pxafb_blank(): * Blank the display by setting all palette values to zero. Note, the * 16 bpp mode does not really use the palette, so this will not * blank the display in all modes.
*/ staticint pxafb_blank(int blank, struct fb_info *info)
{ struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); int i;
switch (blank) { case FB_BLANK_POWERDOWN: case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_NORMAL: if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) for (i = 0; i < fbi->palette_size; i++)
pxafb_setpalettereg(i, 0, 0, 0, 0, info);
pxafb_schedule_work(fbi, C_DISABLE); /* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */ break;
case FB_BLANK_UNBLANK: /* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */ if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
fb_set_cmap(&fbi->fb.cmap, info);
pxafb_schedule_work(fbi, C_ENABLE);
} return 0;
}
/* Depending on the enable status of overlay1/2, the DMA should be * updated from FDADRx (when disabled) or FBRx (when enabled).
*/ staticvoid overlay1fb_enable(struct pxafb_layer *ofb)
{ int enabled = lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN;
uint32_t fdadr1 = ofb->fbi->fdadr[DMA_OV1] | (enabled ? 0x1 : 0);
staticinlineint pxafb_overlay_supported(void)
{ if (cpu_is_pxa27x() || cpu_is_pxa3xx()) return 1;
return 0;
}
staticint pxafb_overlay_map_video_memory(struct pxafb_info *pxafb, struct pxafb_layer *ofb)
{ /* We assume that user will use at most video_mem_size for overlay fb, * anyway, it's useless to use 16bpp main plane and 24bpp overlay
*/
ofb->video_mem = alloc_pages_exact(PAGE_ALIGN(pxafb->video_mem_size),
GFP_KERNEL | __GFP_ZERO); if (ofb->video_mem == NULL) return -ENOMEM;
staticvoid pxafb_overlay_exit(struct pxafb_info *fbi)
{ int i;
if (!pxafb_overlay_supported()) return;
for (i = 0; i < 2; i++) { struct pxafb_layer *ofb = &fbi->overlay[i]; if (ofb->registered) { if (ofb->video_mem)
free_pages_exact(ofb->video_mem,
ofb->video_mem_size);
unregister_framebuffer(&ofb->fb);
}
}
} #else staticinlinevoid pxafb_overlay_init(struct pxafb_info *fbi) {} staticinlinevoid pxafb_overlay_exit(struct pxafb_info *fbi) {} #endif/* CONFIG_FB_PXA_OVERLAY */
/* * Calculate the PCD value from the clock rate (in picoseconds). * We take account of the PPCR clock setting. * From PXA Developer's Manual: * * PixelClock = LCLK * ------------- * 2 ( PCD + 1 ) * * PCD = LCLK * ------------- - 1 * 2(PixelClock) * * Where: * LCLK = LCD/Memory Clock * PCD = LCCR3[7:0] * * PixelClock here is in Hz while the pixclock argument given is the * period in picoseconds. Hence PixelClock = 1 / ( pixclock * 10^-12 ) * * The function get_lclk_frequency_10khz returns LCLK in units of * 10khz. Calling the result of this function lclk gives us the * following * * PCD = (lclk * 10^4 ) * ( pixclock * 10^-12 ) * -------------------------------------- - 1 * 2 * * Factoring the 10^4 and 10^-12 out gives 10^-8 == 1 / 100000000 as used below.
*/ staticinlineunsignedint get_pcd(struct pxafb_info *fbi, unsignedint pixclock)
{ unsignedlonglong pcd;
/* FIXME: Need to take into account Double Pixel Clock mode * (DPC) bit? or perhaps set it based on the various clock
* speeds */
pcd = (unsignedlonglong)(clk_get_rate(fbi->clk) / 10000);
pcd *= pixclock;
do_div(pcd, 100000000 * 2); /* no need for this, since we should subtract 1 anyway. they cancel */ /* pcd += 1; */ /* make up for integer math truncations */ return (unsignedint)pcd;
}
/* * Some touchscreens need hsync information from the video driver to * function correctly. We export it here. Note that 'hsync_time' is * the *reciprocal* of the hsync period in seconds.
*/ staticinlinevoid set_hsync_time(struct pxafb_info *fbi, unsignedint pcd)
{ unsignedlong htime;
int pxafb_smart_flush(struct fb_info *info)
{ struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb);
uint32_t prsr; int ret = 0;
/* disable controller until all registers are set up */
lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB);
/* 1. make it an even number of commands to align on 32-bit boundary * 2. add the interrupt command to the end of the chain so we can * keep track of the end of the transfer
*/
while (fbi->n_smart_cmds & 1)
fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_NOOP;
int pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds)
{ int i; struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb);
for (i = 0; i < n_cmds; i++, cmds++) { /* if it is a software delay, flush and delay */ if ((*cmds & 0xff00) == SMART_CMD_DELAY) {
pxafb_smart_flush(info);
mdelay(*cmds & 0xff); continue;
}
/* leave 2 commands for INTERRUPT and WAIT_FOR_SYNC */ if (fbi->n_smart_cmds == CMD_BUFF_SIZE - 8)
pxafb_smart_flush(info);
/* * If we have a dual scan LCD, we need to halve * the YRES parameter.
*/
lines_per_panel = var->yres; if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual)
lines_per_panel /= 2;
if (pcd) {
fbi->reg_lccr3 |= LCCR3_PixClkDiv(pcd);
set_hsync_time(fbi, pcd);
}
}
/* * pxafb_activate_var(): * Configures LCD Controller based on entries in var parameter. * Settings are only written to the controller if changes were made.
*/ staticint pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *fbi)
{
u_long flags;
/* * Only update the registers if the controller is enabled * and something has changed.
*/ if ((lcd_readl(fbi, LCCR0) != fbi->reg_lccr0) ||
(lcd_readl(fbi, LCCR1) != fbi->reg_lccr1) ||
(lcd_readl(fbi, LCCR2) != fbi->reg_lccr2) ||
(lcd_readl(fbi, LCCR3) != fbi->reg_lccr3) ||
(lcd_readl(fbi, LCCR4) != fbi->reg_lccr4) ||
(lcd_readl(fbi, FDADR0) != fbi->fdadr[0]) ||
((fbi->lccr0 & LCCR0_SDS) &&
(lcd_readl(fbi, FDADR1) != fbi->fdadr[1])))
pxafb_schedule_work(fbi, C_REENABLE);
return 0;
}
/* * NOTE! The following functions are purely helpers for set_ctrlr_state. * Do not call them directly; set_ctrlr_state does the correct serialisation * to ensure that things happen in the right way 100% of time time. * -- rmk
*/ staticinlinevoid __pxafb_backlight_power(struct pxafb_info *fbi, int on)
{
pr_debug("pxafb: backlight o%s\n", on ? "n" : "ff");
if (fbi->backlight_power)
fbi->backlight_power(on);
}
staticinlinevoid __pxafb_lcd_power(struct pxafb_info *fbi, int on)
{
pr_debug("pxafb: LCD power o%s\n", on ? "n" : "ff");
if (fbi->lcd_power)
fbi->lcd_power(on, &fbi->fb.var);
if (fbi->lcd_supply && fbi->lcd_supply_enabled != on) { int ret;
if (on)
ret = regulator_enable(fbi->lcd_supply); else
ret = regulator_disable(fbi->lcd_supply);
if (ret < 0)
pr_warn("Unable to %s LCD supply regulator: %d\n",
on ? "enable" : "disable", ret); else
fbi->lcd_supply_enabled = on;
}
}
/* * This function must be called from task context only, since it will * sleep when disabling the LCD controller, or if we get two contending * processes trying to alter state.
*/ staticvoid set_ctrlr_state(struct pxafb_info *fbi, u_int state)
{
u_int old_state;
mutex_lock(&fbi->ctrlr_lock);
old_state = fbi->state;
/* * Hack around fbcon initialisation.
*/ if (old_state == C_STARTUP && state == C_REENABLE)
state = C_ENABLE;
switch (state) { case C_DISABLE_CLKCHANGE: /* * Disable controller for clock change. If the * controller is already disabled, then do nothing.
*/ if (old_state != C_DISABLE && old_state != C_DISABLE_PM) {
fbi->state = state; /* TODO __pxafb_lcd_power(fbi, 0); */
pxafb_disable_controller(fbi);
} break;
case C_DISABLE_PM: case C_DISABLE: /* * Disable controller
*/ if (old_state != C_DISABLE) {
fbi->state = state;
__pxafb_backlight_power(fbi, 0);
__pxafb_lcd_power(fbi, 0); if (old_state != C_DISABLE_CLKCHANGE)
pxafb_disable_controller(fbi);
} break;
case C_ENABLE_CLKCHANGE: /* * Enable the controller after clock change. Only * do this if we were disabled for the clock change.
*/ if (old_state == C_DISABLE_CLKCHANGE) {
fbi->state = C_ENABLE;
pxafb_enable_controller(fbi); /* TODO __pxafb_lcd_power(fbi, 1); */
} break;
case C_REENABLE: /* * Re-enable the controller only if it was already * enabled. This is so we reprogram the control * registers.
*/ if (old_state == C_ENABLE) {
__pxafb_lcd_power(fbi, 0);
pxafb_disable_controller(fbi);
pxafb_enable_controller(fbi);
__pxafb_lcd_power(fbi, 1);
} break;
case C_ENABLE_PM: /* * Re-enable the controller after PM. This is not * perfect - think about the case where we were doing * a clock change, and we suspended half-way through.
*/ if (old_state != C_DISABLE_PM) break;
fallthrough;
case C_ENABLE: /* * Power up the LCD screen, enable controller, and * turn on the backlight.
*/ if (old_state != C_ENABLE) {
fbi->state = C_ENABLE;
pxafb_enable_controller(fbi);
__pxafb_lcd_power(fbi, 1);
__pxafb_backlight_power(fbi, 1);
} break;
}
mutex_unlock(&fbi->ctrlr_lock);
}
/* * Our LCD controller task (which is called when we blank or unblank) * via keventd.
*/ staticvoid pxafb_task(struct work_struct *work)
{ struct pxafb_info *fbi =
container_of(work, struct pxafb_info, task);
u_int state = xchg(&fbi->task_state, -1);
set_ctrlr_state(fbi, state);
}
#ifdef CONFIG_CPU_FREQ /* * CPU clock speed change handler. We need to adjust the LCD timing * parameters when the CPU clock is adjusted by the power management * subsystem. * * TODO: Determine why f->new != 10*get_lclk_frequency_10khz()
*/ staticint
pxafb_freq_transition(struct notifier_block *nb, unsignedlong val, void *data)
{ struct pxafb_info *fbi = TO_INF(nb, freq_transition); /* TODO struct cpufreq_freqs *f = data; */
u_int pcd;
switch (val) { case CPUFREQ_PRECHANGE: #ifdef CONFIG_FB_PXA_OVERLAY if (!(fbi->overlay[0].usage || fbi->overlay[1].usage)) #endif
set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE); break;
#ifdef CONFIG_PM /* * Power management hooks. Note that we won't be called from IRQ context, * unlike the blank functions above, so we may sleep.
*/ staticint pxafb_suspend(struct device *dev)
{ struct pxafb_info *fbi = dev_get_drvdata(dev);
/* decide video memory size as follows: * 1. default to mode of maximum resolution * 2. allow platform to override * 3. allow module parameter to override
*/ for (i = 0, m = &inf->modes[0]; i < inf->num_modes; i++, m++)
fbi->video_mem_size = max_t(size_t, fbi->video_mem_size,
m->xres * m->yres * m->bpp / 8);
if (inf->video_mem_size > fbi->video_mem_size)
fbi->video_mem_size = inf->video_mem_size;
if (video_mem_size > fbi->video_mem_size)
fbi->video_mem_size = video_mem_size;
}
/* Alloc the pxafb_info and pseudo_palette in one step */
fbi = devm_kzalloc(dev, sizeof(struct pxafb_info) + sizeof(u32) * 16,
GFP_KERNEL); if (!fbi) return ERR_PTR(-ENOMEM);
fbi->dev = dev;
fbi->inf = inf;
fbi->clk = devm_clk_get(dev, NULL); if (IS_ERR(fbi->clk)) return ERR_CAST(fbi->clk);
dev_dbg(dev, "options are \"%s\"\n", options ? options : "null");
/* could be made table driven or similar?... */ while ((this_opt = strsep(&options, ",")) != NULL) {
ret = parse_opt(dev, this_opt, inf); if (ret) return ret;
} return 0;
}
#ifdef DEBUG_VAR /* Check for various illegal bit-combinations. Currently only
* a warning is given. */ staticvoid pxafb_check_options(struct device *dev, struct pxafb_mach_info *inf)
{ if (inf->lcd_conn) return;
if (inf->lccr0 & LCCR0_INVALID_CONFIG_MASK)
dev_warn(dev, "machine LCCR0 setting contains " "illegal bits: %08x\n",
inf->lccr0 & LCCR0_INVALID_CONFIG_MASK); if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK)
dev_warn(dev, "machine LCCR3 setting contains " "illegal bits: %08x\n",
inf->lccr3 & LCCR3_INVALID_CONFIG_MASK); if (inf->lccr0 & LCCR0_DPD &&
((inf->lccr0 & LCCR0_PAS) != LCCR0_Pas ||
(inf->lccr0 & LCCR0_SDS) != LCCR0_Sngl ||
(inf->lccr0 & LCCR0_CMS) != LCCR0_Mono))
dev_warn(dev, "Double Pixel Data (DPD) mode is " "only valid in passive mono" " single panel mode\n"); if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Act &&
(inf->lccr0 & LCCR0_SDS) == LCCR0_Dual)
dev_warn(dev, "Dual panel only valid in passive mode\n"); if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas &&
(inf->modes->upper_margin || inf->modes->lower_margin))
dev_warn(dev, "Upper and lower margins must be 0 in " "passive mode\n");
} #else #define pxafb_check_options(...) do {} while (0) #endif
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) 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 staticstruct pxafb_mach_info *of_pxafb_of_mach_info(struct device *dev)
{ return NULL;
} #endif
fbi->lcd_supply = devm_regulator_get_optional(&dev->dev, "lcd"); if (IS_ERR(fbi->lcd_supply)) { if (PTR_ERR(fbi->lcd_supply) == -EPROBE_DEFER) return -EPROBE_DEFER;
fbi->lcd_supply = NULL;
}
fbi->mmio_base = devm_platform_ioremap_resource(dev, 0); if (IS_ERR(fbi->mmio_base)) {
dev_err(&dev->dev, "failed to get I/O memory\n");
ret = PTR_ERR(fbi->mmio_base); goto failed;
}
fbi->dma_buff_size = PAGE_ALIGN(sizeof(struct pxafb_dma_buff));
fbi->dma_buff = dma_alloc_coherent(fbi->dev, fbi->dma_buff_size,
&fbi->dma_buff_phys, GFP_KERNEL); if (fbi->dma_buff == NULL) {
dev_err(&dev->dev, "failed to allocate memory for DMA\n");
ret = -ENOMEM; goto failed;
}
ret = pxafb_init_video_memory(fbi); if (ret) {
dev_err(&dev->dev, "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM; goto failed_free_dma;
}
irq = platform_get_irq(dev, 0); if (irq < 0) {
ret = -ENODEV; goto failed_free_mem;
}
ret = devm_request_irq(&dev->dev, irq, pxafb_handle_irq, 0, "LCD", fbi); if (ret) {
dev_err(&dev->dev, "request_irq failed: %d\n", ret);
ret = -EBUSY; goto failed_free_mem;
}
ret = pxafb_smart_init(fbi); if (ret) {
dev_err(&dev->dev, "failed to initialize smartpanel\n"); goto failed_free_mem;
}
/* * This makes sure that our colour bitfield * descriptors are correctly initialised.
*/
ret = pxafb_check_var(&fbi->fb.var, &fbi->fb); if (ret) {
dev_err(&dev->dev, "failed to get suitable mode\n"); goto failed_free_mem;
}
ret = pxafb_set_par(&fbi->fb); if (ret) {
dev_err(&dev->dev, "Failed to set parameters\n"); goto failed_free_mem;
}
platform_set_drvdata(dev, fbi);
ret = register_framebuffer(&fbi->fb); if (ret < 0) {
dev_err(&dev->dev, "Failed to register framebuffer device: %d\n", ret); goto failed_free_cmap;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.