| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include <common.h> |
| #include <malloc.h> |
| #include <amlogic/cpu_id.h> |
| #include <fdtdec.h> |
| #include <amlogic/keyunify.h> |
| #include <amlogic/media/vout/aml_vout.h> |
| #include <amlogic/media/vout/lcd/aml_lcd.h> |
| #ifdef CONFIG_AML_LCD_EXTERN |
| #include <amlogic/media/vout/lcd/lcd_extern.h> |
| #endif |
| #include "lcd_reg.h" |
| #include "lcd_common.h" |
| |
| #define PANEL_NAME "panel" |
| |
| //DECLARE_GLOBAL_DATA_PTR; |
| |
| unsigned int lcd_debug_print_flag; |
| unsigned int lcd_debug_test; |
| struct aml_lcd_data_s *lcd_data; |
| static struct aml_lcd_drv_s *lcd_driver[LCD_MAX_DRV]; |
| static struct lcd_debug_ctrl_s debug_ctrl; |
| |
| static struct aml_lcd_data_s lcd_data_g12a = { |
| .chip_type = LCD_CHIP_G12A, |
| .chip_name = "g12a", |
| .rev_type = 0, |
| .drv_max = 1, |
| .offset_venc = {0}, |
| .offset_venc_if = {0}, |
| .offset_venc_data = {0}, |
| .dft_conf = {NULL, NULL, NULL}, |
| }; |
| |
| static struct aml_lcd_data_s lcd_data_g12b = { |
| .chip_type = LCD_CHIP_G12B, |
| .chip_name = "g12b", |
| .rev_type = 0, |
| .drv_max = 1, |
| .offset_venc = {0}, |
| .offset_venc_if = {0}, |
| .offset_venc_data = {0}, |
| .dft_conf = {NULL, NULL, NULL}, |
| }; |
| |
| static struct aml_lcd_data_s lcd_data_tl1 = { |
| .chip_type = LCD_CHIP_TL1, |
| .chip_name = "tl1", |
| .rev_type = 0, |
| .drv_max = 1, |
| .offset_venc = {0}, |
| .offset_venc_if = {0}, |
| .offset_venc_data = {0}, |
| .dft_conf = {NULL, NULL, NULL}, |
| }; |
| |
| static struct aml_lcd_data_s lcd_data_sm1 = { |
| .chip_type = LCD_CHIP_SM1, |
| .chip_name = "sm1", |
| .rev_type = 0, |
| .drv_max = 1, |
| .offset_venc = {0}, |
| .offset_venc_if = {0}, |
| .offset_venc_data = {0}, |
| .dft_conf = {NULL, NULL, NULL}, |
| }; |
| |
| static struct aml_lcd_data_s lcd_data_tm2 = { |
| .chip_type = LCD_CHIP_TM2, |
| .chip_name = "tm2", |
| .rev_type = 0, |
| .drv_max = 1, |
| .offset_venc = {0}, |
| .offset_venc_if = {0}, |
| .offset_venc_data = {0}, |
| .dft_conf = {NULL, NULL, NULL}, |
| }; |
| |
| static struct aml_lcd_data_s lcd_data_t5 = { |
| .chip_type = LCD_CHIP_T5, |
| .chip_name = "t5", |
| .rev_type = 0, |
| .drv_max = 1, |
| .offset_venc = {0}, |
| .offset_venc_if = {0}, |
| .offset_venc_data = {0}, |
| .dft_conf = {NULL, NULL, NULL}, |
| }; |
| |
| static struct aml_lcd_data_s lcd_data_t5d = { |
| .chip_type = LCD_CHIP_T5D, |
| .chip_name = "t5d", |
| .rev_type = 0, |
| .drv_max = 1, |
| .offset_venc = {0}, |
| .offset_venc_if = {0}, |
| .offset_venc_data = {0}, |
| .dft_conf = {NULL, NULL, NULL}, |
| }; |
| |
| static struct aml_lcd_data_s lcd_data_t7 = { |
| .chip_type = LCD_CHIP_T7, |
| .chip_name = "t7", |
| .rev_type = 0, |
| .drv_max = 3, |
| .offset_venc = {0x0, (0x600 << 2), (0x800 << 2)}, |
| .offset_venc_if = {0x0, (0x500 << 2), (0x600 << 2)}, |
| .offset_venc_data = {0x0, (0x100 << 2), (0x200 << 2)}, |
| .dft_conf = {NULL, NULL, NULL}, |
| }; |
| |
| static void lcd_chip_detect(void) |
| { |
| #if 1 |
| unsigned int cpu_type; |
| unsigned int rev_type; |
| |
| cpu_type = get_cpu_id().family_id; |
| rev_type = get_cpu_id().chip_rev; |
| switch (cpu_type) { |
| case MESON_CPU_MAJOR_ID_G12A: |
| lcd_data = &lcd_data_g12a; |
| break; |
| case MESON_CPU_MAJOR_ID_G12B: |
| lcd_data = &lcd_data_g12b; |
| break; |
| case MESON_CPU_MAJOR_ID_TL1: |
| lcd_data = &lcd_data_tl1; |
| break; |
| case MESON_CPU_MAJOR_ID_SM1: |
| lcd_data = &lcd_data_sm1; |
| break; |
| case MESON_CPU_MAJOR_ID_TM2: |
| lcd_data = &lcd_data_tm2; |
| break; |
| case MESON_CPU_MAJOR_ID_T5: |
| lcd_data = &lcd_data_t5; |
| break; |
| case MESON_CPU_MAJOR_ID_T5D: |
| lcd_data = &lcd_data_t5d; |
| break; |
| case MESON_CPU_MAJOR_ID_T7: |
| lcd_data = &lcd_data_t7; |
| break; |
| default: |
| lcd_data = NULL; |
| return; |
| } |
| lcd_data->rev_type = rev_type; |
| #else |
| lcd_data = &lcd_data_t7; |
| #endif |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) { |
| LCDPR("check chip: %d %s\n", |
| lcd_data->chip_type, lcd_data->chip_name); |
| } |
| } |
| |
| struct aml_lcd_data_s *aml_lcd_get_data(void) |
| { |
| return lcd_data; |
| } |
| |
| static struct aml_lcd_drv_s *lcd_driver_check_valid(int index) |
| { |
| if (!lcd_driver[index] || !lcd_driver[index]->config_check) { |
| LCDERR("invalid lcd%d config\n", index); |
| return NULL; |
| } |
| return lcd_driver[index]; |
| } |
| |
| struct aml_lcd_drv_s *aml_lcd_get_driver(int index) |
| { |
| return lcd_driver_check_valid(index); |
| } |
| |
| static void lcd_power_ctrl(struct aml_lcd_drv_s *pdrv, int status) |
| { |
| struct lcd_power_ctrl_s *lcd_power; |
| struct lcd_power_step_s *power_step; |
| char *str; |
| unsigned int i, wait, gpio; |
| int value = LCD_PMU_GPIO_NUM_MAX; |
| #ifdef CONFIG_AML_LCD_EXTERN |
| struct lcd_extern_driver_s *ext_drv; |
| #endif |
| i = 0; |
| lcd_power = &pdrv->config.power; |
| if (status) { |
| /* check if factory test */ |
| if (pdrv->factory_lcd_power_on_step) { |
| LCDPR("[%d]: %s: factory test power_on_step!\n", |
| pdrv->index, __func__); |
| power_step = pdrv->factory_lcd_power_on_step; |
| } else { |
| power_step = &lcd_power->power_on_step[0]; |
| } |
| } else { |
| power_step = &lcd_power->power_off_step[0]; |
| } |
| |
| while (i < LCD_PWR_STEP_MAX) { |
| if (power_step->type >= LCD_POWER_TYPE_MAX) |
| break; |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) { |
| LCDPR("[%d]: power_ctrl: %d, step %d: type=%d, index=%d, value=%d, delay=%d\n", |
| pdrv->index, status, i, |
| power_step->type, power_step->index, |
| power_step->value, power_step->delay); |
| } |
| switch (power_step->type) { |
| case LCD_POWER_TYPE_CPU: |
| if (power_step->index < LCD_CPU_GPIO_NUM_MAX) { |
| str = lcd_power->cpu_gpio[power_step->index]; |
| gpio = lcd_gpio_name_map_num(str); |
| lcd_gpio_set(gpio, power_step->value); |
| } else { |
| LCDERR("[%d]: invalid cpu_gpio index: %d\n", |
| pdrv->index, power_step->index); |
| } |
| break; |
| case LCD_POWER_TYPE_PMU: |
| if (power_step->index < LCD_PMU_GPIO_NUM_MAX) |
| LCDPR("to do\n"); |
| else |
| LCDERR("pmu_gpio invalid index: %d\n", |
| power_step->index); |
| break; |
| case LCD_POWER_TYPE_SIGNAL: |
| if (status) |
| pdrv->driver_init(pdrv); |
| else |
| pdrv->driver_disable(pdrv); |
| break; |
| #ifdef CONFIG_AML_LCD_EXTERN |
| case LCD_POWER_TYPE_EXTERN: |
| ext_drv = lcd_extern_get_driver(power_step->index); |
| if (!ext_drv) { |
| LCDERR("no ext_drv\n"); |
| break; |
| } |
| if (status) { |
| if (ext_drv->power_on) |
| ext_drv->power_on(); |
| else |
| LCDERR("no ext power on\n"); |
| } else { |
| if (ext_drv->power_off) |
| ext_drv->power_off(); |
| else |
| LCDERR("no ext power off\n"); |
| } |
| break; |
| #endif |
| case LCD_POWER_TYPE_WAIT_GPIO: |
| if (power_step->index < LCD_CPU_GPIO_NUM_MAX) { |
| str = lcd_power->cpu_gpio[power_step->index]; |
| gpio = lcd_gpio_name_map_num(str); |
| lcd_gpio_set(gpio, LCD_GPIO_INPUT); |
| } else { |
| LCDERR("[%d]: wait_gpio index: %d\n", |
| pdrv->index, power_step->index); |
| break; |
| } |
| LCDPR("[%d]: lcd_power_type_wait_gpio wait\n", pdrv->index); |
| for (wait = 0; wait < power_step->delay; wait++) { |
| value = lcd_gpio_input_get(gpio); |
| if (value == power_step->value) { |
| LCDPR("[%d]: get value: %d, wait ok\n", |
| pdrv->index, value); |
| break; |
| } |
| mdelay(1); |
| } |
| if (wait == power_step->delay) { |
| LCDERR("[%d]: get value: %d, wait timeout!\n", |
| pdrv->index, value); |
| } |
| break; |
| case LCD_POWER_TYPE_CLK_SS: |
| break; |
| #ifdef CONFIG_AML_LCD_TCON |
| case LCD_POWER_TYPE_TCON_SPI_DATA_LOAD: |
| if (pdrv->lcd_tcon_spi_data_load) |
| pdrv->lcd_tcon_spi_data_load(); |
| break; |
| #endif |
| default: |
| break; |
| } |
| |
| if (power_step->type != LCD_POWER_TYPE_WAIT_GPIO) { |
| #ifndef CONFIG_AML_LCD_PXP |
| if (power_step->delay > 0) |
| mdelay(power_step->delay); |
| #endif |
| } |
| i++; |
| power_step++; |
| } |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s: %d\n", pdrv->index, __func__, status); |
| } |
| |
| static void lcd_encl_on(struct aml_lcd_drv_s *pdrv) |
| { |
| pdrv->driver_init_pre(pdrv); |
| if (lcd_debug_test) |
| aml_lcd_debug_test(pdrv, lcd_debug_test); |
| |
| pdrv->status |= LCD_STATUS_ENCL_ON; |
| } |
| |
| static void lcd_interface_on(struct aml_lcd_drv_s *pdrv) |
| { |
| lcd_power_ctrl(pdrv, 1); |
| pdrv->config.retry_enable_cnt = 0; |
| while (pdrv->config.retry_enable_flag) { |
| if (pdrv->config.retry_enable_cnt++ >= LCD_ENABLE_RETRY_MAX) |
| break; |
| LCDPR("[%d]: retry enable...%d\n", |
| pdrv->index, pdrv->config.retry_enable_cnt); |
| lcd_power_ctrl(pdrv, 0); |
| mdelay(1000); |
| lcd_power_ctrl(pdrv, 1); |
| } |
| pdrv->config.retry_enable_cnt = 0; |
| pdrv->status |= LCD_STATUS_IF_ON; |
| } |
| |
| static void lcd_module_enable(struct aml_lcd_drv_s *pdrv, char *mode, unsigned int frac) |
| { |
| unsigned int sync_duration; |
| struct lcd_config_s *pconf; |
| int ret; |
| |
| pconf = &pdrv->config; |
| ret = pdrv->config_check(pdrv, mode, frac); |
| if (ret) { |
| LCDERR("[%d]: init exit\n", pdrv->index); |
| return; |
| } |
| |
| sync_duration = pconf->timing.sync_duration_num; |
| sync_duration = (sync_duration * 100 / |
| pconf->timing.sync_duration_den); |
| LCDPR("[%d]: enable: %s, %s, %ux%u@%u.%02uHz\n", |
| pdrv->index, pconf->basic.model_name, |
| lcd_type_type_to_str(pconf->basic.lcd_type), |
| pconf->basic.h_active, pconf->basic.v_active, |
| (sync_duration / 100), (sync_duration % 100)); |
| |
| if ((pdrv->status & LCD_STATUS_ENCL_ON) == 0) |
| lcd_encl_on(pdrv); |
| if ((pdrv->status & LCD_STATUS_IF_ON) == 0) { |
| if (pdrv->boot_ctrl.init_level == LCD_INIT_LEVEL_NORMAL) { |
| lcd_interface_on(pdrv); |
| #ifdef CONFIG_AML_LCD_BACKLIGHT |
| aml_bl_driver_enable(pdrv->index); |
| #endif |
| } |
| } |
| if (!lcd_debug_test) |
| lcd_mute_setting(pdrv, 0); |
| } |
| |
| static void lcd_module_disable(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int offset; |
| |
| LCDPR("[%d]: disable: %s\n", pdrv->index, pdrv->config.basic.model_name); |
| |
| offset = pdrv->data->offset_venc[pdrv->index]; |
| |
| lcd_mute_setting(pdrv, 1); |
| if (pdrv->status & LCD_STATUS_IF_ON) { |
| #ifdef CONFIG_AML_LCD_BACKLIGHT |
| aml_bl_driver_disable(pdrv->index); |
| #endif |
| lcd_power_ctrl(pdrv, 0); |
| } |
| |
| lcd_vcbus_write(ENCL_VIDEO_EN + offset, 0); |
| lcd_disable_clk(pdrv); |
| pdrv->status = 0; |
| } |
| |
| static void lcd_module_prepare(struct aml_lcd_drv_s *pdrv, |
| char *mode, unsigned int frac) |
| { |
| int ret; |
| |
| ret = pdrv->config_check(pdrv, mode, frac); |
| if (ret) { |
| LCDERR("[%d]: prepare exit\n", pdrv->index); |
| return; |
| } |
| |
| if ((pdrv->status & LCD_STATUS_ENCL_ON) == 0) |
| lcd_encl_on(pdrv); |
| } |
| |
| #ifdef CONFIG_AML_LCD_EXTERN |
| static int lcd_extern_load_config(char *dt_addr, struct lcd_config_s *pconf) |
| { |
| struct lcd_power_step_s *power_step; |
| int index, i = 0; |
| |
| /* mipi extern_init is special */ |
| if (pconf->lcd_basic.lcd_type == LCD_MIPI) { |
| index = pconf->lcd_control.mipi_config->extern_init; |
| if (index < LCD_EXTERN_INDEX_INVALID) |
| lcd_extern_probe(dt_addr, index); |
| } |
| |
| while (i < LCD_PWR_STEP_MAX) { |
| power_step = &pconf->lcd_power->power_on_step[i]; |
| if (power_step->type >= LCD_POWER_TYPE_MAX) |
| break; |
| if (power_step->type == LCD_POWER_TYPE_EXTERN) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) { |
| LCDPR("power_on: step %d: type=%d, index=%d\n", |
| i, power_step->type, power_step->index); |
| } |
| index = power_step->index; |
| if (index < LCD_EXTERN_INDEX_INVALID) |
| lcd_extern_probe(dt_addr, index); |
| } |
| i++; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| static int lcd_init_load_from_dts(char *dt_addr, struct aml_lcd_drv_s *pdrv) |
| { |
| #ifdef CONFIG_OF_LIBFDT |
| struct lcd_config_s *pconf = &pdrv->config; |
| int parent_offset; |
| char *propdata, *p, snode[10]; |
| const char *str; |
| unsigned int temp; |
| int i, j; |
| |
| if (pdrv->index == 0) |
| sprintf(snode, "/lcd"); |
| else |
| sprintf(snode, "/lcd%d", pdrv->index); |
| parent_offset = fdt_path_offset(dt_addr, snode); |
| if (parent_offset < 0) { |
| LCDERR("[%d]: not find %s node: %s\n", |
| pdrv->index, snode, fdt_strerror(parent_offset)); |
| return -1; |
| } |
| sprintf(snode, "lcd%d", pdrv->index); |
| |
| /* check lcd_mode & lcd_key_valid */ |
| propdata = (char *)fdt_getprop(dt_addr, parent_offset, "mode", NULL); |
| if (!propdata) { |
| LCDERR("[%d]: failed to get mode\n", pdrv->index); |
| return -1; |
| } else { |
| pdrv->mode = lcd_mode_str_to_mode(propdata); |
| } |
| propdata = (char *)fdt_getprop(dt_addr, parent_offset, "key_valid", NULL); |
| if (!propdata) { |
| LCDERR("[%d]: failed to get key_valid\n", pdrv->index); |
| pdrv->key_valid = 0; |
| } else { |
| pdrv->key_valid = (unsigned char)(be32_to_cpup((u32*)propdata)); |
| } |
| |
| /* check lcd_clk_path */ |
| propdata = (char *)fdt_getprop(dt_addr, parent_offset, "clk_path", NULL); |
| if (!propdata) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: failed to get clk_path\n", pdrv->index); |
| pdrv->clk_path = 0; |
| } else { |
| pdrv->clk_path = (unsigned char)(be32_to_cpup((u32 *)propdata)); |
| } |
| LCDPR("[%d]: detect mode: %s, key_valid: %d, clk_path: %d\n", |
| pdrv->index, lcd_mode_mode_to_str(pdrv->mode), |
| pdrv->key_valid, pdrv->clk_path); |
| |
| temp = env_get_ulong("lcd_clk_path", 10, 0xffff); |
| if (temp != 0xffff) { |
| if (temp) |
| pdrv->clk_path = 1; |
| else |
| pdrv->clk_path = 0; |
| LCDPR("[%d]: lcd_clk_path flag set clk_path: %d\n", |
| pdrv->index, pdrv->clk_path); |
| } |
| |
| i = 0; |
| propdata = (char *)fdt_getprop(dt_addr, parent_offset, |
| "lcd_cpu_gpio_names", NULL); |
| if (!propdata) { |
| LCDPR("[%d]: failed to get lcd_cpu_gpio_names\n", pdrv->index); |
| } else { |
| p = propdata; |
| while (i < LCD_CPU_GPIO_NUM_MAX) { |
| str = p; |
| if (strlen(str) == 0) |
| break; |
| strcpy(pconf->power.cpu_gpio[i], str); |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) { |
| LCDPR("[%d]: i=%d, gpio=%s\n", |
| pdrv->index, i, pconf->power.cpu_gpio[i]); |
| } |
| p += strlen(p) + 1; |
| i++; |
| } |
| } |
| for (j = i; j < LCD_CPU_GPIO_NUM_MAX; j++) |
| strcpy(pconf->power.cpu_gpio[j], "invalid"); |
| |
| #endif |
| return 0; |
| } |
| |
| static int lcd_init_load_from_bsp(struct aml_lcd_drv_s *pdrv) |
| { |
| struct lcd_dft_config_s *dft_conf; |
| unsigned int temp; |
| char snode[10]; |
| char (*lcd_gpio)[LCD_CPU_GPIO_NAME_MAX]; |
| int i, j; |
| |
| dft_conf = lcd_data->dft_conf[pdrv->index]; |
| if (!dft_conf) { |
| LCDERR("%s: dft_conf is NULL\n", __func__); |
| return -1; |
| } |
| sprintf(snode, "lcd%d", pdrv->index); |
| |
| pdrv->mode = dft_conf->mode; |
| pdrv->key_valid = dft_conf->key_valid; |
| pdrv->clk_path = dft_conf->clk_path; |
| LCDPR("[%d]: detect mode: %s, key_valid: %d, clk_path: %d\n", |
| pdrv->index, lcd_mode_mode_to_str(pdrv->mode), |
| pdrv->key_valid, pdrv->clk_path); |
| |
| temp = env_get_ulong("lcd_clk_path", 10, 0xffff); |
| if (temp != 0xffff) { |
| if (temp) |
| pdrv->clk_path = 1; |
| else |
| pdrv->clk_path = 0; |
| LCDPR("[%d]: lcd_clk_path flag set clk_path: %d\n", |
| pdrv->index, pdrv->clk_path); |
| } |
| |
| i = 0; |
| lcd_gpio = pdrv->data->dft_conf[pdrv->index]->lcd_gpio; |
| if (!lcd_gpio) { |
| LCDERR("[%d]: %s lcd_gpio is null\n", pdrv->index, __func__); |
| return -1; |
| } |
| while (i < LCD_CPU_GPIO_NUM_MAX) { |
| if (strcmp(lcd_gpio[i], "invalid") == 0) |
| break; |
| strcpy(pdrv->config.power.cpu_gpio[i], lcd_gpio[i]); |
| i++; |
| } |
| for (j = i; j < LCD_CPU_GPIO_NUM_MAX; j++) |
| strcpy(pdrv->config.power.cpu_gpio[j], "invalid"); |
| |
| return 0; |
| } |
| |
| static int lcd_mode_init(struct aml_lcd_drv_s *pdrv) |
| { |
| int ret = -1; |
| |
| switch (debug_ctrl.debug_lcd_mode) { |
| case 1: |
| LCDPR("[%d]: lcd_debug_mode: 1,tv\n", pdrv->index); |
| pdrv->mode = LCD_MODE_TV; |
| break; |
| case 2: |
| LCDPR("[%d]: lcd_debug_mode: 2,tablet\n", pdrv->index); |
| pdrv->mode = LCD_MODE_TABLET; |
| break; |
| default: |
| break; |
| } |
| |
| switch (pdrv->mode) { |
| #ifdef CONFIG_AML_LCD_TV |
| case LCD_MODE_TV: |
| ret = lcd_mode_tv_init(pdrv); |
| break; |
| #endif |
| #ifdef CONFIG_AML_LCD_TABLET |
| case LCD_MODE_TABLET: |
| ret = lcd_mode_tablet_init(pdrv); |
| break; |
| #endif |
| default: |
| LCDERR("[%d]: invalid lcd mode: %d\n", pdrv->index, pdrv->mode); |
| break; |
| } |
| |
| if (ret) { |
| pdrv->config_check = NULL; |
| LCDERR("[%d]: %s: invalid config\n", pdrv->index, __func__); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static unsigned int lcd_get_drv_cnt_flag_from_dts(char *dt_addr) |
| { |
| #ifdef CONFIG_OF_LIBFDT |
| int parent_offset; |
| char str[10]; |
| unsigned int i, flag = 0; |
| |
| for (i = 0; i < lcd_data->drv_max; i++) { |
| if (i == 0) |
| sprintf(str, "/lcd"); |
| else |
| sprintf(str, "/lcd%d", i); |
| |
| parent_offset = fdt_path_offset(dt_addr, str); |
| if (parent_offset < 0) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("not find %s node\n", str); |
| } else { |
| flag |= (1 << i); |
| } |
| } |
| |
| return flag; |
| #else |
| return 0; |
| #endif |
| } |
| |
| static unsigned int lcd_get_drv_cnt_flag_from_bsp(void) |
| { |
| unsigned int i, flag = 0; |
| |
| for (i = 0; i < lcd_data->drv_max; i++) { |
| if (!lcd_data->dft_conf[i]) |
| continue; |
| if (lcd_data->dft_conf[i]->ext_lcd) { |
| flag |= (1 << i); |
| } else { |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("not find ext_lcd[%d]\n", i); |
| } |
| } |
| |
| return flag; |
| } |
| |
| static struct aml_lcd_drv_s *lcd_driver_add(int index) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| if (index >= lcd_data->drv_max) { |
| LCDERR("%s: invalid index: %d\n", __func__, index); |
| return NULL; |
| } |
| if (!lcd_data->dft_conf[index]) { |
| LCDERR("%s: no lcd%d dft_conf\n", __func__, index); |
| return NULL; |
| } |
| if (!lcd_driver[index]) { |
| lcd_driver[index] = (struct aml_lcd_drv_s *) |
| malloc(sizeof(struct aml_lcd_drv_s)); |
| if (!lcd_driver[index]) { |
| LCDERR("%s: Not enough memory\n", __func__); |
| return NULL; |
| } |
| } |
| pdrv = lcd_driver[index]; |
| memset(pdrv, 0, sizeof(struct aml_lcd_drv_s)); |
| pdrv->index = index; |
| |
| /* default config */ |
| pdrv->data = lcd_data; |
| pdrv->config.basic.lcd_type = LCD_TYPE_MAX; |
| pdrv->config.power.power_on_step[0].type = LCD_POWER_TYPE_MAX; |
| pdrv->config.power.power_off_step[0].type = LCD_POWER_TYPE_MAX; |
| pdrv->config.pinmux_set[0][0] = LCD_PINMUX_END; |
| pdrv->config.pinmux_set[0][1] = 0x0; |
| pdrv->config.pinmux_clr[0][0] = LCD_PINMUX_END; |
| pdrv->config.pinmux_clr[0][1] = 0x0; |
| pdrv->config.backlight_index = 0xff; |
| |
| /* default setting */ |
| pdrv->config.retry_enable_flag = 0; |
| pdrv->config.retry_enable_cnt = 0; |
| |
| return pdrv; |
| } |
| |
| static int lcd_driver_remove(int index) |
| { |
| if (index >= lcd_data->drv_max) |
| return 0; |
| |
| if (!lcd_driver[index]) |
| return 0; |
| |
| free(lcd_driver[index]); |
| lcd_driver[index] = NULL; |
| |
| return 0; |
| } |
| |
| static void lcd_update_ctrl_bootargs(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int val = 0; |
| char env_str[15], ctrl_str[20]; |
| |
| pdrv->boot_ctrl.lcd_type = pdrv->config.basic.lcd_type; |
| pdrv->boot_ctrl.lcd_bits = pdrv->config.basic.lcd_bits; |
| switch (pdrv->config.basic.lcd_type) { |
| case LCD_TTL: |
| pdrv->boot_ctrl.advanced_flag = |
| pdrv->config.control.ttl_cfg.sync_valid; |
| break; |
| case LCD_P2P: |
| pdrv->boot_ctrl.advanced_flag = |
| pdrv->config.control.p2p_cfg.p2p_type; |
| break; |
| default: |
| break; |
| } |
| pdrv->boot_ctrl.init_level = env_get_ulong("lcd_init_level", 10, 0); |
| |
| /* |
| *bit[31:20]: reserved |
| *bit[19:18]: lcd_init_level |
| *bit[17:16]: reserved |
| *bit[15:8]: advanced flag(p2p_type when lcd_type=p2p) |
| *bit[7:4]: lcd bits |
| *bit[3:0]: lcd_type |
| */ |
| val |= (pdrv->boot_ctrl.lcd_type & 0xf); |
| val |= (pdrv->boot_ctrl.lcd_bits & 0xf) << 4; |
| val |= (pdrv->boot_ctrl.advanced_flag & 0xff) << 8; |
| val |= (pdrv->boot_ctrl.init_level & 0x3) << 18; |
| sprintf(ctrl_str, "0x%08x", val); |
| |
| if (pdrv->index == 0) |
| sprintf(env_str, "lcd_ctrl"); |
| else |
| sprintf(env_str, "lcd%d_ctrl", pdrv->index); |
| env_set(env_str, ctrl_str); |
| } |
| |
| static void lcd_update_debug_bootargs(void) |
| { |
| unsigned int val = 0; |
| char ctrl_str[20]; |
| |
| debug_ctrl.debug_print_flag = lcd_debug_print_flag; |
| debug_ctrl.debug_test_pattern = lcd_debug_test; |
| debug_ctrl.debug_para_source = env_get_ulong("lcd_debug_para", 10, 0); |
| debug_ctrl.debug_lcd_mode = env_get_ulong("lcd_debug_mode", 10, 0); |
| |
| /* |
| *bit[31:30]: lcd mode(0=normal, 1=tv; 2=tablet, 3=TBD) |
| *bit[29:28]: lcd debug para source(0=normal, 1=dts, 2=unifykey, |
| * 3=bsp for uboot) |
| *bit[27:16]: reserved |
| *bit[15:8]: lcd test pattern |
| *bit[7:0]: lcd debug print flag |
| */ |
| val |= (debug_ctrl.debug_print_flag & 0xff); |
| val |= (debug_ctrl.debug_test_pattern & 0xff) << 8; |
| val |= (debug_ctrl.debug_para_source & 0x3) << 28; |
| val |= (debug_ctrl.debug_lcd_mode & 0x3) << 30; |
| sprintf(ctrl_str, "0x%08x", val); |
| env_set("lcd_debug", ctrl_str); |
| } |
| |
| static int lcd_config_probe(void) |
| { |
| int load_id = 0, load_id_lcd; |
| char *dt_addr = NULL; |
| struct aml_lcd_drv_s *pdrv; |
| unsigned int drv_cnt_flag; |
| int i, ret; |
| |
| #ifdef CONFIG_DTB_MEM_ADDR |
| dt_addr = (char *)CONFIG_DTB_MEM_ADDR; |
| #else |
| dt_addr = (char *)0x01000000; |
| #endif |
| |
| #ifdef CONFIG_OF_LIBFDT |
| if (fdt_check_header(dt_addr) < 0) { |
| LCDERR("check dts: %s, load default lcd parameters\n", |
| fdt_strerror(fdt_check_header(dt_addr))); |
| } else { |
| load_id = 0x1; |
| } |
| #endif |
| |
| switch (debug_ctrl.debug_para_source) { |
| case 1: |
| LCDPR("lcd_debug_para: 1,dts\n"); |
| load_id = 0x1; |
| break; |
| case 2: |
| LCDPR("lcd_debug_para: 2,unifykey\n"); |
| load_id = 0x11; |
| break; |
| case 3: |
| LCDPR("lcd_debug_para: 3,bsp\n"); |
| load_id = 0x0; |
| break; |
| default: |
| break; |
| } |
| |
| load_id_lcd = load_id; |
| if (load_id & 0x1) { |
| drv_cnt_flag = lcd_get_drv_cnt_flag_from_dts(dt_addr); |
| if (drv_cnt_flag == 0) { |
| LCDPR("not find /lcd node\n"); |
| drv_cnt_flag = lcd_get_drv_cnt_flag_from_bsp(); |
| load_id_lcd = 0x0; |
| } |
| } else { |
| drv_cnt_flag = lcd_get_drv_cnt_flag_from_bsp(); |
| } |
| |
| if (load_id_lcd & 0x1) { |
| for (i = 0; i < lcd_data->drv_max; i++) { |
| if ((drv_cnt_flag & (1 << i)) == 0) |
| continue; |
| pdrv = lcd_driver_add(i); |
| if (!pdrv) |
| continue; |
| |
| ret = lcd_init_load_from_dts(dt_addr, pdrv); |
| if (ret) { |
| lcd_driver_remove(i); |
| continue; |
| } |
| lcd_clk_config_probe(pdrv); |
| ret = lcd_get_config(dt_addr, load_id_lcd, pdrv); |
| if (ret) { |
| lcd_driver_remove(i); |
| continue; |
| } |
| lcd_phy_probe(pdrv); |
| lcd_debug_probe(pdrv); |
| lcd_update_ctrl_bootargs(pdrv); |
| lcd_mode_init(pdrv); |
| } |
| } else { |
| for (i = 0; i < lcd_data->drv_max; i++) { |
| if ((drv_cnt_flag & (1 << i)) == 0) |
| continue; |
| pdrv = lcd_driver_add(i); |
| if (!pdrv) |
| continue; |
| |
| ret = lcd_init_load_from_bsp(pdrv); |
| if (ret) { |
| lcd_driver_remove(i); |
| continue; |
| } |
| lcd_clk_config_probe(pdrv); |
| ret = lcd_get_config(dt_addr, load_id_lcd, pdrv); |
| if (ret) { |
| lcd_driver_remove(i); |
| continue; |
| } |
| lcd_phy_probe(pdrv); |
| lcd_debug_probe(pdrv); |
| lcd_update_ctrl_bootargs(pdrv); |
| lcd_mode_init(pdrv); |
| } |
| } |
| |
| #ifdef CONFIG_AML_LCD_EXTERN |
| lcd_extern_load_config(dt_addr, load_id); |
| #endif |
| #ifdef CONFIG_AML_LCD_BACKLIGHT |
| aml_bl_probe(dt_addr, load_id); |
| #endif |
| |
| return 0; |
| } |
| |
| int lcd_probe(void) |
| { |
| int ret = 0; |
| |
| lcd_debug_print_flag = env_get_ulong("lcd_debug_print", 16, 0); |
| LCDPR("lcd_debug_print flag: %d\n", lcd_debug_print_flag); |
| |
| lcd_debug_test = env_get_ulong("lcd_debug_test", 10, 0); |
| |
| debug_ctrl.debug_print_flag = lcd_debug_print_flag; |
| debug_ctrl.debug_test_pattern = lcd_debug_test; |
| debug_ctrl.debug_para_source = env_get_ulong("lcd_debug_para", 10, 0); |
| debug_ctrl.debug_lcd_mode = env_get_ulong("lcd_debug_mode", 10, 0); |
| |
| #ifdef CONFIG_AML_LCD_EXTERN |
| lcd_extern_init(); |
| #endif |
| #ifdef CONFIG_AML_LCD_BACKLIGHT |
| aml_bl_init(); |
| #endif |
| |
| lcd_chip_detect(); |
| if (!lcd_data) { |
| LCDERR("%s: invalid lcd data\n", __func__); |
| return -1; |
| } |
| lcd_config_bsp_init(); |
| lcd_phy_config_init(lcd_data); |
| ret = lcd_config_probe(); |
| if (ret) |
| return -1; |
| |
| lcd_update_debug_bootargs(); |
| |
| return 0; |
| } |
| |
| int lcd_remove(void) |
| { |
| int i; |
| |
| if (!lcd_data) |
| return 0; |
| |
| aml_bl_remove(); |
| #ifdef CONFIG_AML_LCD_EXTERN |
| lcd_extern_remove(); |
| #endif |
| |
| for (i = 0; i < LCD_MAX_DRV; i++) { |
| if (lcd_driver[i]) { |
| free(lcd_driver[i]); |
| lcd_driver[i] = NULL; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* ********************************************** * |
| lcd driver API |
| * ********************************************** */ |
| int aml_lcd_driver_probe(int index) |
| { |
| return lcd_probe(); |
| } |
| |
| /*********************************************** |
| * use for vout |
| ************************************************/ |
| void aml_lcd_driver_list_support_mode(void) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| int index; |
| |
| for (index = 0; index < LCD_MAX_DRV; index++) { |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| continue; |
| |
| if (pdrv->list_support_mode) |
| pdrv->list_support_mode(&pdrv->config); |
| } |
| } |
| |
| /*********************************************** |
| * use for vout |
| * parameters: mode, such as panel, panel2, 1080p60hz... |
| * frac, 1=59.94hz |
| * return: viu_mux |
| ************************************************/ |
| unsigned int aml_lcd_driver_outputmode_check(char *mode, unsigned int frac) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| unsigned int viu_mux = VIU_MUX_MAX; |
| int index, ret; |
| |
| for (index = 0; index < LCD_MAX_DRV; index++) { |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| continue; |
| |
| if (pdrv->outputmode_check) { |
| ret = pdrv->outputmode_check(pdrv, mode, frac); |
| if (ret == 0) { |
| viu_mux = ((pdrv->index << 4) | VIU_MUX_ENCL); |
| break; |
| } |
| } |
| } |
| |
| return viu_mux; |
| } |
| |
| void aml_lcd_driver_prepare(int index, char *mode, unsigned int frac) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| |
| if (pdrv->status & LCD_STATUS_ENCL_ON) { |
| LCDPR("[%d]: already enabled\n", pdrv->index); |
| return; |
| } |
| |
| lcd_module_prepare(pdrv, mode, frac); |
| } |
| |
| void aml_lcd_driver_enable(int index, char *mode, unsigned int frac) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| |
| if (pdrv->status & LCD_STATUS_IF_ON) { |
| LCDPR("[%d]: already enabled\n", pdrv->index); |
| return; |
| } |
| |
| lcd_module_enable(pdrv, mode, frac); |
| } |
| |
| void aml_lcd_driver_disable(int index) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| |
| if ((pdrv->status & LCD_STATUS_ENCL_ON) == 0) { |
| LCDPR("[%d]: already disabled\n", pdrv->index); |
| return; |
| } |
| |
| lcd_module_disable(pdrv); |
| } |
| |
| void aml_lcd_driver_set_ss(int index, unsigned int level, unsigned int freq, |
| unsigned int mode) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| unsigned int temp; |
| int ret; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| |
| if ((pdrv->status & LCD_STATUS_ENCL_ON) == 0) { |
| LCDPR("[%d]: already disabled\n", pdrv->index); |
| return; |
| } |
| |
| temp = pdrv->config.timing.ss_level; |
| ret = lcd_set_ss(pdrv, level, freq, mode); |
| if (ret == 0) { |
| if (level < 0xff) { |
| temp &= ~(0xff); |
| temp |= level; |
| pdrv->config.timing.ss_level = temp; |
| } |
| if (freq < 0xff) { |
| temp &= ~((0xf << LCD_CLK_SS_BIT_FREQ) << 8); |
| temp |= ((freq << LCD_CLK_SS_BIT_FREQ) << 8); |
| pdrv->config.timing.ss_level = temp; |
| } |
| if (mode < 0xff) { |
| temp &= ~((0xf << LCD_CLK_SS_BIT_MODE) << 8); |
| temp |= ((mode << LCD_CLK_SS_BIT_MODE) << 8); |
| pdrv->config.timing.ss_level = temp; |
| } |
| } |
| } |
| |
| void aml_lcd_driver_get_ss(int index) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| |
| if ((pdrv->status & LCD_STATUS_ENCL_ON) == 0) { |
| LCDPR("[%d]: already disabled\n", pdrv->index); |
| return; |
| } |
| |
| lcd_get_ss(pdrv); |
| } |
| |
| void aml_lcd_driver_test(int index, int num) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| |
| if (num == 10) { |
| lcd_display_init_test(pdrv); |
| return; |
| } else if (num == 20) { |
| lcd_display_init_reg_dump(pdrv); |
| return; |
| } |
| |
| if ((pdrv->status & LCD_STATUS_IF_ON) == 0) { |
| LCDPR("[%d]: already disabled\n", pdrv->index); |
| return; |
| } |
| |
| aml_lcd_debug_test(pdrv, num); |
| } |
| |
| void aml_lcd_driver_clk_info(int index) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| |
| lcd_clk_config_print(pdrv); |
| } |
| |
| void aml_lcd_driver_info(int index) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| |
| lcd_info_print(pdrv); |
| } |
| |
| void aml_lcd_driver_reg_info(int index) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| |
| lcd_reg_print(pdrv); |
| } |
| |
| void aml_lcd_vbyone_rst(int index) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| |
| lcd_vbyone_rst(pdrv); |
| } |
| |
| void aml_lcd_vbyone_cdr(int index) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| |
| lcd_vbyone_cdr(pdrv); |
| } |
| |
| void aml_lcd_driver_bl_on(int index) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| #ifdef CONFIG_AML_LCD_BACKLIGHT |
| aml_bl_driver_enable(pdrv->index); |
| #endif |
| } |
| |
| void aml_lcd_driver_bl_off(int index) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| #ifdef CONFIG_AML_LCD_BACKLIGHT |
| aml_bl_driver_disable(pdrv->index); |
| #endif |
| } |
| |
| void aml_lcd_driver_set_bl_level(int index, int level) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| #ifdef CONFIG_AML_LCD_BACKLIGHT |
| aml_bl_set_level(pdrv->index, level); |
| #endif |
| } |
| |
| unsigned int aml_lcd_driver_get_bl_level(int index) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return 0; |
| #ifdef CONFIG_AML_LCD_BACKLIGHT |
| return aml_bl_get_level(pdrv->index); |
| #endif |
| return 0; |
| } |
| |
| void aml_lcd_driver_bl_config_print(int index) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return; |
| #ifdef CONFIG_AML_LCD_BACKLIGHT |
| aml_bl_config_print(pdrv->index); |
| #endif |
| } |
| |
| int aml_lcd_driver_prbs(int index, unsigned int s, unsigned int mode_flag) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = lcd_driver_check_valid(index); |
| if (!pdrv) |
| return 0; |
| |
| return lcd_prbs_test(pdrv, s, mode_flag); |
| } |
| |
| void aml_lcd_driver_unifykey_dump(int index, unsigned int flag) |
| { |
| unsigned int key_flag = LCD_UKEY_DEBUG_NORMAL; |
| |
| if (flag & (1 << 0)) { |
| key_flag = LCD_UKEY_DEBUG_NORMAL; |
| } else if (flag & (1 << 1)) { |
| #ifdef CONFIG_AML_LCD_TCON |
| key_flag = (LCD_UKEY_DEBUG_TCON | LCD_UKEY_TCON_SIZE_NEW); |
| #endif |
| } |
| lcd_unifykey_dump(index, key_flag); |
| } |