/* * Driver for AT91 LCD Controller * * Copyright (C) 2007 Atmel Corporation * * 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.
*/
/** * atmel_lcdfb_alloc_video_memory - Allocate framebuffer memory * @sinfo: the frame buffer to allocate memory for * * This function is called only from the atmel_lcdfb_probe() * so no locking by fb_info->mm_lock around smem_len setting is needed.
*/ staticint atmel_lcdfb_alloc_video_memory(struct atmel_lcdfb_info *sinfo)
{ struct fb_info *info = sinfo->info; struct fb_var_screeninfo *var = &info->var; unsignedint smem_len;
/** * atmel_lcdfb_check_var - Validates a var passed in. * @var: frame buffer variable screen structure * @info: frame buffer structure that represents a single frame buffer * * Checks to see if the hardware supports the state requested by * var passed in. This function does not alter the hardware * state!!! This means the data stored in struct fb_info and * struct atmel_lcdfb_info do not change. This includes the var * inside of struct fb_info. Do NOT change these. This function * can be called on its own if we intent to only test a mode and * not actually set it. The stuff in modedb.c is a example of * this. If the var passed in is slightly off by what the * hardware can support then we alter the var PASSED in to what * we can do. If the hardware doesn't support mode change a * -EINVAL will be returned by the upper layers. You don't need * to implement this function then. If you hardware doesn't * support changing the resolution then this function is not * needed. In this case the driver would just provide a var that * represents the static state the screen is in. * * Returns negative errno on error, or zero on success.
*/ staticint atmel_lcdfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{ struct device *dev = info->device; struct atmel_lcdfb_info *sinfo = info->par; struct atmel_lcdfb_pdata *pdata = &sinfo->pdata; unsignedlong clk_value_khz;
if (!(var->pixclock && var->bits_per_pixel)) { /* choose a suitable mode if possible */ if (!atmel_lcdfb_choose_mode(var, info)) {
dev_err(dev, "needed value not specified\n"); return -EINVAL;
}
}
if (info->fix.smem_len) { unsignedint smem_len = (var->xres_virtual * var->yres_virtual
* ((var->bits_per_pixel + 7) / 8)); if (smem_len > info->fix.smem_len) {
dev_err(dev, "Frame buffer is too small (%u) for screen size (need at least %u)\n",
info->fix.smem_len, smem_len); return -EINVAL;
}
}
/** * atmel_lcdfb_set_par - Alters the hardware state. * @info: frame buffer structure that represents a single frame buffer * * Using the fb_var_screeninfo in fb_info we set the resolution * of the this particular framebuffer. This function alters the * par AND the fb_fix_screeninfo stored in fb_info. It doesn't * not alter var in fb_info since we are using that data. This * means we depend on the data in var inside fb_info to be * supported by the hardware. atmel_lcdfb_check_var is always called * before atmel_lcdfb_set_par to ensure this. Again if you can't * change the resolution you don't need this function. *
*/ staticint atmel_lcdfb_set_par(struct fb_info *info)
{ struct atmel_lcdfb_info *sinfo = info->par; struct atmel_lcdfb_pdata *pdata = &sinfo->pdata; unsignedlong hozval_linesz; unsignedlong value; unsignedlong clk_value_khz; unsignedlong bits_per_line; unsignedlong pix_factor = 2;
/* Initialize control register 2 */
value = pdata->default_lcdcon2;
if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
value |= ATMEL_LCDC_INVLINE_INVERTED; if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
value |= ATMEL_LCDC_INVFRAME_INVERTED;
switch (info->var.bits_per_pixel) { case 1: value |= ATMEL_LCDC_PIXELSIZE_1; break; case 2: value |= ATMEL_LCDC_PIXELSIZE_2; break; case 4: value |= ATMEL_LCDC_PIXELSIZE_4; break; case 8: value |= ATMEL_LCDC_PIXELSIZE_8; break; case 15: fallthrough; case 16: value |= ATMEL_LCDC_PIXELSIZE_16; break; case 24: value |= ATMEL_LCDC_PIXELSIZE_24; break; case 32: value |= ATMEL_LCDC_PIXELSIZE_32; break; default: BUG(); break;
}
dev_dbg(info->device, " * LCDCON2 = %08lx\n", value);
lcdc_writel(sinfo, ATMEL_LCDC_LCDCON2, value);
/* Vertical timing */
value = (info->var.vsync_len - 1) << ATMEL_LCDC_VPW_OFFSET;
value |= info->var.upper_margin << ATMEL_LCDC_VBP_OFFSET;
value |= info->var.lower_margin;
dev_dbg(info->device, " * LCDTIM1 = %08lx\n", value);
lcdc_writel(sinfo, ATMEL_LCDC_TIM1, value);
/* FIFO Threshold: Use formula from data sheet */
value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3);
lcdc_writel(sinfo, ATMEL_LCDC_FIFO, value);
/* Toggle LCD_MODE every frame */
lcdc_writel(sinfo, ATMEL_LCDC_MVAL, 0);
/** * atmel_lcdfb_setcolreg - Optional function. Sets a color register. * @regno: Which register in the CLUT we are programming * @red: The red value which can be up to 16 bits wide * @green: The green value which can be up to 16 bits wide * @blue: The blue value which can be up to 16 bits wide. * @transp: If supported the alpha value which can be up to 16 bits wide. * @info: frame buffer info structure * * Set a single color register. The values supplied have a 16 bit * magnitude which needs to be scaled in this function for the hardware. * Things to take into consideration are how many color registers, if * any, are supported with the current color visual. With truecolor mode * no color palettes are supported. Here a pseudo palette is created * which we store the value in pseudo_palette in struct fb_info. For * pseudocolor mode we have a limited color palette. To deal with this * we can program what color is displayed for a particular pixel value. * DirectColor is similar in that we can program each color field. If * we have a static colormap we don't need to implement this function. * * Returns negative errno on error, or zero on success. In an * ideal world, this would have been the case, but as it turns * out, the other drivers return 1 on failure, so that's what * we're going to do.
*/ staticint atmel_lcdfb_setcolreg(unsignedint regno, unsignedint red, unsignedint green, unsignedint blue, unsignedint transp, struct fb_info *info)
{ struct atmel_lcdfb_info *sinfo = info->par; struct atmel_lcdfb_pdata *pdata = &sinfo->pdata; unsignedint val;
u32 *pal; int ret = 1;
if (info->var.grayscale)
red = green = blue = (19595 * red + 38470 * green
+ 7471 * blue) >> 16;
switch (info->fix.visual) { case FB_VISUAL_TRUECOLOR: if (regno < 16) {
pal = info->pseudo_palette;
val = chan_to_field(red, &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue, &info->var.blue);
pal[regno] = val;
ret = 0;
} break;
case FB_VISUAL_PSEUDOCOLOR: if (regno < 256) { if (sinfo->config->have_intensity_bit) { /* old style I+BGR:555 */
val = ((red >> 11) & 0x001f);
val |= ((green >> 6) & 0x03e0);
val |= ((blue >> 1) & 0x7c00);
/* * TODO: intensity bit. Maybe something like * ~(red[10] ^ green[10] ^ blue[10]) & 1
*/
} else { /* new style BGR:565 / RGB:565 */ if (pdata->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) {
val = ((blue >> 11) & 0x001f);
val |= ((red >> 0) & 0xf800);
} else {
val = ((red >> 11) & 0x001f);
val |= ((blue >> 0) & 0xf800);
}
val |= ((green >> 5) & 0x07e0);
}
lcdc_writel(sinfo, ATMEL_LCDC_LUT(regno), val);
ret = 0;
} break;
case FB_VISUAL_MONO01: if (regno < 2) {
val = (regno == 0) ? 0x00 : 0x1F;
lcdc_writel(sinfo, ATMEL_LCDC_LUT(regno), val);
ret = 0;
} break;
switch (blank_mode) { case FB_BLANK_UNBLANK: case FB_BLANK_NORMAL:
atmel_lcdfb_start(sinfo); break; case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: break; case FB_BLANK_POWERDOWN:
atmel_lcdfb_stop(sinfo); break; default: return -EINVAL;
}
/* let fbcon do a soft blank for us */ return ((blank_mode == FB_BLANK_NORMAL) ? 1 : 0);
}
display_np = of_parse_phandle(np, "display", 0); if (!display_np) {
dev_err(dev, "failed to find display phandle\n"); return -ENOENT;
}
ret = of_property_read_u32(display_np, "bits-per-pixel", &var->bits_per_pixel); if (ret < 0) {
dev_err(dev, "failed to get property bits-per-pixel\n"); goto put_display_node;
}
ret = of_property_read_u32(display_np, "atmel,guard-time", &pdata->guard_time); if (ret < 0) {
dev_err(dev, "failed to get property atmel,guard-time\n"); goto put_display_node;
}
ret = of_property_read_u32(display_np, "atmel,lcdcon2", &pdata->default_lcdcon2); if (ret < 0) {
dev_err(dev, "failed to get property atmel,lcdcon2\n"); goto put_display_node;
}
ret = of_property_read_u32(display_np, "atmel,dmacon", &pdata->default_dmacon); if (ret < 0) {
dev_err(dev, "failed to get property bits-per-pixel\n"); goto put_display_node;
}
INIT_LIST_HEAD(&pdata->pwr_gpios); for (i = 0; i < gpiod_count(dev, "atmel,power-control"); i++) {
ret = -ENOMEM;
gpiod = devm_gpiod_get_index(dev, "atmel,power-control",
i, GPIOD_ASIS); if (IS_ERR(gpiod)) continue;
og = devm_kzalloc(dev, sizeof(*og), GFP_KERNEL); if (!og) goto put_display_node;
og->gpiod = gpiod;
is_gpio_power = true;
ret = gpiod_direction_output(gpiod, gpiod_is_active_low(gpiod)); if (ret) {
dev_err(dev, "set direction output gpio atmel,power-control[%d] failed\n", i); goto put_display_node;
}
list_add(&og->list, &pdata->pwr_gpios);
}
if (is_gpio_power)
pdata->atmel_lcdfb_power_control = atmel_lcdfb_power_control_gpio;
ret = atmel_lcdfb_get_of_wiring_modes(display_np); if (ret < 0) {
dev_err(dev, "invalid atmel,lcd-wiring-mode\n"); goto put_display_node;
}
pdata->lcd_wiring_mode = ret;
/* Some operations on the LCDC might sleep and
* require a preemptible task context */
INIT_WORK(&sinfo->task, atmel_lcdfb_task);
ret = atmel_lcdfb_init_fbinfo(sinfo); if (ret < 0) {
dev_err(dev, "init fbinfo failed: %d\n", ret); goto unregister_irqs;
}
ret = atmel_lcdfb_set_par(info); if (ret < 0) {
dev_err(dev, "set par failed: %d\n", ret); goto unregister_irqs;
}
dev_set_drvdata(dev, info);
/* * Tell the world that we're ready to go
*/
ret = register_framebuffer(info); if (ret < 0) {
dev_err(dev, "failed to register framebuffer device: %d\n", ret); goto reset_drvdata;
}
/* Power up the LCDC screen */
atmel_lcdfb_power_control(sinfo, 1);
dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %d\n",
info->node, info->fix.mmio_start, sinfo->mmio, sinfo->irq_base);
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.