atmel_lcdfb.c

来自「linux 内核源代码」· C语言 代码 · 共 806 行 · 第 1/2 页

C
806
字号
 *      @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);	lcdc_writel(sinfo, ATMEL_LCDC_IDR, status);	return IRQ_HANDLED;}static int __init atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo){	struct fb_info *info = sinfo->info;	int ret = 0;	memset_io(info->screen_base, 0, info->fix.smem_len);	info->var.activate |= FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW;	dev_info(info->device,	       "%luKiB frame buffer at %08lx (mapped at %p)\n",	       (unsigned long)info->fix.smem_len / 1024,	       (unsigned long)info->fix.smem_start,	       info->screen_base);	/* Allocate colormap */	ret = fb_alloc_cmap(&info->cmap, 256, 0);	if (ret < 0)		dev_err(info->device, "Alloc color map failed\n");	return ret;}static void atmel_lcdfb_start_clock(struct atmel_lcdfb_info *sinfo){	if (sinfo->bus_clk)		clk_enable(sinfo->bus_clk);	clk_enable(sinfo->lcdc_clk);}static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo){	if (sinfo->bus_clk)		clk_disable(sinfo->bus_clk);	clk_disable(sinfo->lcdc_clk);}static int __init atmel_lcdfb_probe(struct platform_device *pdev){	struct device *dev = &pdev->dev;	struct fb_info *info;	struct atmel_lcdfb_info *sinfo;	struct atmel_lcdfb_info *pdata_sinfo;	struct resource *regs = NULL;	struct resource *map = NULL;	int ret;	dev_dbg(dev, "%s BEGIN\n", __func__);	ret = -ENOMEM;	info = framebuffer_alloc(sizeof(struct atmel_lcdfb_info), dev);	if (!info) {		dev_err(dev, "cannot allocate memory\n");		goto out;	}	sinfo = info->par;	if (dev->platform_data) {		pdata_sinfo = (struct atmel_lcdfb_info *)dev->platform_data;		sinfo->default_bpp = pdata_sinfo->default_bpp;		sinfo->default_dmacon = pdata_sinfo->default_dmacon;		sinfo->default_lcdcon2 = pdata_sinfo->default_lcdcon2;		sinfo->default_monspecs = pdata_sinfo->default_monspecs;		sinfo->atmel_lcdfb_power_control = pdata_sinfo->atmel_lcdfb_power_control;		sinfo->guard_time = pdata_sinfo->guard_time;	} else {		dev_err(dev, "cannot get default configuration\n");		goto free_info;	}	sinfo->info = info;	sinfo->pdev = pdev;	strcpy(info->fix.id, sinfo->pdev->name);	info->flags = ATMEL_LCDFB_FBINFO_DEFAULT;	info->pseudo_palette = sinfo->pseudo_palette;	info->fbops = &atmel_lcdfb_ops;	memcpy(&info->monspecs, sinfo->default_monspecs, sizeof(info->monspecs));	info->fix = atmel_lcdfb_fix;	/* Enable LCDC Clocks */	if (cpu_is_at91sam9261() || cpu_is_at32ap7000()) {		sinfo->bus_clk = clk_get(dev, "hck1");		if (IS_ERR(sinfo->bus_clk)) {			ret = PTR_ERR(sinfo->bus_clk);			goto free_info;		}	}	sinfo->lcdc_clk = clk_get(dev, "lcdc_clk");	if (IS_ERR(sinfo->lcdc_clk)) {		ret = PTR_ERR(sinfo->lcdc_clk);		goto put_bus_clk;	}	atmel_lcdfb_start_clock(sinfo);	ret = fb_find_mode(&info->var, info, NULL, info->monspecs.modedb,			info->monspecs.modedb_len, info->monspecs.modedb,			sinfo->default_bpp);	if (!ret) {		dev_err(dev, "no suitable video mode found\n");		goto stop_clk;	}	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);	if (!regs) {		dev_err(dev, "resources unusable\n");		ret = -ENXIO;		goto stop_clk;	}	sinfo->irq_base = platform_get_irq(pdev, 0);	if (sinfo->irq_base < 0) {		dev_err(dev, "unable to get irq\n");		ret = sinfo->irq_base;		goto stop_clk;	}	/* Initialize video memory */	map = platform_get_resource(pdev, IORESOURCE_MEM, 1);	if (map) {		/* use a pre-allocated memory buffer */		info->fix.smem_start = map->start;		info->fix.smem_len = map->end - map->start + 1;		if (!request_mem_region(info->fix.smem_start,					info->fix.smem_len, pdev->name)) {			ret = -EBUSY;			goto stop_clk;		}		info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);		if (!info->screen_base)			goto release_intmem;	} else {		/* alocate memory buffer */		ret = atmel_lcdfb_alloc_video_memory(sinfo);		if (ret < 0) {			dev_err(dev, "cannot allocate framebuffer: %d\n", ret);			goto stop_clk;		}	}	/* LCDC registers */	info->fix.mmio_start = regs->start;	info->fix.mmio_len = regs->end - regs->start + 1;	if (!request_mem_region(info->fix.mmio_start,				info->fix.mmio_len, pdev->name)) {		ret = -EBUSY;		goto free_fb;	}	sinfo->mmio = ioremap(info->fix.mmio_start, info->fix.mmio_len);	if (!sinfo->mmio) {		dev_err(dev, "cannot map LCDC registers\n");		goto release_mem;	}	/* interrupt */	ret = request_irq(sinfo->irq_base, atmel_lcdfb_interrupt, 0, pdev->name, info);	if (ret) {		dev_err(dev, "request_irq failed: %d\n", ret);		goto unmap_mmio;	}	ret = atmel_lcdfb_init_fbinfo(sinfo);	if (ret < 0) {		dev_err(dev, "init fbinfo failed: %d\n", ret);		goto unregister_irqs;	}	/*	 * This makes sure that our colour bitfield	 * descriptors are correctly initialised.	 */	atmel_lcdfb_check_var(&info->var, info);	ret = fb_set_var(info, &info->var);	if (ret) {		dev_warn(dev, "unable to set display parameters\n");		goto free_cmap;	}	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 free_cmap;	}	/* Power up the LCDC screen */	if (sinfo->atmel_lcdfb_power_control)		sinfo->atmel_lcdfb_power_control(1);	dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %lu\n",		       info->node, info->fix.mmio_start, sinfo->mmio, sinfo->irq_base);	return 0;free_cmap:	fb_dealloc_cmap(&info->cmap);unregister_irqs:	free_irq(sinfo->irq_base, info);unmap_mmio:	iounmap(sinfo->mmio);release_mem: 	release_mem_region(info->fix.mmio_start, info->fix.mmio_len);free_fb:	if (map)		iounmap(info->screen_base);	else		atmel_lcdfb_free_video_memory(sinfo);release_intmem:	if (map)		release_mem_region(info->fix.smem_start, info->fix.smem_len);stop_clk:	atmel_lcdfb_stop_clock(sinfo);	clk_put(sinfo->lcdc_clk);put_bus_clk:	if (sinfo->bus_clk)		clk_put(sinfo->bus_clk);free_info:	framebuffer_release(info);out:	dev_dbg(dev, "%s FAILED\n", __func__);	return ret;}static int __exit atmel_lcdfb_remove(struct platform_device *pdev){	struct device *dev = &pdev->dev;	struct fb_info *info = dev_get_drvdata(dev);	struct atmel_lcdfb_info *sinfo = info->par;	if (!sinfo)		return 0;	if (sinfo->atmel_lcdfb_power_control)		sinfo->atmel_lcdfb_power_control(0);	unregister_framebuffer(info);	atmel_lcdfb_stop_clock(sinfo);	clk_put(sinfo->lcdc_clk);	if (sinfo->bus_clk)		clk_put(sinfo->bus_clk);	fb_dealloc_cmap(&info->cmap);	free_irq(sinfo->irq_base, info);	iounmap(sinfo->mmio); 	release_mem_region(info->fix.mmio_start, info->fix.mmio_len);	if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) {		iounmap(info->screen_base);		release_mem_region(info->fix.smem_start, info->fix.smem_len);	} else {		atmel_lcdfb_free_video_memory(sinfo);	}	dev_set_drvdata(dev, NULL);	framebuffer_release(info);	return 0;}static struct platform_driver atmel_lcdfb_driver = {	.remove		= __exit_p(atmel_lcdfb_remove),	.driver		= {		.name	= "atmel_lcdfb",		.owner	= THIS_MODULE,	},};static int __init atmel_lcdfb_init(void){	return platform_driver_probe(&atmel_lcdfb_driver, atmel_lcdfb_probe);}static void __exit atmel_lcdfb_exit(void){	platform_driver_unregister(&atmel_lcdfb_driver);}module_init(atmel_lcdfb_init);module_exit(atmel_lcdfb_exit);MODULE_DESCRIPTION("AT91/AT32 LCD Controller framebuffer driver");MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?