commit: http://blackfin.uclinux.org/git/?p=linux-kernel;a=commitdiff;h=2deec61720263897c7e7c7abcfe60eec9a7460bd branch: http://blackfin.uclinux.org/git/?p=linux-kernel;a=shortlog;h=refs/heads/trunk
Signed-off-by: Scott Jiang <[email protected]> --- drivers/video/Kconfig | 13 + drivers/video/Makefile | 1 + drivers/video/bf609-nl8048.c | 535 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 549 insertions(+), 0 deletions(-) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 4e759e4..c0c6d14 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -640,6 +640,19 @@ config FB_BFIN_7393 To compile this driver as a module, choose M here: the module will be called bfin_adv7393fb. +config FB_BF609_NL8048 + tristate "NEC NL8048HL WVGA LCD for BF609" + depends on FB && BF60x && SPI_MASTER + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the framebuffer device for NEC NL8048HL WVGA LCD + attached to BF609. + + To compile this driver as a module, choose M here: the + module will be called bf609-nl8048. + choice prompt "Video mode support" depends on FB_BFIN_7393 diff --git a/drivers/video/Makefile b/drivers/video/Makefile index e56d643..c34be66 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -161,6 +161,7 @@ obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o obj-$(CONFIG_FB_BFIN_LQ035Q1) += bfin-lq035q1-fb.o obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o +obj-$(CONFIG_FB_BF609_NL8048) += bf609-nl8048.o obj-$(CONFIG_FB_HITACHI_TX09) += hitachi-tx09.o obj-$(CONFIG_FB_MX3) += mx3fb.o obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o diff --git a/drivers/video/bf609-nl8048.c b/drivers/video/bf609-nl8048.c new file mode 100644 index 0000000..2082779 --- /dev/null +++ b/drivers/video/bf609-nl8048.c @@ -0,0 +1,535 @@ +/* + * bf609-nl8048.c NEC WVGA LCD NL8048HL11-01B driver for BF609 + * + * Copyright (c) 2012 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/fb.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ioctl.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/types.h> + +#include <asm/bfin_ppi.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#define WIDTH 800 +#define HEIGHT 480 +#define BPP 16 +#define MEM_SIZE (WIDTH * HEIGHT * 2) + +struct bfin_fb_par { + u32 pseudo_pal[16]; + struct spi_device *spi; + int dma_ch; + int irq_err; + struct bfin_eppi3_regs *reg; + const unsigned short *per_fs; + const unsigned short *per_data; +}; + +static const unsigned short eppi0_per_fs[] = { + P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, 0, +}; +static const unsigned short eppi0_per_data[] = { + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, + P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, + P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, + P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, + P_PPI0_D16, P_PPI0_D17, P_PPI0_D18, P_PPI0_D19, + P_PPI0_D20, P_PPI0_D21, P_PPI0_D22, P_PPI0_D23, + 0, +}; + +static struct fb_fix_screeninfo bfin_fb_fix __devinitdata = { + .id = KBUILD_MODNAME, + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .line_length = WIDTH * BPP / 8, + .accel = FB_ACCEL_NONE, +}; + +static struct fb_var_screeninfo bfin_fb_var = { + .bits_per_pixel = BPP, + .activate = FB_ACTIVATE_TEST, + .xres = WIDTH, + .yres = HEIGHT, + .xres_virtual = WIDTH, + .yres_virtual = HEIGHT, + .height = -1, + .width = -1, + .left_margin = 0, + .right_margin = 0, + .upper_margin = 0, + .lower_margin = 0, + .red = {11, 5, 0}, + .green = {5, 6, 0}, + .blue = {0, 5, 0}, + .transp = {0, 0, 0}, +}; + +static u8 lcd_init_regs[] = { + 3, 0x01, + 0, 0x00, + 1, 0x01, + 4, 0x00, + 5, 0x14, + 6, 0x24, + 16, 0xD7, + 17, 0x00, + 18, 0x00, + 19, 0x55, + 20, 0x01, + 21, 0x70, + 22, 0x1E, + 23, 0x25, + 24, 0x25, + 25, 0x02, + 26, 0x02, + 27, 0xA0, + 32, 0x2F, + 33, 0x0F, + 34, 0x0F, + 35, 0x0F, + 36, 0x0F, + 37, 0x0F, + 38, 0x0F, + 39, 0x00, + 40, 0x02, + 41, 0x02, + 42, 0x02, + 43, 0x0F, + 44, 0x0F, + 45, 0x0F, + 46, 0x0F, + 47, 0x0F, + 48, 0x0F, + 49, 0x0F, + 50, 0x00, + 51, 0x02, + 52, 0x02, + 53, 0x02, + 80, 0x0C, + 83, 0x42, + 84, 0x42, + 85, 0x41, + 86, 0x14, + 89, 0x88, + 90, 0x01, + 91, 0x00, + 92, 0x02, + 93, 0x0C, + 94, 0x1C, + 95, 0x27, + 98, 0x49, + 99, 0x27, + 102, 0x76, + 103, 0x27, + 112, 0x01, + 113, 0x0E, + 114, 0x02, + 115, 0x0C, + 118, 0x0C, + 121, 0x20, + 130, 0x00, + 131, 0x00, + 132, 0xFC, + 134, 0x00, + 136, 0x00, + 138, 0x00, + 139, 0x00, + 140, 0x00, + 141, 0xFC, + 143, 0x00, + 145, 0x00, + 147, 0x00, + 148, 0x00, + 149, 0x00, + 150, 0xFC, + 152, 0x00, + 154, 0x00, + 156, 0x00, + 157, 0x00, +}; + +static int soft_switch_config(void) +{ + struct i2c_adapter *adapter; + struct i2c_client *client; + + adapter = i2c_get_adapter(0); + if (!adapter) + return -ENODEV; + client = i2c_new_dummy(adapter, 0x27); + i2c_smbus_write_byte_data(client, 0x03, 0xbf); + i2c_smbus_write_byte_data(client, 0x05, 0xff); + i2c_smbus_write_byte_data(client, 0x07, 0xff); + i2c_smbus_write_byte_data(client, 0x09, 0xff); + i2c_smbus_write_byte_data(client, 0x0b, 0xb3); + i2c_smbus_write_byte_data(client, 0x0d, 0x6f); + + i2c_smbus_write_byte_data(client, 0x02, 0x00); + i2c_smbus_write_byte_data(client, 0x04, 0x00); + i2c_smbus_write_byte_data(client, 0x06, 0x00); + i2c_smbus_write_byte_data(client, 0x08, 0x00); + i2c_smbus_write_byte_data(client, 0x0a, 0x00); + i2c_smbus_write_byte_data(client, 0x0c, 0x00); + i2c_unregister_device(client); + i2c_put_adapter(adapter); + return 0; +} + +static int lcd_write_reg(struct spi_device *spi, u8 reg, u8 val) +{ + struct spi_message msg; + struct spi_transfer x; + u8 command[4]; + + command[0] = 0; + command[1] = reg; + command[2] = 1; + command[3] = val; + spi_message_init(&msg); + memset(&x, 0, sizeof(x)); + x.tx_buf = command; + x.len = 4; + spi_message_add_tail(&x, &msg); + return spi_sync(spi, &msg); +} + +static void lcd_write_regs(struct spi_device *spi, u8 *regs, int len) +{ + int i; + + for (i = 0; i < len; i += 2) + lcd_write_reg(spi, regs[i], regs[i + 1]); +} + +static int dummy_probe(struct spi_device *spi) +{ + return 0; +} + +static int dummy_remove(struct spi_device *spi) +{ + return 0; +} + +static struct spi_driver dummy_driver = { + .driver.name = "dummy", + .probe = dummy_probe, + .remove = dummy_remove, +}; + +static int lcd_startup(struct bfin_fb_par *par) +{ + int ret; + struct spi_master *master; + struct spi_board_info board = { + .modalias = "dummy", + .max_speed_hz = 5000000, + .bus_num = 0, + .chip_select = 4, + }; + + master = spi_busnum_to_master(0); + if (!master) + return -ENODEV; + par->spi = spi_new_device(master, &board); + if (!par->spi) + return -EINVAL; + ret = spi_register_driver(&dummy_driver); + if (ret < 0) { + spi_unregister_device(par->spi); + return ret; + } + lcd_write_regs(par->spi, lcd_init_regs, ARRAY_SIZE(lcd_init_regs)); + udelay(20); + lcd_write_reg(par->spi, 2, 0x00); + return 0; +} + +static void lcd_shutdown(struct bfin_fb_par *par) +{ + lcd_write_reg(par->spi, 16, 0x05); + udelay(20); + lcd_write_reg(par->spi, 16, 0x01); + udelay(20); + lcd_write_reg(par->spi, 16, 0x00); + udelay(20); + lcd_write_reg(par->spi, 3, 0x01); + spi_unregister_device(par->spi); + spi_unregister_driver(&dummy_driver); +} + +static void start_ppi(struct fb_info *info) +{ + struct fb_var_screeninfo *var = &info->var; + struct bfin_fb_par *par = info->par; + struct bfin_eppi3_regs *reg = par->reg; + int dma_config, ppi_control; + int bytes_per_line; + + /* setup dma */ + dma_config = DMAFLOW_AUTO | RESTART | WDSIZE_32 | PSIZE_32 | DMA2D; + + bytes_per_line = var->xres * var->bits_per_pixel / 8; + set_dma_x_count(par->dma_ch, bytes_per_line >> 2); + set_dma_x_modify(par->dma_ch, 4); + set_dma_y_count(par->dma_ch, var->yres); + set_dma_y_modify(par->dma_ch, 4); + set_dma_start_addr(par->dma_ch, info->fix.smem_start); + set_dma_config(par->dma_ch, dma_config); + + /* setup ppi */ + ppi_control = EPPI_CTL_PACKEN | EPPI_CTL_DLEN16 | EPPI_CTL_FS1LO_FS2LO + | EPPI_CTL_POLC0 | EPPI_CTL_IFSGEN | EPPI_CTL_SYNC2 + | EPPI_CTL_NON656 | EPPI_CTL_DIR | EPPI_CTL_EN; + + bfin_write32(®->line, 811); + bfin_write32(®->frame, 488); + bfin_write32(®->hdly, 5); + bfin_write32(®->vdly, 5); + bfin_write32(®->hcnt, var->xres); + bfin_write32(®->vcnt, var->yres); + bfin_write32(®->fs1_wlhb, 1); + bfin_write32(®->fs1_paspl, 811); + bfin_write32(®->fs2_wlvb, 811); + bfin_write32(®->fs2_palpf, 811 * 488); + + enable_dma(par->dma_ch); + SSYNC(); + bfin_write32(®->ctl, ppi_control); +} + +static void stop_ppi(struct fb_info *info) +{ + struct bfin_fb_par *par = info->par; + struct bfin_eppi3_regs *reg = par->reg; + + bfin_write32(®->ctl, 0); + clear_dma_irqstat(par->dma_ch); + disable_dma(par->dma_ch); +} + +static irqreturn_t eppi_irq_err(int irq, void *dev_id) +{ + struct fb_info *info = dev_id; + struct bfin_fb_par *par = info->par; + struct bfin_eppi3_regs *reg = par->reg; + + bfin_write32(®->stat, 0xc0ff); + + return IRQ_HANDLED; +} + +static int bfin_fb_open(struct fb_info *info, int user) +{ + struct bfin_fb_par *par = info->par; + int usr; + int ret; + + usr = atomic_read(&info->count); + ret = request_dma(par->dma_ch, "EPPI DMA"); + if (ret) { + dev_err(info->dev, "Can't allocate DMA channel for EPPI\n"); + return ret; + } + + ret = request_irq(par->irq_err, eppi_irq_err, 0, "EPPI ERROR", info); + if (ret) { + dev_err(info->dev, "Can't allocate IRQ for EPPI\n"); + goto err; + } + + ret = peripheral_request_list(par->per_fs, KBUILD_MODNAME); + if (ret) { + dev_err(info->dev, "Can't request FS pins\n"); + goto err1; + } + + ret = peripheral_request_list(par->per_data, KBUILD_MODNAME); + if (ret) { + dev_err(info->dev, "Can't request DATA pins\n"); + goto err2; + } + start_ppi(info); + return 0; +err2: + peripheral_free_list(par->per_fs); +err1: + free_irq(par->irq_err, info); +err: + free_dma(par->dma_ch); + return ret; + +} + +static int bfin_fb_release(struct fb_info *info, int user) +{ + struct bfin_fb_par *par = info->par; + + stop_ppi(info); + peripheral_free_list(par->per_data); + peripheral_free_list(par->per_fs); + free_irq(par->irq_err, info); + free_dma(par->dma_ch); + return 0; +} + +static int bfin_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + int bpp = var->bits_per_pixel; + int line_length; + /* check color depth */ + if (bpp != 16) + return -EINVAL; + + /* various resolution checks */ + if (info->var.xres != var->xres + || info->var.yres != var->yres + || info->var.xres_virtual != var->xres_virtual + || info->var.yres_virtual != var->yres_virtual) + return -EINVAL; + + /* check memory limit */ + line_length = var->xres_virtual * bpp / 8; + if (line_length * var->yres_virtual > info->fix.smem_len) + return -EINVAL; + + var->red = info->var.red; + var->green = info->var.green; + var->blue = info->var.blue; + var->transp = info->var.transp; + + return 0; +} + +static int bfin_fb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + return -EINVAL; +} + +static struct fb_ops bfin_fb_ops = { + .owner = THIS_MODULE, + .fb_open = bfin_fb_open, + .fb_release = bfin_fb_release, + .fb_check_var = bfin_fb_check_var, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = bfin_fb_cursor, +}; + +static int __devinit bfin_nl8048_probe(struct platform_device *pdev) +{ + struct fb_info *info; + struct bfin_fb_par *par; + dma_addr_t dma_handle; + int ret; + + info = framebuffer_alloc(sizeof(struct bfin_fb_par), &pdev->dev); + if (!info) + return -ENOMEM; + par = info->par; + par->dma_ch = CH_EPPI0_CH0; + par->irq_err = IRQ_EPPI0_STAT; + par->reg = (struct bfin_eppi3_regs *)EPPI0_STAT; + par->per_fs = eppi0_per_fs; + par->per_data = eppi0_per_data; + + info->screen_base = dma_alloc_coherent(NULL, MEM_SIZE, + &dma_handle, GFP_KERNEL); + if (!info->screen_base) { + ret = -ENOMEM; + dev_err(&pdev->dev, "Can't alloc dma buffer\n"); + goto err; + } + bfin_fb_fix.smem_start = (unsigned long)info->screen_base; + bfin_fb_fix.smem_len = MEM_SIZE; + info->fix = bfin_fb_fix; + info->var = bfin_fb_var; + info->fbops = &bfin_fb_ops; + info->pseudo_palette = par->pseudo_pal; + info->flags = FBINFO_DEFAULT; + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret < 0) { + dev_err(&pdev->dev, "Can't alloc cmap\n"); + goto err1; + } + + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(&pdev->dev, "Can't register frame buffer\n"); + goto err2; + } + + soft_switch_config(); + ret = lcd_startup(par); + if (ret < 0) { + dev_err(&pdev->dev, "Can't start LCD\n"); + goto err3; + } + + platform_set_drvdata(pdev, info); + return 0; +err3: + unregister_framebuffer(info); +err2: + fb_dealloc_cmap(&info->cmap); +err1: + dma_free_coherent(NULL, MEM_SIZE, info->screen_base, 0); +err: + framebuffer_release(info); + return ret; +} + +static int __devexit bfin_nl8048_remove(struct platform_device *pdev) +{ + struct fb_info *info = platform_get_drvdata(pdev); + + lcd_shutdown(info->par); + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + dma_free_coherent(NULL, MEM_SIZE, info->screen_base, 0); + framebuffer_release(info); + return 0; +} + +static struct platform_driver bfin_nl8048_driver = { + .probe = bfin_nl8048_probe, + .remove = __devexit_p(bfin_nl8048_remove), + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, +}; +module_platform_driver(bfin_nl8048_driver); + +MODULE_DESCRIPTION("NEC WVGA LCD NL8048HL11-01B driver"); +MODULE_AUTHOR("Scott Jiang <[email protected]>"); +MODULE_LICENSE("GPL v2");
_______________________________________________ Linux-kernel-commits mailing list [email protected] https://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits
