@@ -30,8 +30,17 @@ enum jz4740_fb_lcd_type {
JZ_LCD_TYPE_DUAL_COLOR_STN = 10,
JZ_LCD_TYPE_DUAL_MONOCHROME_STN = 11,
JZ_LCD_TYPE_8BIT_SERIAL = 12,
+
+ JZ_SLCD_TYPE_PARALLEL_8_BIT = 1 | (1 << 5),
+ JZ_SLCD_TYPE_PARALLEL_16_BIT = 0 | (1 << 5),
+ JZ_SLCD_TYPE_PARALLEL_18_BIT = 2 | (1 << 5),
+ JZ_SLCD_TYPE_SERIAL_8_BIT = 1 | (3 << 5),
+ JZ_SLCD_TYPE_SERIAL_16_BIT = 0 | (3 << 5),
+ JZ_SLCD_TYPE_SERIAL_18_BIT = 2 | (3 << 5),
};
+#define JZ4740_FB_IS_SLCD_TYPE(type) ((type) & (1 << 5))
+#define JZ4740_FB_IS_SLCD_SERIAL_TYPE(type) ((type) & (2 << 5))
#define JZ4740_FB_SPECIAL_TFT_CONFIG(start, stop) (((start) << 16) | (stop))
/*
@@ -62,6 +71,20 @@ struct jz4740_fb_platform_data {
unsigned pixclk_falling_edge:1;
unsigned date_enable_active_low:1;
+ unsigned chip_select_active_low:1;
+ unsigned register_select_active_low:1;
};
+struct platform_device;
+
+extern void jz4740_fb_slcd_disable_transfer(struct platform_device *pdev);
+extern void jz4740_fb_slcd_enable_transfer(struct platform_device *pdev);
+extern void jz4740_fb_slcd_send_cmd_data(struct platform_device *pdev,
+ unsigned int cmd, unsigned int data);
+extern void jz4740_fb_slcd_send_cmd(struct platform_device *pdev,
+ unsigned int cmd);
+
#endif
@@ -6,6 +6,9 @@ choice
config JZ4740_QI_LB60
bool "Qi Hardware Ben NanoNote"
+config JZ4740_ONDAVX747
+ bool "Onda VX747"
+
endchoice
config HAVE_PWM
@@ -12,6 +12,7 @@ obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o
# board specific support
obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o
+obj-$(CONFIG_JZ4740_ONDAVX747) += board-ondavx747.o
# PM support
new file mode 100644
@@ -0,0 +1,263 @@
+/*
+ * linux/arch/mips/jz4740/board-ondavx747.c
+ *
+ * Onda VX747 board support
+ *
+ * Copyright (c) 2010 Maurus Cuelenaere <mcuelenaere@gmail.com>
+ * based on board-qi_lb60.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or later
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+
+#include <video/truly_g240400rtsw.h>
+
+#include <asm/mach-jz4740/jz4740_fb.h>
+#include <asm/mach-jz4740/platform.h>
+
+#include "clock.h"
+
+/* Display */
+static struct fb_videomode ondavx747_video_modes[] = {
+ {
+ .name = "240x400",
+ .xres = 240,
+ .yres = 400,
+ .refresh = 36*1000000,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+};
+
+static struct jz4740_fb_platform_data ondavx747_fb_pdata = {
+ .modes = ondavx747_video_modes,
+ .num_modes = ARRAY_SIZE(ondavx747_video_modes),
+ .bpp = 16,
+ .lcd_type = JZ_SLCD_TYPE_PARALLEL_16_BIT,
+ .pixclk_falling_edge = 1,
+ .chip_select_active_low = 1,
+ .register_select_active_low = 1,
+};
+
+static struct g240400_pdata ondavx747_lcd_pdata __initdata = {
+ .jz4740_fb = &jz4740_framebuffer_device,
+ .default_mode = ondavx747_video_modes,
+ .gpio_cs = JZ_GPIO_PORTB(17),
+ .gpio_reset = JZ_GPIO_PORTB(18),
+};
+
+static struct platform_device ondavx747_lcd_device = {
+ .name = "truly_g240400rtsw",
+ .id = -1,
+ .dev.platform_data = &ondavx747_lcd_pdata,
+ .dev.parent = &jz4740_framebuffer_device.dev,
+};
+
+static struct platform_device *ondavx747_pdevices[] __initdata = {
+ &jz4740_framebuffer_device,
+ &ondavx747_lcd_device,
+};
+
+struct jz4740_clock_board_data jz4740_clock_bdata = {
+ .ext_rate = 12000000,
+ .rtc_rate = 32768,
+};
+
+static int __init ondavx747_board_setup(void)
+{
+ jz4740_framebuffer_device.dev.platform_data = &ondavx747_fb_pdata;
+
+ jz4740_serial_device_register();
+
+ return platform_add_devices(ondavx747_pdevices,
+ ARRAY_SIZE(ondavx747_pdevices));
+}
+arch_initcall(ondavx747_board_setup);
@@ -109,6 +109,13 @@ config LCD_S6E63M0
If you have an S6E63M0 LCD Panel, say Y to enable its
LCD control driver.
+config LCD_G240400
+ tristate "Truly G240400RTSW LCD Driver"
+ depends on MACH_JZ4740
+ help
+ If you have a Truly G2400400RTSW LCD panel, say Y to enable its LCD
+ control driver.
+
endif # LCD_CLASS_DEVICE
#
@@ -12,6 +12,7 @@ obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o
obj-$(CONFIG_LCD_TDO24M) += tdo24m.o
obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o
obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o
+obj-$(CONFIG_LCD_G240400) += truly_g240400rtsw.o
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o
new file mode 100644
@@ -0,0 +1,446 @@
+#include <linux/backlight.h>
+#include <linux/device.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/lcd.h>
+
+#include <linux/spi/spi.h>
+
+#include <asm/mach-jz4740/jz4740_fb.h>
+
+#include <video/truly_g240400rtsw.h>
+
+/* Register list */
+#define REG_DEVICE_CODE 0x000
+#define REG_DRIVER_OUTPUT 0x001
+#define REG_LCD_DR_WAVE_CTRL 0x002
+#define REG_ENTRY_MODE 0x003
+#define REG_OUTL_SHARP_CTRL 0x006
+#define REG_DISP_CTRL1 0x007
+#define REG_DISP_CTRL2 0x008
+#define REG_DISP_CTRL3 0x009
+#define REG_LPCTRL 0x00B
+#define REG_EXT_DISP_CTRL1 0x00C
+#define REG_EXT_DISP_CTRL2 0x00F
+#define REG_PAN_INTF_CTRL1 0x010
+#define REG_PAN_INTF_CTRL2 0x011
+#define REG_PAN_INTF_CTRL3 0x012
+#define REG_PAN_INTF_CTRL4 0x020
+#define REG_PAN_INTF_CTRL5 0x021
+#define REG_PAN_INTF_CTRL6 0x022
+#define REG_FRM_MRKR_CTRL 0x090
+
+#define REG_PWR_CTRL1 0x100
+#define REG_PWR_CTRL2 0x101
+#define REG_PWR_CTRL3 0x102
+#define REG_PWR_CTRL4 0x103
+#define REG_PWR_CTRL5 0x107
+#define REG_PWR_CTRL6 0x110
+#define REG_PWR_CTRL7 0x112
+
+#define REG_RAM_HADDR_SET 0x200
+#define REG_RAM_VADDR_SET 0x201
+#define REG_RW_GRAM 0x202
+#define REG_RAM_HADDR_START 0x210
+#define REG_RAM_HADDR_END 0x211
+#define REG_RAM_VADDR_START 0x212
+#define REG_RAM_VADDR_END 0x213
+#define REG_RW_NVM 0x280
+#define REG_VCOM_HVOLTAGE1 0x281
+#define REG_VCOM_HVOLTAGE2 0x282
+
+#define REG_GAMMA_CTRL1 0x300
+#define REG_GAMMA_CTRL2 0x301
+#define REG_GAMMA_CTRL3 0x302
+#define REG_GAMMA_CTRL4 0x303
+#define REG_GAMMA_CTRL5 0x304
+#define REG_GAMMA_CTRL6 0x305
+#define REG_GAMMA_CTRL7 0x306
+#define REG_GAMMA_CTRL8 0x307
+#define REG_GAMMA_CTRL9 0x308
+#define REG_GAMMA_CTRL10 0x309
+#define REG_GAMMA_CTRL11 0x30A
+#define REG_GAMMA_CTRL12 0x30B
+#define REG_GAMMA_CTRL13 0x30C
+#define REG_GAMMA_CTRL14 0x30D
+
+#define REG_BIMG_NR_LINE 0x400
+#define REG_BIMG_DISP_CTRL 0x401
+#define REG_BIMG_VSCROLL_CTRL 0x404
+
+#define REG_PARTIMG1_POS 0x500
+#define REG_PARTIMG1_RAM_START 0x501
+#define REG_PARTIMG1_RAM_END 0x502
+#define REG_PARTIMG2_POS 0x503
+#define REG_PARTIMG2_RAM_START 0x504
+#define REG_PARTIMG2_RAM_END 0x505
+
+#define REG_SOFT_RESET 0x600
+#define REG_ENDIAN_CTRL 0x606
+#define REG_NVM_ACCESS_CTRL 0x6F0
+
+/* Bits */
+#define DRIVER_OUTPUT_SS_BIT (1 << 8)
+#define DRIVER_OUTPUT_SM_BIT (1 << 10)
+
+#define ENTRY_MODE_TRI (1 << 15)
+#define ENTRY_MODE_DFM (1 << 14)
+#define ENTRY_MODE_BGR (1 << 12)
+#define ENTRY_MODE_HWM (1 << 9)
+#define ENTRY_MODE_ORG (1 << 7)
+#define ENTRY_MODE_VID (1 << 5)
+#define ENTRY_MODE_HID (1 << 4)
+#define ENTRY_MODE_AM (1 << 3)
+#define ENTRY_MODE_EPF(n) (n & 3)
+
+#define OUTL_SHARP_CTRL_EGMODE (1 << 15)
+#define OUTL_SHARP_CTRL_AVST(n) ((n & 7) << 7)
+#define OUTL_SHARP_CTRL_ADST(n) ((n & 7) << 4)
+#define OUTL_SHARP_CTRL_DTHU(n) ((n & 3) << 2)
+#define OUTL_SHARP_CTRL_DTHL(n) (n & 3)
+
+#define DISP_CTRL1_PTDE(n) ((n & 4) << 12)
+#define DISP_CTRL1_BASEE (1 << 8)
+#define DISP_CTRL1_VON (1 << 6)
+#define DISP_CTRL1_GON (1 << 5)
+#define DISP_CTRL1_DTE (1 << 4)
+#define DISP_CTRL1_D(n) (n & 3)
+
+#define EXT_DISP_CTRL1_ENC(n) ((n & 7) << 12)
+#define EXT_DISP_CTRL1_RM(n) ((n & 1) << 8)
+#define EXT_DISP_CTRL1_DM(n) ((n & 3) << 4)
+#define EXT_DISP_CTRL1_RIM(n) (n & 3)
+
+#define PWR_CTRL1_SAP(n) ((n & 3) << 13)
+#define PWR_CTRL1_SAPE (1 << 12)
+#define PWR_CTRL1_BT(n) ((n & 7) << 8)
+#define PWR_CTRL1_APE (1 << 7)
+#define PWR_CTRL1_AP(n) ((n & 7) << 4)
+#define PWR_CTRL1_DSTB (1 << 2)
+#define PWR_CTRL1_SLP (1 << 1)
+
+#define SOFT_RESET(n) (n << 0)
+
+struct g240400 {
+ struct device *dev;
+ struct lcd_device *lcd;
+
+ struct g240400_pdata pdata;
+
+ int power;
+};
+
+#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
+
+static void g240400_reset(struct g240400 *lcm)
+{
+ struct platform_device *jzfb = lcm->pdata.jz4740_fb;
+
+ gpio_direction_output(lcm->pdata.gpio_reset, 1);
+ mdelay(2*10);
+ gpio_direction_output(lcm->pdata.gpio_reset, 0);
+ mdelay(2*10);
+ gpio_direction_output(lcm->pdata.gpio_reset, 1);
+ mdelay(2*10);
+
+ jz4740_fb_slcd_disable_transfer(jzfb);
+
+#define i 20
+#define SLEEP(x) mdelay(x/10000)
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_SOFT_RESET, SOFT_RESET(1));
+ //SLEEP(700000);
+ mdelay(2*i);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_SOFT_RESET, SOFT_RESET(0));
+ //SLEEP(700000);
+ mdelay(2*i);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_ENDIAN_CTRL, 0);
+
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_DRIVER_OUTPUT, 0x100);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_LCD_DR_WAVE_CTRL, 0x100);
+
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_ENTRY_MODE, (ENTRY_MODE_BGR | ENTRY_MODE_HWM /*| ENTRY_MODE_DFM*/ | ENTRY_MODE_VID | ENTRY_MODE_HID));
+
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_DISP_CTRL2, 0x503);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_DISP_CTRL3, 1);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_LPCTRL, 0x10);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_EXT_DISP_CTRL1, EXT_DISP_CTRL1_RIM(1)); /* 16-bit RGB interface */
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_EXT_DISP_CTRL2, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_DISP_CTRL1, DISP_CTRL1_D(1));
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PAN_INTF_CTRL1, 0x12);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PAN_INTF_CTRL2, 0x202);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PAN_INTF_CTRL3, 0x300);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PAN_INTF_CTRL4, 0x21e);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PAN_INTF_CTRL5, 0x202);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PAN_INTF_CTRL6, 0x100);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_FRM_MRKR_CTRL, 0x8000);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PWR_CTRL1, (PWR_CTRL1_SAPE | PWR_CTRL1_BT(6) | PWR_CTRL1_APE | PWR_CTRL1_AP(3)));
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PWR_CTRL2, 0x147);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PWR_CTRL3, 0x1bd);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PWR_CTRL4, 0x2f00);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PWR_CTRL5, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PWR_CTRL6, 1);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RW_NVM, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_VCOM_HVOLTAGE1, 6);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_VCOM_HVOLTAGE2, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL1, 0x101);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL2, 0xb27);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL3, 0x132a);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL4, 0x2a13);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL5, 0x270b);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL6, 0x101);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL7, 0x1205);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL8, 0x512);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL9, 5);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL10, 3);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL11, 0xf04);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL12, 0xf00);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL13, 0xf);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_GAMMA_CTRL14, 0x40f);
+ jz4740_fb_slcd_send_cmd_data(jzfb, 0x30e, 0x300);
+ jz4740_fb_slcd_send_cmd_data(jzfb, 0x30f, 0x500);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_BIMG_NR_LINE, 0x3100);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_BIMG_DISP_CTRL, 1);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_BIMG_VSCROLL_CTRL, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PARTIMG1_POS, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PARTIMG1_RAM_START, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PARTIMG1_RAM_END, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PARTIMG2_POS, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PARTIMG2_RAM_START, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_PARTIMG2_RAM_END, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_ENDIAN_CTRL, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_NVM_ACCESS_CTRL, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, 0x7f0, 0x5420);
+ jz4740_fb_slcd_send_cmd_data(jzfb, 0x7f3, 0x288a);
+ jz4740_fb_slcd_send_cmd_data(jzfb, 0x7f4, 0x22);
+ jz4740_fb_slcd_send_cmd_data(jzfb, 0x7f5, 1);
+ jz4740_fb_slcd_send_cmd_data(jzfb, 0x7f0, 0);
+
+ /* LCD ON */
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_DISP_CTRL1, (DISP_CTRL1_BASEE | DISP_CTRL1_VON |
+ DISP_CTRL1_GON | DISP_CTRL1_DTE | DISP_CTRL1_D(3)));
+ //SLEEP(3500000);
+ mdelay(10*i);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_DISP_CTRL1, (DISP_CTRL1_BASEE | DISP_CTRL1_VON |
+ DISP_CTRL1_GON | DISP_CTRL1_DTE | DISP_CTRL1_D(2)));
+ //SLEEP(3500000);
+ mdelay(10*i);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_DISP_CTRL1, (DISP_CTRL1_BASEE | DISP_CTRL1_VON |
+ DISP_CTRL1_GON | DISP_CTRL1_DTE | DISP_CTRL1_D(3)));
+ //SLEEP(3500000);
+ mdelay(10*i);
+#undef i
+
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RAM_HADDR_START, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RAM_HADDR_END, lcm->pdata.default_mode->xres - 1);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RAM_VADDR_START, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RAM_VADDR_END, lcm->pdata.default_mode->yres - 1);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RAM_HADDR_SET, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RAM_VADDR_SET, 0);
+ jz4740_fb_slcd_send_cmd(jzfb, REG_RW_GRAM); /* write data to GRAM */
+
+ jz4740_fb_slcd_enable_transfer(jzfb);
+}
+
+static void g240400_powerdown(struct g240400 *lcm)
+{
+}
+
+static void g240400_standby(struct g240400 *lcm)
+{
+}
+
+static void g240400_poweron(struct g240400 *lcm)
+{
+}
+
+static int g240400_get_power(struct lcd_device *lcd)
+{
+ struct g240400 *lcm = lcd_get_data(lcd);
+
+ return lcm->power;
+}
+
+static int g240400_set_power(struct lcd_device *lcd, int power)
+{
+ struct g240400 *lcm = lcd_get_data(lcd);
+
+ if (power == lcm->power)
+ return 0;
+
+ if (POWER_IS_ON(power))
+ g240400_poweron(lcm);
+ else
+ g240400_standby(lcm);
+
+ lcm->power = power;
+
+ return 0;
+}
+
+static int g240400_set_mode(struct lcd_device *lcd, struct fb_videomode *mode)
+{
+ struct g240400 *lcm = lcd_get_data(lcd);
+ struct platform_device *jzfb = lcm->pdata.jz4740_fb;
+
+ jz4740_fb_slcd_disable_transfer(jzfb);
+
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RAM_HADDR_START, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RAM_HADDR_END, mode->xres - 1);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RAM_VADDR_START, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RAM_VADDR_END, mode->yres - 1);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RAM_HADDR_SET, 0);
+ jz4740_fb_slcd_send_cmd_data(jzfb, REG_RAM_VADDR_SET, 0);
+ jz4740_fb_slcd_send_cmd(jzfb, REG_RW_GRAM); /* write data to GRAM */
+
+ jz4740_fb_slcd_enable_transfer(jzfb);
+
+ lcm->pdata.default_mode = mode;
+
+ return 0;
+}
+
+static struct lcd_ops g240400_lcd_ops = {
+ .get_power = g240400_get_power,
+ .set_power = g240400_set_power,
+ .set_mode = g240400_set_mode,
+};
+
+static int __devinit g240400_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct g240400 *lcm;
+ struct lcd_device *lcd;
+ struct g240400_pdata *pdata = pdev->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Platform data wasn't provided!");
+ return -EINVAL;
+ }
+
+ lcm = kzalloc(sizeof(struct g240400), GFP_KERNEL);
+ if (!lcm)
+ return -ENOMEM;
+
+ lcm->dev = &pdev->dev;
+ memcpy(&lcm->pdata, pdata, sizeof(struct g240400_pdata));
+
+ lcd = lcd_device_register("truly_g240400", &pdev->dev, lcm, &g240400_lcd_ops);
+ if (IS_ERR(lcd)) {
+ ret = PTR_ERR(lcd);
+ goto out_free_device;
+ }
+
+ if (gpio_is_valid(lcm->pdata.gpio_reset)) {
+ ret = gpio_request(lcm->pdata.gpio_reset, "lcd reset");
+ if (!ret)
+ goto out_unregister_lcd;
+ }
+
+ if (gpio_is_valid(lcm->pdata.gpio_cs)) {
+ ret = gpio_request(lcm->pdata.gpio_cs, "lcd chip select");
+ if (!ret)
+ goto out_free_pin_cs;
+ }
+
+ dev_set_drvdata(&pdev->dev, lcm);
+
+ /* Always select LCD chip */
+ gpio_direction_output(lcm->pdata.gpio_cs, 0);
+ g240400_reset(lcm);
+
+ return 0;
+
+out_free_pin_cs:
+ if (gpio_is_valid(lcm->pdata.gpio_cs))
+ gpio_free(lcm->pdata.gpio_cs);
+out_unregister_lcd:
+ lcd_device_unregister(lcd);
+out_free_device:
+ kfree(lcm);
+
+ return ret;
+}
+
+static int __devexit g240400_remove(struct platform_device *pdev)
+{
+ struct g240400 *lcm = dev_get_drvdata(&pdev->dev);
+
+ if (gpio_is_valid(lcm->pdata.gpio_reset))
+ gpio_free(lcm->pdata.gpio_reset);
+ if (gpio_is_valid(lcm->pdata.gpio_cs))
+ gpio_free(lcm->pdata.gpio_cs);
+ lcd_device_unregister(lcm->lcd);
+ kfree(lcm);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int g240400_suspend(struct device *dev)
+{
+ struct g240400 *lcm = dev_get_drvdata(dev);
+
+ g240400_powerdown(lcm);
+ gpio_direction_output(lcm->pdata.gpio_cs, 1);
+
+ return 0;
+}
+
+static int g240400_resume(struct device *dev)
+{
+ struct g240400 *lcm = dev_get_drvdata(dev);
+
+ gpio_direction_output(lcm->pdata.gpio_cs, 0);
+ g240400_reset(lcm);
+
+ return 0;
+}
+
+static const struct dev_pm_ops g240400_ops = {
+ .suspend = g240400_suspend,
+ .resume = g240400_resume,
+};
+#endif
+
+static struct platform_driver g240400_driver = {
+ .driver = {
+ .name = "truly_g240400rtsw",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &g240400_ops,
+#endif
+ },
+ .probe = g240400_probe,
+ .remove = __devexit_p(g240400_remove),
+};
+
+static int __init g240400_init(void)
+{
+ return platform_driver_register(&g240400_driver);
+}
+module_init(g240400_init);
+
+static void __exit g240400_exit(void)
+{
+ platform_driver_unregister(&g240400_driver);
+}
+module_exit(g240400_exit);
@@ -26,6 +26,7 @@
#include <linux/dma-mapping.h>
+#include <asm/mach-jz4740/dma.h>
#include <asm/mach-jz4740/jz4740_fb.h>
#include <asm/mach-jz4740/gpio.h>
@@ -107,6 +108,38 @@
#define JZ_LCD_STATE_DISABLED BIT(0)
+#define JZ_REG_SLCD_CFG 0xA0
+#define JZ_REG_SLCD_CTRL 0xA4
+#define JZ_REG_SLCD_STATE 0xA8
+#define JZ_REG_SLCD_DATA 0xAC
+#define JZ_REG_SLCD_FIFO 0xB0
+
+#define JZ_SLCD_CFG_BURST_8_WORD BIT(14)
+#define JZ_SLCD_CFG_DWIDTH_18 (0 << 10)
+#define JZ_SLCD_CFG_DWIDTH_16 (1 << 10)
+#define JZ_SLCD_CFG_DWIDTH_8_x3 (2 << 10)
+#define JZ_SLCD_CFG_DWIDTH_8_x2 (3 << 10)
+#define JZ_SLCD_CFG_DWIDTH_8_x1 (4 << 10)
+#define JZ_SLCD_CFG_DWIDTH_9_x2 (7 << 10)
+#define JZ_SLCD_CFG_CWIDTH(n) ((n) << 8)
+#define JZ_SLCD_CFG_CWIDTH_16BIT (0 << 8)
+#define JZ_SLCD_CFG_CWIDTH_8BIT (1 << 8)
+#define JZ_SLCD_CFG_CWIDTH_18BIT (2 << 8)
+#define JZ_SLCD_CFG_CS_ACTIVE_HIGH BIT(4)
+#define JZ_SLCD_CFG_RS_CMD_HIGH BIT(3)
+#define JZ_SLCD_CFG_CLK_ACTIVE_RISING BIT(1)
+#define JZ_SLCD_CFG_TYPE_SERIAL BIT(0)
+
+#define JZ_SLCD_CTRL_DMA_EN (1 << 0)
+
+#define JZ_SLCD_STATE_BUSY (1 << 0)
+
+#define JZ_SLCD_DATA_RS_DATA (0 << 31)
+#define JZ_SLCD_DATA_RS_COMMAND (1 << 31)
+
+#define JZ_SLCD_FIFO_RS_DATA (0 << 31)
+#define JZ_SLCD_FIFO_RS_COMMAND (1 << 31)
+
struct jzfb_framedesc {
uint32_t next;
uint32_t addr;
@@ -118,6 +151,7 @@ struct jzfb {
struct fb_info *fb;
struct platform_device *pdev;
void __iomem *base;
+ phys_t phys_base;
struct resource *mem;
struct jz4740_fb_platform_data *pdata;
@@ -126,6 +160,7 @@ struct jzfb {
dma_addr_t vidmem_phys;
struct jzfb_framedesc *framedesc;
dma_addr_t framedesc_phys;
+ struct jz4740_dma_chan *slcd_dma;
struct clk *ldclk;
struct clk *lpclk;
@@ -136,6 +171,8 @@ struct jzfb {
uint32_t pseudo_palette[16];
};
+#define JZFB_IS_SLCD(jzfb) JZ4740_FB_IS_SLCD_TYPE((jzfb)->pdata->lcd_type)
+
static const struct fb_fix_screeninfo jzfb_fix __devinitdata = {
.id = "JZ4740 FB",
.type = FB_TYPE_PACKED_PIXELS,
@@ -178,59 +215,72 @@ static const struct jz_gpio_bulk_request jz_lcd_data_pins[] = {
JZ_GPIO_BULK_PIN(LCD_DATA17),
};
-static unsigned int jzfb_num_ctrl_pins(struct jzfb *jzfb)
+enum jzfb_pin_operation {
+ REQUEST_PINS,
+ FREE_PINS,
+ RESUME_PINS,
+ SUSPEND_PINS,
+};
+
+static void jzfb_pins_operation(struct jzfb *jzfb,
+ enum jzfb_pin_operation operation)
{
- unsigned int num;
+ unsigned int ctrl_num = 0, data_num = 0, data_start = 0;
switch (jzfb->pdata->lcd_type) {
case JZ_LCD_TYPE_GENERIC_16_BIT:
- num = 4;
+ case JZ_SLCD_TYPE_PARALLEL_16_BIT:
+ ctrl_num = 4;
+ data_num = 16;
break;
case JZ_LCD_TYPE_GENERIC_18_BIT:
- num = 4;
+ case JZ_SLCD_TYPE_PARALLEL_18_BIT:
+ ctrl_num = 4;
+ data_num = 18;
break;
case JZ_LCD_TYPE_8BIT_SERIAL:
- num = 3;
+ case JZ_SLCD_TYPE_PARALLEL_8_BIT:
+ ctrl_num = 3;
+ data_num = 8;
break;
case JZ_LCD_TYPE_SPECIAL_TFT_1:
case JZ_LCD_TYPE_SPECIAL_TFT_2:
case JZ_LCD_TYPE_SPECIAL_TFT_3:
- num = 8;
+ ctrl_num = 8;
+ if (jzfb->pdata->bpp == 18)
+ data_num = 18;
+ else
+ data_num = 16;
break;
- default:
- num = 0;
+ case JZ_SLCD_TYPE_SERIAL_8_BIT:
+ case JZ_SLCD_TYPE_SERIAL_16_BIT:
+ case JZ_SLCD_TYPE_SERIAL_18_BIT:
+ data_start = 15;
+ data_num = 1;
break;
}
- return num;
-}
-static unsigned int jzfb_num_data_pins(struct jzfb *jzfb)
-{
- unsigned int num;
+ if (JZFB_IS_SLCD(jzfb))
+ ctrl_num = 3;
- switch (jzfb->pdata->lcd_type) {
- case JZ_LCD_TYPE_GENERIC_16_BIT:
- num = 16;
+ switch (operation) {
+ case REQUEST_PINS:
+ jz_gpio_bulk_request(jz_lcd_ctrl_pins, ctrl_num);
+ jz_gpio_bulk_request(&jz_lcd_data_pins[data_start], data_num);
break;
- case JZ_LCD_TYPE_GENERIC_18_BIT:
- num = 18;
+ case FREE_PINS:
+ jz_gpio_bulk_free(jz_lcd_ctrl_pins, ctrl_num);
+ jz_gpio_bulk_free(&jz_lcd_data_pins[data_start], data_num);
break;
- case JZ_LCD_TYPE_8BIT_SERIAL:
- num = 8;
+ case RESUME_PINS:
+ jz_gpio_bulk_resume(jz_lcd_ctrl_pins, ctrl_num);
+ jz_gpio_bulk_resume(&jz_lcd_data_pins[data_start], data_num);
break;
- case JZ_LCD_TYPE_SPECIAL_TFT_1:
- case JZ_LCD_TYPE_SPECIAL_TFT_2:
- case JZ_LCD_TYPE_SPECIAL_TFT_3:
- if (jzfb->pdata->bpp == 18)
- num = 18;
- else
- num = 16;
- break;
- default:
- num = 0;
+ case SUSPEND_PINS:
+ jz_gpio_bulk_suspend(jz_lcd_ctrl_pins, ctrl_num);
+ jz_gpio_bulk_suspend(&jz_lcd_data_pins[data_start], data_num);
break;
}
- return num;
}
/* Based on CNVT_TOHW macro from skeletonfb.c */
@@ -347,12 +397,9 @@ static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb)
return 0;
}
-static int jzfb_set_par(struct fb_info *info)
+static void jzfb_lcd_set_par(struct jzfb *jzfb, struct fb_videomode *mode)
{
- struct jzfb *jzfb = info->par;
struct jz4740_fb_platform_data *pdata = jzfb->pdata;
- struct fb_var_screeninfo *var = &info->var;
- struct fb_videomode *mode;
uint16_t hds, vds;
uint16_t hde, vde;
uint16_t ht, vt;
@@ -360,15 +407,6 @@ static int jzfb_set_par(struct fb_info *info)
uint32_t cfg;
unsigned long rate;
- mode = jzfb_get_mode(jzfb, var);
- if (mode == NULL)
- return -EINVAL;
-
- if (mode == info->mode)
- return 0;
-
- info->mode = mode;
-
hds = mode->hsync_len + mode->left_margin;
hde = hds + mode->xres;
ht = hde + mode->right_margin;
@@ -477,42 +515,199 @@ static int jzfb_set_par(struct fb_info *info)
clk_set_rate(jzfb->lpclk, rate);
clk_set_rate(jzfb->ldclk, rate * 3);
+}
+
+static void jzfb_slcd_set_par(struct jzfb *jzfb, struct fb_videomode *mode)
+{
+ struct jz4740_fb_platform_data *pdata = jzfb->pdata;
+ uint32_t cfg;
+ unsigned long rate;
+
+ cfg = JZ_SLCD_CFG_BURST_8_WORD;
+ cfg |= JZ_SLCD_CFG_CWIDTH(jzfb->pdata->lcd_type & 3);
+
+ if (JZ4740_FB_IS_SLCD_SERIAL_TYPE(jzfb->pdata->lcd_type)) {
+ switch (jzfb->pdata->lcd_type) {
+ case JZ_SLCD_TYPE_SERIAL_8_BIT:
+ cfg |= JZ_SLCD_CFG_DWIDTH_8_x1;
+ break;
+ case JZ_SLCD_TYPE_SERIAL_16_BIT:
+ cfg |= JZ_SLCD_CFG_DWIDTH_16;
+ break;
+ case JZ_SLCD_TYPE_SERIAL_18_BIT:
+ cfg |= JZ_SLCD_CFG_DWIDTH_18;
+ break;
+ }
+ cfg |= JZ_SLCD_CFG_TYPE_SERIAL;
+ } else {
+ switch (jzfb->pdata->bpp) {
+ case 8:
+ cfg |= JZ_SLCD_CFG_DWIDTH_8_x1;
+ break;
+ case 15:
+ case 16:
+ switch (jzfb->pdata->lcd_type) {
+ case JZ_SLCD_TYPE_PARALLEL_8_BIT:
+ cfg |= JZ_SLCD_CFG_DWIDTH_8_x2;
+ break;
+ case JZ_SLCD_TYPE_PARALLEL_16_BIT:
+ case JZ_SLCD_TYPE_PARALLEL_18_BIT:
+ cfg |= JZ_SLCD_CFG_DWIDTH_16;
+ break;
+ }
+ break;
+ case 18:
+ switch (jzfb->pdata->lcd_type) {
+ case JZ_SLCD_TYPE_PARALLEL_8_BIT:
+ cfg |= JZ_SLCD_CFG_DWIDTH_8_x3;
+ break;
+ case JZ_SLCD_TYPE_PARALLEL_16_BIT:
+ cfg |= JZ_SLCD_CFG_DWIDTH_9_x2;
+ break;
+ case JZ_SLCD_TYPE_PARALLEL_18_BIT:
+ cfg |= JZ_SLCD_CFG_DWIDTH_18;
+ break;
+ }
+ break;
+ case 24:
+ /* ??? */
+ cfg |= JZ_SLCD_CFG_DWIDTH_8_x3;
+ break;
+ }
+ }
+
+ if (!pdata->pixclk_falling_edge)
+ cfg |= JZ_SLCD_CFG_CLK_ACTIVE_RISING;
+
+ if (!pdata->chip_select_active_low)
+ cfg |= JZ_SLCD_CFG_CS_ACTIVE_HIGH;
+
+ if (!pdata->register_select_active_low)
+ cfg |= JZ_SLCD_CFG_RS_CMD_HIGH;
+
+ if (mode->pixclock) {
+ rate = PICOS2KHZ(mode->pixclock) * 1000;
+ mode->refresh = rate;
+ } else {
+ rate = mode->refresh;
+ mode->pixclock = KHZ2PICOS(rate / 1000);
+ }
+
+ mutex_lock(&jzfb->lock);
+ if (!jzfb->is_enabled)
+ clk_enable(jzfb->ldclk);
+
+ writel(JZ_LCD_CFG_SLCD, jzfb->base + JZ_REG_LCD_CFG);
+ writel(cfg, jzfb->base + JZ_REG_SLCD_CFG);
+ writel(0, jzfb->base + JZ_REG_SLCD_CTRL);
+
+ if (!jzfb->is_enabled)
+ clk_disable(jzfb->ldclk);
+ mutex_unlock(&jzfb->lock);
+
+ clk_set_rate(jzfb->lpclk, rate);
+ clk_set_rate(jzfb->ldclk, rate * 3);
+}
+
+static int jzfb_set_par(struct fb_info *info)
+{
+ struct jzfb *jzfb = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ struct fb_videomode *mode;
+
+ mode = jzfb_get_mode(jzfb, var);
+ if (mode == NULL)
+ return -EINVAL;
+
+ if (mode == info->mode)
+ return 0;
+
+ info->mode = mode;
+
+ if (JZFB_IS_SLCD(jzfb))
+ jzfb_slcd_set_par(jzfb, mode);
+ else
+ jzfb_lcd_set_par(jzfb, mode);
return 0;
}
+static void jzfb_slcd_wait(struct jzfb *jzfb)
+{
+ int timeout = 1000;
+ while (readb(jzfb->base + JZ_REG_SLCD_STATE) & JZ_SLCD_STATE_BUSY
+ && timeout--)
+ cpu_relax();
+
+ if (timeout <= 0)
+ dev_warn(&jzfb->pdev->dev, "waiting for SLCD busy timed out!");
+}
+
+static void jzfb_slcd_start_dma(struct jzfb *jzfb)
+{
+ struct fb_info *fb = jzfb->fb;
+ unsigned int length = fb->fix.line_length * fb->mode->yres;
+
+ jzfb_slcd_wait(jzfb);
+
+ jz4740_dma_set_src_addr(jzfb->slcd_dma, jzfb->vidmem_phys);
+ jz4740_dma_set_dst_addr(jzfb->slcd_dma,
+ jzfb->phys_base + JZ_REG_SLCD_FIFO);
+ jz4740_dma_set_transfer_count(jzfb->slcd_dma, length);
+
+ jz4740_dma_enable(jzfb->slcd_dma);
+}
+
+static void jzfb_slcd_dma_callback(struct jz4740_dma_chan *chan, int unk,
+ void *dev)
+{
+ struct jzfb *jzfb = dev;
+
+ /* FIXME: use DMA descriptors! */
+ jzfb_slcd_start_dma(jzfb);
+}
+
static void jzfb_enable(struct jzfb *jzfb)
{
uint32_t ctrl;
clk_enable(jzfb->ldclk);
- jz_gpio_bulk_resume(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
- jz_gpio_bulk_resume(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
+ if (JZFB_IS_SLCD(jzfb)) {
+ jzfb_slcd_wait(jzfb);
+ writeb(JZ_SLCD_CTRL_DMA_EN, jzfb->base + JZ_REG_SLCD_CTRL);
- writel(0, jzfb->base + JZ_REG_LCD_STATE);
+ jzfb_slcd_start_dma(jzfb);
+ } else {
+ writel(0, jzfb->base + JZ_REG_LCD_STATE);
- writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0);
+ writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0);
- ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL);
- ctrl |= JZ_LCD_CTRL_ENABLE;
- ctrl &= ~JZ_LCD_CTRL_DISABLE;
- writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL);
+ ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL);
+ ctrl |= JZ_LCD_CTRL_ENABLE;
+ ctrl &= ~JZ_LCD_CTRL_DISABLE;
+ writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL);
+ }
}
static void jzfb_disable(struct jzfb *jzfb)
{
uint32_t ctrl;
- ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL);
- ctrl |= JZ_LCD_CTRL_DISABLE;
- writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL);
- do {
- ctrl = readl(jzfb->base + JZ_REG_LCD_STATE);
- } while (!(ctrl & JZ_LCD_STATE_DISABLED));
+ if (JZFB_IS_SLCD(jzfb)) {
+ jz4740_dma_disable(jzfb->slcd_dma);
- jz_gpio_bulk_suspend(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
- jz_gpio_bulk_suspend(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
+ jzfb_slcd_wait(jzfb);
+ writeb(0, jzfb->base + JZ_REG_SLCD_CTRL);
+ } else {
+ ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL);
+ ctrl |= JZ_LCD_CTRL_DISABLE;
+ writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL);
+ do {
+ ctrl = readl(jzfb->base + JZ_REG_LCD_STATE);
+ } while (!(ctrl & JZ_LCD_STATE_DISABLED));
+ }
clk_disable(jzfb->ldclk);
}
@@ -565,12 +760,19 @@ static int jzfb_alloc_devmem(struct jzfb *jzfb)
max_videosize *= jzfb_get_controller_bpp(jzfb) >> 3;
- jzfb->framedesc = dma_alloc_coherent(&jzfb->pdev->dev,
- sizeof(*jzfb->framedesc),
- &jzfb->framedesc_phys, GFP_KERNEL);
+ if (!JZFB_IS_SLCD(jzfb)) {
+ jzfb->framedesc = dma_alloc_coherent(&jzfb->pdev->dev,
+ sizeof(*jzfb->framedesc),
+ &jzfb->framedesc_phys,
+ GFP_KERNEL);
- if (!jzfb->framedesc)
- return -ENOMEM;
+ if (!jzfb->framedesc)
+ return -ENOMEM;
+ } else {
+ jzfb->slcd_dma = jz4740_dma_request(jzfb, "SLCD");
+ if (!jzfb->slcd_dma)
+ return -ENXIO;
+ }
jzfb->vidmem_size = PAGE_ALIGN(max_videosize);
jzfb->vidmem = dma_alloc_coherent(&jzfb->pdev->dev,
@@ -586,17 +788,48 @@ static int jzfb_alloc_devmem(struct jzfb *jzfb)
SetPageReserved(virt_to_page(page));
}
- jzfb->framedesc->next = jzfb->framedesc_phys;
- jzfb->framedesc->addr = jzfb->vidmem_phys;
- jzfb->framedesc->id = 0xdeafbead;
- jzfb->framedesc->cmd = 0;
- jzfb->framedesc->cmd |= max_videosize / 4;
+ if (jzfb->framedesc) {
+ jzfb->framedesc->next = jzfb->framedesc_phys;
+ jzfb->framedesc->addr = jzfb->vidmem_phys;
+ jzfb->framedesc->id = 0xdeafbead;
+ jzfb->framedesc->cmd = 0;
+ jzfb->framedesc->cmd |= max_videosize / 4;
+ } else {
+ struct jz4740_dma_config config = {
+ .src_width = JZ4740_DMA_WIDTH_32BIT,
+ .request_type = JZ4740_DMA_TYPE_SLCD,
+ .flags = JZ4740_DMA_SRC_AUTOINC,
+ .mode = JZ4740_DMA_MODE_SINGLE,
+ };
+
+ switch (jzfb->pdata->bpp) {
+ case 1 ... 8:
+ config.dst_width = JZ4740_DMA_WIDTH_8BIT;
+ config.transfer_size = JZ4740_DMA_TRANSFER_SIZE_1BYTE;
+ break;
+ case 9 ... 16:
+ config.dst_width = JZ4740_DMA_WIDTH_16BIT;
+ config.transfer_size = JZ4740_DMA_TRANSFER_SIZE_2BYTE;
+ break;
+ case 17 ... 32:
+ config.dst_width = JZ4740_DMA_WIDTH_32BIT;
+ config.transfer_size = JZ4740_DMA_TRANSFER_SIZE_4BYTE;
+ break;
+ }
+
+ jz4740_dma_configure(jzfb->slcd_dma, &config);
+ jz4740_dma_set_complete_cb(jzfb->slcd_dma,
+ jzfb_slcd_dma_callback);
+ }
return 0;
err_free_framedesc:
- dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc),
- jzfb->framedesc, jzfb->framedesc_phys);
+ if (jzfb->framedesc)
+ dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc),
+ jzfb->framedesc, jzfb->framedesc_phys);
+ if (jzfb->slcd_dma)
+ jz4740_dma_free(jzfb->slcd_dma);
return -ENOMEM;
}
@@ -604,8 +837,11 @@ static void jzfb_free_devmem(struct jzfb *jzfb)
{
dma_free_coherent(&jzfb->pdev->dev, jzfb->vidmem_size,
jzfb->vidmem, jzfb->vidmem_phys);
- dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc),
- jzfb->framedesc, jzfb->framedesc_phys);
+ if (jzfb->framedesc)
+ dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc),
+ jzfb->framedesc, jzfb->framedesc_phys);
+ if (jzfb->slcd_dma)
+ jz4740_dma_free(jzfb->slcd_dma);
}
static struct fb_ops jzfb_ops = {
@@ -619,6 +855,350 @@ static struct fb_ops jzfb_ops = {
.fb_setcolreg = jzfb_setcolreg,
};
+#define JZ_SLCD_CFG_DWIDTH_MASK (0x7 << 10)
+#define JZ_SLCD_CFG_CWIDTH_MASK (0x3 << 8)
+
+/* Send a command without data. */
+static void send_panel_command(struct jzfb *jzfb, u32 cmd)
+{
+ u16 slcd_cfg = readw(jzfb->base + JZ_REG_SLCD_CFG);
+
+ switch (slcd_cfg & JZ_SLCD_CFG_CWIDTH_MASK) {
+ case JZ_SLCD_CFG_CWIDTH_8BIT:
+ jzfb_slcd_wait(jzfb);
+ writel(JZ_SLCD_DATA_RS_COMMAND | ((cmd & 0xff00) >> 8), jzfb->base + JZ_REG_SLCD_DATA);
+ jzfb_slcd_wait(jzfb);
+ writel(JZ_SLCD_DATA_RS_COMMAND | ((cmd & 0xff) >> 0), jzfb->base + JZ_REG_SLCD_DATA);
+ break;
+
+ case JZ_SLCD_CFG_CWIDTH_16BIT:
+ jzfb_slcd_wait(jzfb);
+ writel(JZ_SLCD_DATA_RS_COMMAND | (cmd & 0xffff), jzfb->base + JZ_REG_SLCD_DATA);
+ break;
+
+ case JZ_SLCD_CFG_CWIDTH_18BIT:
+ jzfb_slcd_wait(jzfb);
+ writel(JZ_SLCD_DATA_RS_COMMAND | ((cmd & 0xff00) << 2) | ((cmd & 0xff) << 1), jzfb->base + JZ_REG_SLCD_DATA);
+ break;
+ }
+}
+
+static void send_panel_data(struct jzfb *jzfb, u32 data)
+{
+ u16 slcd_cfg = readw(jzfb->base + JZ_REG_SLCD_CFG);
+
+ switch (slcd_cfg & JZ_SLCD_CFG_DWIDTH_MASK) {
+ case JZ_SLCD_CFG_DWIDTH_18:
+ data = ((data & 0xff) << 1) | ((data & 0xff00) << 2);
+ data = ((data << 6) & 0xfc0000) | ((data << 4) & 0xfc00) | ((data << 2) & 0xfc);
+ break;
+
+ case JZ_SLCD_CFG_DWIDTH_9_x2:
+ data = ((data & 0xff) << 1) | ((data & 0xff00) << 2);
+ data = ((data << 6) & 0xfc0000) | ((data << 4) & 0xfc00) | ((data << 2) & 0xfc);
+ break;
+
+ case JZ_SLCD_CFG_DWIDTH_16:
+ default:
+ data &= 0xffff;
+ break;
+ }
+
+ jzfb_slcd_wait(jzfb);
+ writel(JZ_SLCD_DATA_RS_DATA | data, jzfb->base + JZ_REG_SLCD_DATA);
+}
+
+void jz4740_fb_slcd_disable_transfer(struct platform_device *pdev)
+{
+ struct jzfb *jzfb = platform_get_drvdata(pdev);
+
+ mutex_lock(&jzfb->lock);
+
+ if (jzfb->is_enabled) {
+ jz4740_dma_disable(jzfb->slcd_dma);
+ jzfb_slcd_wait(jzfb);
+ }
+
+ mutex_unlock(&jzfb->lock);
+}
+
+void jz4740_fb_slcd_enable_transfer(struct platform_device *pdev)
+{
+ struct jzfb *jzfb = platform_get_drvdata(pdev);
+
+ mutex_lock(&jzfb->lock);
+
+ if (jzfb->is_enabled)
+ jzfb_slcd_start_dma(jzfb);
+
+ mutex_unlock(&jzfb->lock);
+}
+
+void jz4740_fb_slcd_send_cmd_data(struct platform_device *pdev,
+ unsigned int cmd, unsigned int data)
+{
+ struct jzfb *jzfb = platform_get_drvdata(pdev);
+
+ mutex_lock(&jzfb->lock);
+
+ if (!jzfb->is_enabled)
+ clk_enable(jzfb->ldclk);
+
+ send_panel_command(jzfb, cmd);
+ send_panel_data(jzfb, data);
+
+ if (!jzfb->is_enabled) {
+ jzfb_slcd_wait(jzfb);
+ clk_disable(jzfb->ldclk);
+ }
+
+ mutex_unlock(&jzfb->lock);
+}
+
+void jz4740_fb_slcd_send_cmd(struct platform_device *pdev, unsigned int cmd)
+{
+ struct jzfb *jzfb = platform_get_drvdata(pdev);
+
+ mutex_lock(&jzfb->lock);
+
+ if (!jzfb->is_enabled)
+ clk_enable(jzfb->ldclk);
+
+ send_panel_command(jzfb, cmd);
+
+ if (!jzfb->is_enabled) {
+ jzfb_slcd_wait(jzfb);
+ clk_disable(jzfb->ldclk);
+ }
+
+ mutex_unlock(&jzfb->lock);
+}
+
static int __devinit jzfb_probe(struct platform_device *pdev)
{
int ret;
@@ -655,6 +1235,7 @@ static int __devinit jzfb_probe(struct platform_device *pdev)
fb->flags = FBINFO_DEFAULT;
jzfb = fb->par;
+ jzfb->fb = fb;
jzfb->pdev = pdev;
jzfb->pdata = pdata;
jzfb->mem = mem;
@@ -673,6 +1254,7 @@ static int __devinit jzfb_probe(struct platform_device *pdev)
goto err_put_ldclk;
}
+ jzfb->phys_base = mem->start;
jzfb->base = ioremap(mem->start, resource_size(mem));
if (!jzfb->base) {
dev_err(&pdev->dev, "Failed to ioremap register memory region\n");
@@ -707,16 +1289,14 @@ static int __devinit jzfb_probe(struct platform_device *pdev)
fb_alloc_cmap(&fb->cmap, 256, 0);
- clk_enable(jzfb->ldclk);
- jzfb->is_enabled = 1;
-
- writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0);
-
fb->mode = NULL;
jzfb_set_par(fb);
- jz_gpio_bulk_request(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
- jz_gpio_bulk_request(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
+ jzfb_pins_operation(jzfb, REQUEST_PINS);
+
+ jzfb_blank(FB_BLANK_UNBLANK, fb);
ret = register_framebuffer(fb);
if (ret) {
@@ -724,13 +1304,11 @@ static int __devinit jzfb_probe(struct platform_device *pdev)
goto err_free_devmem;
}
- jzfb->fb = fb;
-
return 0;
err_free_devmem:
- jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
- jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
+ jzfb_disable(jzfb);
+ jzfb_pins_operation(jzfb, FREE_PINS);
fb_dealloc_cmap(&fb->cmap);
jzfb_free_devmem(jzfb);
@@ -753,8 +1331,7 @@ static int __devexit jzfb_remove(struct platform_device *pdev)
jzfb_blank(FB_BLANK_POWERDOWN, jzfb->fb);
- jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
- jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb));
+ jzfb_pins_operation(jzfb, FREE_PINS);
iounmap(jzfb->base);
release_mem_region(jzfb->mem->start, resource_size(jzfb->mem));
@@ -785,6 +1362,7 @@ static int jzfb_suspend(struct device *dev)
mutex_lock(&jzfb->lock);
if (jzfb->is_enabled)
jzfb_disable(jzfb);
+ jzfb_pins_operation(jzfb, SUSPEND_PINS);
mutex_unlock(&jzfb->lock);
return 0;
@@ -795,6 +1373,7 @@ static int jzfb_resume(struct device *dev)
struct jzfb *jzfb = dev_get_drvdata(dev);
mutex_lock(&jzfb->lock);
+ jzfb_pins_operation(jzfb, RESUME_PINS);
if (jzfb->is_enabled)
jzfb_enable(jzfb);
mutex_unlock(&jzfb->lock);
new file mode 100644
@@ -0,0 +1,31 @@
+#ifndef __VIDEO_TRULY_G240400RTSW_H__
+#define __VIDEO_TRULY_G240400RTSW_H__
+
+struct platform_device;
+struct fb_videomode;
+
+struct g240400_pdata {
+ struct platform_device *jz4740_fb;
+ struct fb_videomode *default_mode;
+ int gpio_reset;
+ int gpio_cs;
+};
+
+#endif /* __VIDEO_TRULY_G240400RTSW_H__ */