18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * KFR2R09 LCD panel support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Magnus Damm 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Register settings based on the out-of-tree t33fb.c driver 88c2ecf20Sopenharmony_ci * Copyright (C) 2008 Lineo Solutions, Inc. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/fb.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/gpio.h> 188c2ecf20Sopenharmony_ci#include <video/sh_mobile_lcdc.h> 198c2ecf20Sopenharmony_ci#include <mach/kfr2r09.h> 208c2ecf20Sopenharmony_ci#include <cpu/sh7724.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* The on-board LCD module is a Hitachi TX07D34VM0AAA. This module is made 238c2ecf20Sopenharmony_ci * up of a 240x400 LCD hooked up to a R61517 driver IC. The driver IC is 248c2ecf20Sopenharmony_ci * communicating with the main port of the LCDC using an 18-bit SYS interface. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * The device code for this LCD module is 0x01221517. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic const unsigned char data_frame_if[] = { 308c2ecf20Sopenharmony_ci 0x02, /* WEMODE: 1=cont, 0=one-shot */ 318c2ecf20Sopenharmony_ci 0x00, 0x00, 328c2ecf20Sopenharmony_ci 0x00, /* EPF, DFM */ 338c2ecf20Sopenharmony_ci 0x02, /* RIM[1] : 1 (18bpp) */ 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic const unsigned char data_panel[] = { 378c2ecf20Sopenharmony_ci 0x0b, 388c2ecf20Sopenharmony_ci 0x63, /* 400 lines */ 398c2ecf20Sopenharmony_ci 0x04, 0x00, 0x00, 0x04, 0x11, 0x00, 0x00, 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic const unsigned char data_timing[] = { 438c2ecf20Sopenharmony_ci 0x00, 0x00, 0x13, 0x08, 0x08, 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic const unsigned char data_timing_src[] = { 478c2ecf20Sopenharmony_ci 0x11, 0x01, 0x00, 0x01, 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic const unsigned char data_gamma[] = { 518c2ecf20Sopenharmony_ci 0x01, 0x02, 0x08, 0x23, 0x03, 0x0c, 0x00, 0x06, 0x00, 0x00, 528c2ecf20Sopenharmony_ci 0x01, 0x00, 0x0c, 0x23, 0x03, 0x08, 0x02, 0x06, 0x00, 0x00, 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic const unsigned char data_power[] = { 568c2ecf20Sopenharmony_ci 0x07, 0xc5, 0xdc, 0x02, 0x33, 0x0a, 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic unsigned long read_reg(void *sohandle, 608c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_sys_bus_ops *so) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci return so->read_data(sohandle); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void write_reg(void *sohandle, 668c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_sys_bus_ops *so, 678c2ecf20Sopenharmony_ci int i, unsigned long v) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci if (i) 708c2ecf20Sopenharmony_ci so->write_data(sohandle, v); /* PTH4/LCDRS High [param, 17:0] */ 718c2ecf20Sopenharmony_ci else 728c2ecf20Sopenharmony_ci so->write_index(sohandle, v); /* PTH4/LCDRS Low [cmd, 7:0] */ 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void write_data(void *sohandle, 768c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_sys_bus_ops *so, 778c2ecf20Sopenharmony_ci unsigned char const *data, int no_data) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci int i; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci for (i = 0; i < no_data; i++) 828c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, data[i]); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic unsigned long read_device_code(void *sohandle, 868c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_sys_bus_ops *so) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci unsigned long device_code; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* access protect OFF */ 918c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xb0); 928c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* deep standby OFF */ 958c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xb1); 968c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* device code command */ 998c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xbf); 1008c2ecf20Sopenharmony_ci mdelay(50); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* dummy read */ 1038c2ecf20Sopenharmony_ci read_reg(sohandle, so); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* read device code */ 1068c2ecf20Sopenharmony_ci device_code = ((read_reg(sohandle, so) & 0xff) << 24); 1078c2ecf20Sopenharmony_ci device_code |= ((read_reg(sohandle, so) & 0xff) << 16); 1088c2ecf20Sopenharmony_ci device_code |= ((read_reg(sohandle, so) & 0xff) << 8); 1098c2ecf20Sopenharmony_ci device_code |= (read_reg(sohandle, so) & 0xff); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return device_code; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void write_memory_start(void *sohandle, 1158c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_sys_bus_ops *so) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0x2c); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void clear_memory(void *sohandle, 1218c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_sys_bus_ops *so) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int i; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* write start */ 1268c2ecf20Sopenharmony_ci write_memory_start(sohandle, so); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* paint it black */ 1298c2ecf20Sopenharmony_ci for (i = 0; i < (240 * 400); i++) 1308c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void display_on(void *sohandle, 1348c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_sys_bus_ops *so) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci /* access protect off */ 1378c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xb0); 1388c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* exit deep standby mode */ 1418c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xb1); 1428c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* frame memory I/F */ 1458c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xb3); 1468c2ecf20Sopenharmony_ci write_data(sohandle, so, data_frame_if, ARRAY_SIZE(data_frame_if)); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* display mode and frame memory write mode */ 1498c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xb4); 1508c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); /* DBI, internal clock */ 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* panel */ 1538c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xc0); 1548c2ecf20Sopenharmony_ci write_data(sohandle, so, data_panel, ARRAY_SIZE(data_panel)); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* timing (normal) */ 1578c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xc1); 1588c2ecf20Sopenharmony_ci write_data(sohandle, so, data_timing, ARRAY_SIZE(data_timing)); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* timing (partial) */ 1618c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xc2); 1628c2ecf20Sopenharmony_ci write_data(sohandle, so, data_timing, ARRAY_SIZE(data_timing)); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* timing (idle) */ 1658c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xc3); 1668c2ecf20Sopenharmony_ci write_data(sohandle, so, data_timing, ARRAY_SIZE(data_timing)); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* timing (source/VCOM/gate driving) */ 1698c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xc4); 1708c2ecf20Sopenharmony_ci write_data(sohandle, so, data_timing_src, ARRAY_SIZE(data_timing_src)); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* gamma (red) */ 1738c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xc8); 1748c2ecf20Sopenharmony_ci write_data(sohandle, so, data_gamma, ARRAY_SIZE(data_gamma)); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* gamma (green) */ 1778c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xc9); 1788c2ecf20Sopenharmony_ci write_data(sohandle, so, data_gamma, ARRAY_SIZE(data_gamma)); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* gamma (blue) */ 1818c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xca); 1828c2ecf20Sopenharmony_ci write_data(sohandle, so, data_gamma, ARRAY_SIZE(data_gamma)); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* power (common) */ 1858c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xd0); 1868c2ecf20Sopenharmony_ci write_data(sohandle, so, data_power, ARRAY_SIZE(data_power)); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* VCOM */ 1898c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xd1); 1908c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 1918c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x0f); 1928c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x02); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* power (normal) */ 1958c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xd2); 1968c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x63); 1978c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x24); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* power (partial) */ 2008c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xd3); 2018c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x63); 2028c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x24); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* power (idle) */ 2058c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xd4); 2068c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x63); 2078c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x24); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0xd8); 2108c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x77); 2118c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x77); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* TE signal */ 2148c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0x35); 2158c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* TE signal line */ 2188c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0x44); 2198c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 2208c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* column address */ 2238c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0x2a); 2248c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 2258c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 2268c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 2278c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0xef); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* page address */ 2308c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0x2b); 2318c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 2328c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x00); 2338c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x01); 2348c2ecf20Sopenharmony_ci write_reg(sohandle, so, 1, 0x8f); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* exit sleep mode */ 2378c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0x11); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci mdelay(120); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* clear vram */ 2428c2ecf20Sopenharmony_ci clear_memory(sohandle, so); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* display ON */ 2458c2ecf20Sopenharmony_ci write_reg(sohandle, so, 0, 0x29); 2468c2ecf20Sopenharmony_ci mdelay(1); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci write_memory_start(sohandle, so); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ciint kfr2r09_lcd_setup(void *sohandle, struct sh_mobile_lcdc_sys_bus_ops *so) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci /* power on */ 2548c2ecf20Sopenharmony_ci gpio_set_value(GPIO_PTF4, 0); /* PROTECT/ -> L */ 2558c2ecf20Sopenharmony_ci gpio_set_value(GPIO_PTE4, 0); /* LCD_RST/ -> L */ 2568c2ecf20Sopenharmony_ci gpio_set_value(GPIO_PTF4, 1); /* PROTECT/ -> H */ 2578c2ecf20Sopenharmony_ci udelay(1100); 2588c2ecf20Sopenharmony_ci gpio_set_value(GPIO_PTE4, 1); /* LCD_RST/ -> H */ 2598c2ecf20Sopenharmony_ci udelay(10); 2608c2ecf20Sopenharmony_ci gpio_set_value(GPIO_PTF4, 0); /* PROTECT/ -> L */ 2618c2ecf20Sopenharmony_ci mdelay(20); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (read_device_code(sohandle, so) != 0x01221517) 2648c2ecf20Sopenharmony_ci return -ENODEV; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci pr_info("KFR2R09 WQVGA LCD Module detected.\n"); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci display_on(sohandle, so); 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_civoid kfr2r09_lcd_start(void *sohandle, struct sh_mobile_lcdc_sys_bus_ops *so) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci write_memory_start(sohandle, so); 2758c2ecf20Sopenharmony_ci} 276