📄 atmel_lcdfb.c
字号:
var->transp.offset = var->transp.length = 0; var->xoffset = var->yoffset = 0; if (info->fix.smem_len) { unsigned int smem_len = (var->xres_virtual * var->yres_virtual * ((var->bits_per_pixel + 7) / 8)); if (smem_len > info->fix.smem_len) return -EINVAL; } /* Saturate vertical and horizontal timings at maximum values */ var->vsync_len = min_t(u32, var->vsync_len, (ATMEL_LCDC_VPW >> ATMEL_LCDC_VPW_OFFSET) + 1); var->upper_margin = min_t(u32, var->upper_margin, ATMEL_LCDC_VBP >> ATMEL_LCDC_VBP_OFFSET); var->lower_margin = min_t(u32, var->lower_margin, ATMEL_LCDC_VFP); var->right_margin = min_t(u32, var->right_margin, (ATMEL_LCDC_HFP >> ATMEL_LCDC_HFP_OFFSET) + 1); var->hsync_len = min_t(u32, var->hsync_len, (ATMEL_LCDC_HPW >> ATMEL_LCDC_HPW_OFFSET) + 1); var->left_margin = min_t(u32, var->left_margin, ATMEL_LCDC_HBP + 1); /* Some parameters can't be zero */ var->vsync_len = max_t(u32, var->vsync_len, 1); var->right_margin = max_t(u32, var->right_margin, 1); var->hsync_len = max_t(u32, var->hsync_len, 1); var->left_margin = max_t(u32, var->left_margin, 1); switch (var->bits_per_pixel) { case 1: case 2: case 4: case 8: var->red.offset = var->green.offset = var->blue.offset = 0; var->red.length = var->green.length = var->blue.length = var->bits_per_pixel; break; case 15: case 16: if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) { /* RGB:565 mode */ var->red.offset = 11; var->blue.offset = 0; var->green.length = 6; } else if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB555) { var->red.offset = 10; var->blue.offset = 0; var->green.length = 5; } else { /* BGR:555 mode */ var->red.offset = 0; var->blue.offset = 10; var->green.length = 5; } var->green.offset = 5; var->red.length = var->blue.length = 5; break; case 32: var->transp.offset = 24; var->transp.length = 8; /* fall through */ case 24: if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) { /* RGB:888 mode */ var->red.offset = 16; var->blue.offset = 0; } else { /* BGR:888 mode */ var->red.offset = 0; var->blue.offset = 16; } var->green.offset = 8; var->red.length = var->green.length = var->blue.length = 8; break; default: dev_err(dev, "color depth %d not supported\n", var->bits_per_pixel); return -EINVAL; } return 0;}/* * LCD reset sequence */static void atmel_lcdfb_reset(struct atmel_lcdfb_info *sinfo){ might_sleep(); atmel_lcdfb_stop(sinfo); atmel_lcdfb_start(sinfo);}/** * 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. * */static int atmel_lcdfb_set_par(struct fb_info *info){ struct atmel_lcdfb_info *sinfo = info->par; unsigned long hozval_linesz; unsigned long value; unsigned long clk_value_khz; unsigned long bits_per_line; might_sleep(); dev_dbg(info->device, "%s:\n", __func__); dev_dbg(info->device, " * resolution: %ux%u (%ux%u virtual)\n", info->var.xres, info->var.yres, info->var.xres_virtual, info->var.yres_virtual); atmel_lcdfb_stop_nowait(sinfo); if (info->var.bits_per_pixel == 1) info->fix.visual = FB_VISUAL_MONO01; else if (info->var.bits_per_pixel <= 8) info->fix.visual = FB_VISUAL_PSEUDOCOLOR; else info->fix.visual = FB_VISUAL_TRUECOLOR; bits_per_line = info->var.xres_virtual * info->var.bits_per_pixel; info->fix.line_length = DIV_ROUND_UP(bits_per_line, 8); /* Re-initialize the DMA engine... */ dev_dbg(info->device, " * update DMA engine\n"); atmel_lcdfb_update_dma(info, &info->var); /* ...set frame size and burst length = 8 words (?) */ value = (info->var.yres * info->var.xres * info->var.bits_per_pixel) / 32; value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET); lcdc_writel(sinfo, ATMEL_LCDC_DMAFRMCFG, value); /* Now, the LCDC core... */ /* Set pixel clock */ clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; value = DIV_ROUND_UP(clk_value_khz, PICOS2KHZ(info->var.pixclock)); if (value < 2) { dev_notice(info->device, "Bypassing pixel clock divider\n"); lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS); } else { value = (value / 2) - 1; dev_dbg(info->device, " * programming CLKVAL = 0x%08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, value << ATMEL_LCDC_CLKVAL_OFFSET); info->var.pixclock = KHZ2PICOS(clk_value_khz / (2 * (value + 1))); dev_dbg(info->device, " updated pixclk: %lu KHz\n", PICOS2KHZ(info->var.pixclock)); } /* Initialize control register 2 */ value = sinfo->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: /* fall through */ 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); /* Horizontal timing */ value = (info->var.right_margin - 1) << ATMEL_LCDC_HFP_OFFSET; value |= (info->var.hsync_len - 1) << ATMEL_LCDC_HPW_OFFSET; value |= (info->var.left_margin - 1); dev_dbg(info->device, " * LCDTIM2 = %08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value); /* Horizontal value (aka line size) */ hozval_linesz = compute_hozval(info->var.xres, lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2)); /* Display size */ value = (hozval_linesz - 1) << ATMEL_LCDC_HOZVAL_OFFSET; value |= info->var.yres - 1; dev_dbg(info->device, " * LCDFRMCFG = %08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_LCDFRMCFG, 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); /* Disable all interrupts */ lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); /* Enable FIFO & DMA errors */ lcdc_writel(sinfo, ATMEL_LCDC_IER, ATMEL_LCDC_UFLWI | ATMEL_LCDC_OWRI | ATMEL_LCDC_MERI); /* ...wait for DMA engine to become idle... */ while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY) msleep(10); atmel_lcdfb_start(sinfo); dev_dbg(info->device, " * DONE\n"); return 0;}static inline unsigned int chan_to_field(unsigned int chan, const struct fb_bitfield *bf){ chan &= 0xffff; chan >>= 16 - bf->length; return chan << bf->offset;}/** * 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 psuedo 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. */static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, unsigned int transp, struct fb_info *info){ struct atmel_lcdfb_info *sinfo = info->par; unsigned int 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) { 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 */ 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; } return ret;}static int atmel_lcdfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info){ dev_dbg(info->device, "%s\n", __func__); atmel_lcdfb_update_dma(info, var); return 0;}static struct fb_ops atmel_lcdfb_ops = { .owner = THIS_MODULE, .fb_check_var = atmel_lcdfb_check_var, .fb_set_par = atmel_lcdfb_set_par, .fb_setcolreg = atmel_lcdfb_setcolreg, .fb_pan_display = atmel_lcdfb_pan_display, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit,};static irqreturn_t atmel_lcdfb_interrupt(int irq, void *dev_id){ struct fb_info *info = dev_id; struct atmel_lcdfb_info *sinfo = info->par; u32 status; status = lcdc_readl(sinfo, ATMEL_LCDC_ISR); if (status & ATMEL_LCDC_UFLWI) { dev_warn(info->device, "FIFO underflow %#x\n", status); /* reset DMA and FIFO to avoid screen shifting */ schedule_work(&sinfo->task); } lcdc_writel(sinfo, ATMEL_LCDC_ICR, status); return IRQ_HANDLED;}/* * LCD controller task (to reset the LCD) */static void atmel_lcdfb_task(struct work_struct *work){ struct atmel_lcdfb_info *sinfo = container_of(work, struct atmel_lcdfb_info, task); atmel_lcdfb_reset(sinfo);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -