From 38c4f8ba4c27fd0c7eb02fa1cdafc3e87e533573 Mon Sep 17 00:00:00 2001 From: Aleksei Mamlin Date: Thu, 30 Jul 2015 20:33:56 +0300 Subject: sunxi: Enable USB DRC on Wexler TAB7200 Enable the otg/drc usb controller on the Wexler TAB7200 tablet. Signed-off-by: Aleksei Mamlin Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede diff --git a/configs/Wexler_TAB7200_defconfig b/configs/Wexler_TAB7200_defconfig index eadceb2..02504f9 100644 --- a/configs/Wexler_TAB7200_defconfig +++ b/configs/Wexler_TAB7200_defconfig @@ -2,10 +2,15 @@ CONFIG_ARM=y CONFIG_ARCH_SUNXI=y CONFIG_MACH_SUN7I=y CONFIG_DRAM_CLK=384 +CONFIG_USB0_VBUS_PIN="PB9" +CONFIG_USB0_VBUS_DET="AXP0-VBUS-DETECT" +CONFIG_USB0_ID_DET="PH4" +CONFIG_AXP_GPIO=y CONFIG_VIDEO_LCD_MODE="x:800,y:480,depth:24,pclk_khz:33000,le:45,ri:210,up:22,lo:22,hs:1,vs:1,sync:3,vmode:0" CONFIG_VIDEO_LCD_POWER="PH8" CONFIG_VIDEO_LCD_BL_EN="PH7" CONFIG_VIDEO_LCD_BL_PWM="PB2" +CONFIG_USB_MUSB_HOST=y CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-wexler-tab7200" # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL=y -- cgit v0.10.2 From ad008299712bbcaf822ebac0f4ab88069e1a1372 Mon Sep 17 00:00:00 2001 From: Karol Gugala Date: Thu, 23 Jul 2015 14:33:01 +0200 Subject: sunxi: nand: Add pinmux and clock settings for NAND support To enable NAND flash in sunxi SPL, pins 0-6, 8-22 and 24 on port C are configured. Signed-off-by: Karol Gugala Signed-off-by: Piotr Zierhoffer Acked-by: Hans de Goede Signed-off-by: Hans de Goede diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h index 58aff16..01d3e28 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h @@ -267,6 +267,8 @@ struct sunxi_ccm_reg { #define CCM_MBUS_CTRL_CLK_SRC_PLL5 0x2 #define CCM_MBUS_CTRL_GATE (0x1 << 31) +#define CCM_NAND_CTRL_ENABLE (0x1 << 31) + #define CCM_MMC_CTRL_M(x) ((x) - 1) #define CCM_MMC_CTRL_OCLK_DLY(x) ((x) << 8) #define CCM_MMC_CTRL_N(x) ((x) << 16) diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h index 8e67b3b..8382101 100644 --- a/arch/arm/include/asm/arch-sunxi/gpio.h +++ b/arch/arm/include/asm/arch-sunxi/gpio.h @@ -158,6 +158,7 @@ enum sunxi_gpio_number { #define SUN8I_GPB_UART2 2 #define SUN8I_A33_GPB_UART0 3 +#define SUNXI_GPC_NAND 2 #define SUNXI_GPC_SDC2 3 #define SUN6I_GPC_SDC3 4 diff --git a/board/sunxi/board.c b/board/sunxi/board.c index afed6a3..f85e825 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -107,6 +107,28 @@ int dram_init(void) return 0; } +#if defined(CONFIG_SPL_NAND_SUNXI) && defined(CONFIG_SPL_BUILD) +static void nand_pinmux_setup(void) +{ + unsigned int pin; + for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(6); pin++) + sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_NAND); + + for (pin = SUNXI_GPC(8); pin <= SUNXI_GPC(22); pin++) + sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_NAND); + + sunxi_gpio_set_cfgpin(SUNXI_GPC(24), SUNXI_GPC_NAND); +} + +static void nand_clock_setup(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + setbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0)); + setbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1); +} +#endif + #ifdef CONFIG_GENERIC_MMC static void mmc_pinmux_setup(int sdc) { @@ -431,6 +453,11 @@ void sunxi_board_init(void) power_failed |= axp221_set_eldo(3, CONFIG_AXP221_ELDO3_VOLT); #endif +#ifdef CONFIG_SPL_NAND_SUNXI + nand_pinmux_setup(); + nand_clock_setup(); +#endif + printf("DRAM:"); ramsize = sunxi_dram_init(); printf(" %lu MiB\n", ramsize >> 20); -- cgit v0.10.2 From aeb4b0d3b35c9eb361bcffee9c48e9bd8debd5b9 Mon Sep 17 00:00:00 2001 From: Piotr Zierhoffer Date: Thu, 23 Jul 2015 14:33:02 +0200 Subject: sunxi: nand: Add basic sunxi NAND driver for SPL with DMA support This driver adds NAND support to SPL. It was tested on Allwinner A20. Signed-off-by: Peter Gielda Signed-off-by: Tomasz Gorochowik Signed-off-by: Mateusz Holenko Signed-off-by: Piotr Zierhoffer Signed-off-by: Karol Gugala Acked-by: Hans de Goede Signed-off-by: Hans de Goede diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 347ea62..b40dda8 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -12,6 +12,7 @@ NORMAL_DRIVERS=y endif obj-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o +obj-$(CONFIG_SPL_NAND_SUNXI) += sunxi_nand_spl.o obj-$(CONFIG_SPL_NAND_DENALI) += denali_spl.o obj-$(CONFIG_SPL_NAND_DOCG4) += docg4_spl.o obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o diff --git a/drivers/mtd/nand/sunxi_nand_spl.c b/drivers/mtd/nand/sunxi_nand_spl.c new file mode 100644 index 0000000..ac5f56d --- /dev/null +++ b/drivers/mtd/nand/sunxi_nand_spl.c @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2014-2015, Antmicro Ltd + * Copyright (c) 2015, AW-SOM Technologies + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/* registers */ +#define NFC_CTL 0x00000000 +#define NFC_ST 0x00000004 +#define NFC_INT 0x00000008 +#define NFC_TIMING_CTL 0x0000000C +#define NFC_TIMING_CFG 0x00000010 +#define NFC_ADDR_LOW 0x00000014 +#define NFC_ADDR_HIGH 0x00000018 +#define NFC_SECTOR_NUM 0x0000001C +#define NFC_CNT 0x00000020 +#define NFC_CMD 0x00000024 +#define NFC_RCMD_SET 0x00000028 +#define NFC_WCMD_SET 0x0000002C +#define NFC_IO_DATA 0x00000030 +#define NFC_ECC_CTL 0x00000034 +#define NFC_ECC_ST 0x00000038 +#define NFC_DEBUG 0x0000003C +#define NFC_ECC_CNT0 0x00000040 +#define NFC_ECC_CNT1 0x00000044 +#define NFC_ECC_CNT2 0x00000048 +#define NFC_ECC_CNT3 0x0000004C +#define NFC_USER_DATA_BASE 0x00000050 +#define NFC_EFNAND_STATUS 0x00000090 +#define NFC_SPARE_AREA 0x000000A0 +#define NFC_PATTERN_ID 0x000000A4 +#define NFC_RAM0_BASE 0x00000400 +#define NFC_RAM1_BASE 0x00000800 + +#define NFC_CTL_EN (1 << 0) +#define NFC_CTL_RESET (1 << 1) +#define NFC_CTL_RAM_METHOD (1 << 14) + + +#define NFC_ECC_EN (1 << 0) +#define NFC_ECC_PIPELINE (1 << 3) +#define NFC_ECC_EXCEPTION (1 << 4) +#define NFC_ECC_BLOCK_SIZE (1 << 5) +#define NFC_ECC_RANDOM_EN (1 << 9) +#define NFC_ECC_RANDOM_DIRECTION (1 << 10) + + +#define NFC_ADDR_NUM_OFFSET 16 +#define NFC_SEND_ADR (1 << 19) +#define NFC_ACCESS_DIR (1 << 20) +#define NFC_DATA_TRANS (1 << 21) +#define NFC_SEND_CMD1 (1 << 22) +#define NFC_WAIT_FLAG (1 << 23) +#define NFC_SEND_CMD2 (1 << 24) +#define NFC_SEQ (1 << 25) +#define NFC_DATA_SWAP_METHOD (1 << 26) +#define NFC_ROW_AUTO_INC (1 << 27) +#define NFC_SEND_CMD3 (1 << 28) +#define NFC_SEND_CMD4 (1 << 29) + +#define NFC_CMD_INT_FLAG (1 << 1) + +#define NFC_READ_CMD_OFFSET 0 +#define NFC_RANDOM_READ_CMD0_OFFSET 8 +#define NFC_RANDOM_READ_CMD1_OFFSET 16 + +#define NFC_CMD_RNDOUTSTART 0xE0 +#define NFC_CMD_RNDOUT 0x05 +#define NFC_CMD_READSTART 0x30 + + +#define NFC_PAGE_CMD (2 << 30) + +#define SUNXI_DMA_CFG_REG0 0x300 +#define SUNXI_DMA_SRC_START_ADDR_REG0 0x304 +#define SUNXI_DMA_DEST_START_ADDRR_REG0 0x308 +#define SUNXI_DMA_DDMA_BC_REG0 0x30C +#define SUNXI_DMA_DDMA_PARA_REG0 0x318 + +#define SUNXI_DMA_DDMA_CFG_REG_LOADING (1 << 31) +#define SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 (2 << 25) +#define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 (2 << 9) +#define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO (1 << 5) +#define SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC (3 << 0) + +#define SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC (0x0F << 0) +#define SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE (0x7F << 8) + +/* minimal "boot0" style NAND support for Allwinner A20 */ + +/* temporary buffer in internal ram */ +unsigned char temp_buf[CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE] + __aligned(0x10) __section(".text#"); + +/* random seed used by linux */ +const uint16_t random_seed[128] = { + 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, + 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, + 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, + 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, + 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, + 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, + 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, + 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, + 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, + 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, + 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, + 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, + 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, + 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, + 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, + 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, +}; + +/* random seed used for syndrome calls */ +const uint16_t random_seed_syndrome = 0x4a80; + +#define MAX_RETRIES 10 + +static int check_value_inner(int offset, int expected_bits, + int max_number_of_retries, int negation) +{ + int retries = 0; + do { + int val = readl(offset) & expected_bits; + if (negation ? !val : val) + return 1; + mdelay(1); + retries++; + } while (retries < max_number_of_retries); + + return 0; +} + +static inline int check_value(int offset, int expected_bits, + int max_number_of_retries) +{ + return check_value_inner(offset, expected_bits, + max_number_of_retries, 0); +} + +static inline int check_value_negated(int offset, int unexpected_bits, + int max_number_of_retries) +{ + return check_value_inner(offset, unexpected_bits, + max_number_of_retries, 1); +} + +void nand_init(void) +{ + uint32_t val; + + val = readl(SUNXI_NFC_BASE + NFC_CTL); + /* enable and reset CTL */ + writel(val | NFC_CTL_EN | NFC_CTL_RESET, + SUNXI_NFC_BASE + NFC_CTL); + + if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL, + NFC_CTL_RESET, MAX_RETRIES)) { + printf("Couldn't initialize nand\n"); + } +} + +static void nand_read_page(unsigned int real_addr, int syndrome, + uint32_t *ecc_errors) +{ + uint32_t val; + int ecc_off = 0; + uint16_t ecc_mode = 0; + uint16_t rand_seed; + uint32_t page; + uint16_t column; + uint32_t oob_offset; + + switch (CONFIG_NAND_SUNXI_SPL_ECC_STRENGTH) { + case 16: + ecc_mode = 0; + ecc_off = 0x20; + break; + case 24: + ecc_mode = 1; + ecc_off = 0x2e; + break; + case 28: + ecc_mode = 2; + ecc_off = 0x32; + break; + case 32: + ecc_mode = 3; + ecc_off = 0x3c; + break; + case 40: + ecc_mode = 4; + ecc_off = 0x4a; + break; + case 48: + ecc_mode = 4; + ecc_off = 0x52; + break; + case 56: + ecc_mode = 4; + ecc_off = 0x60; + break; + case 60: + ecc_mode = 4; + ecc_off = 0x0; + break; + case 64: + ecc_mode = 4; + ecc_off = 0x0; + break; + default: + ecc_mode = 0; + ecc_off = 0; + } + + if (ecc_off == 0) { + printf("Unsupported ECC strength (%d)!\n", + CONFIG_NAND_SUNXI_SPL_ECC_STRENGTH); + return; + } + + /* clear temp_buf */ + memset(temp_buf, 0, CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE); + + /* set CMD */ + writel(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET, + SUNXI_NFC_BASE + NFC_CMD); + + if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_CMD_INT_FLAG, + MAX_RETRIES)) { + printf("Error while initilizing command interrupt\n"); + return; + } + + page = real_addr / CONFIG_NAND_SUNXI_SPL_PAGE_SIZE; + column = real_addr % CONFIG_NAND_SUNXI_SPL_PAGE_SIZE; + + if (syndrome) + column += (column / CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE) + * ecc_off; + + /* clear ecc status */ + writel(0, SUNXI_NFC_BASE + NFC_ECC_ST); + + /* Choose correct seed */ + if (syndrome) + rand_seed = random_seed_syndrome; + else + rand_seed = random_seed[page % 128]; + + writel((rand_seed << 16) | NFC_ECC_RANDOM_EN | NFC_ECC_EN + | NFC_ECC_PIPELINE | (ecc_mode << 12), + SUNXI_NFC_BASE + NFC_ECC_CTL); + + val = readl(SUNXI_NFC_BASE + NFC_CTL); + writel(val | NFC_CTL_RAM_METHOD, SUNXI_NFC_BASE + NFC_CTL); + + if (syndrome) { + writel(CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE, + SUNXI_NFC_BASE + NFC_SPARE_AREA); + } else { + oob_offset = CONFIG_NAND_SUNXI_SPL_PAGE_SIZE + + (column / CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE) + * ecc_off; + writel(oob_offset, SUNXI_NFC_BASE + NFC_SPARE_AREA); + } + + /* SUNXI_DMA */ + writel(0x0, SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); /* clr dma cmd */ + /* read from REG_IO_DATA */ + writel(SUNXI_NFC_BASE + NFC_IO_DATA, + SUNXI_DMA_BASE + SUNXI_DMA_SRC_START_ADDR_REG0); + /* read to RAM */ + writel((uint32_t)temp_buf, + SUNXI_DMA_BASE + SUNXI_DMA_DEST_START_ADDRR_REG0); + writel(SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC + | SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE, + SUNXI_DMA_BASE + SUNXI_DMA_DDMA_PARA_REG0); + writel(CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE, + SUNXI_DMA_BASE + SUNXI_DMA_DDMA_BC_REG0); /* 1kB */ + writel(SUNXI_DMA_DDMA_CFG_REG_LOADING + | SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 + | SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 + | SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO + | SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC, + SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); + + writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) + | (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) + | (NFC_CMD_READSTART | NFC_READ_CMD_OFFSET), SUNXI_NFC_BASE + + NFC_RCMD_SET); + writel(1, SUNXI_NFC_BASE + NFC_SECTOR_NUM); + writel(((page & 0xFFFF) << 16) | column, + SUNXI_NFC_BASE + NFC_ADDR_LOW); + writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH); + writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_DATA_TRANS | + NFC_PAGE_CMD | NFC_WAIT_FLAG | (4 << NFC_ADDR_NUM_OFFSET) | + NFC_SEND_ADR | NFC_DATA_SWAP_METHOD | (syndrome ? NFC_SEQ : 0), + SUNXI_NFC_BASE + NFC_CMD); + + if (!check_value(SUNXI_NFC_BASE + NFC_ST, (1 << 2), + MAX_RETRIES)) { + printf("Error while initializing dma interrupt\n"); + return; + } + + if (!check_value_negated(SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0, + SUNXI_DMA_DDMA_CFG_REG_LOADING, MAX_RETRIES)) { + printf("Error while waiting for dma transfer to finish\n"); + return; + } + + if (readl(SUNXI_NFC_BASE + NFC_ECC_ST)) + (*ecc_errors)++; +} + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) +{ + void *current_dest; + uint32_t count; + uint32_t current_count; + uint32_t ecc_errors = 0; + + memset(dest, 0x0, size); /* clean destination memory */ + for (current_dest = dest; + current_dest < (dest + size); + current_dest += CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE) { + nand_read_page(offs, offs + < CONFIG_NAND_SUNXI_SPL_SYNDROME_PARTITIONS_END, + &ecc_errors); + count = current_dest - dest; + + if (size - count > CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE) + current_count = CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE; + else + current_count = size - count; + + memcpy(current_dest, + temp_buf, + current_count); + offs += CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE; + } + return ecc_errors ? -1 : 0; +} + +void nand_deselect(void) {} -- cgit v0.10.2 From 960caeba8f76baffc7201b7535de964f406d48b3 Mon Sep 17 00:00:00 2001 From: Piotr Zierhoffer Date: Thu, 23 Jul 2015 14:33:03 +0200 Subject: sunxi: nand: Add board configuration options When SPL_NAND_SUNXI option is selected in config, set some configuration options for sunxi NAND. This commit also introduces the configurable options in Kconfig. Signed-off-by: Peter Gielda Signed-off-by: Tomasz Gorochowik Signed-off-by: Mateusz Holenko Signed-off-by: Piotr Zierhoffer Signed-off-by: Karol Gugala Acked-by: Hans de Goede Signed-off-by: Hans de Goede diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 3024357..41ebfea 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -85,6 +85,46 @@ config SPL_NAND_DENALI This is a small implementation of the Denali NAND controller for use on SPL. +config SPL_NAND_SUNXI + bool "Support for NAND on Allwinner A20 in SPL" + depends on MACH_SUN7I + ---help--- + Enable support for NAND. This option allows SPL to read from + sunxi NAND using DMA transfers. + Depending on the NAND chip, values like ECC strength and page sizes + have to be configured. + +config NAND_SUNXI_SPL_SYNDROME_PARTITIONS_END + hex "Size of syndrome partitions in sunxi NAND" + default 0x400000 + depends on SPL_NAND_SUNXI + ---help--- + End address for boot partitions on NAND. Those partitions have a + different random seed that has to match the sunxi BROM setting. + +config NAND_SUNXI_SPL_ECC_STRENGTH + int "ECC Strength for sunxi NAND" + default 40 + depends on SPL_NAND_SUNXI + ---help--- + ECC strength used by the sunxi NAND SPL driver. This is specific to the + chosen NAND chip and has to match the value used by the sunxi BROM. + +config NAND_SUNXI_SPL_ECC_PAGE_SIZE + hex "ECC page size for sunxi NAND" + default 0x400 + depends on SPL_NAND_SUNXI + ---help--- + ECC page size used by the sunxi NAND SPL driver for syndrome partitions. + This setting has to match the value used by the sunxi BROM. + +config NAND_SUNXI_SPL_PAGE_SIZE + hex "Page size for sunxi NAND" + default 0x2000 + depends on SPL_NAND_SUNXI + ---help--- + Page size of the NAND flash used by the sunxi NAND SPL driver. This is + specific to the chosen NAND chip. endif endmenu diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index edcacd5..71b2368 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -139,6 +139,16 @@ #define CONFIG_INITRD_TAG #define CONFIG_SERIAL_TAG +#if defined(CONFIG_SPL_NAND_SUNXI) +#define CONFIG_SPL_NAND_DRIVERS +#define CONFIG_SPL_NAND_SUPPORT + +#define CONFIG_SYS_NAND_SPL_KERNEL_OFFS 0x280000 +#define CONFIG_SYS_NAND_U_BOOT_OFFS 0x008000 + +#endif + + /* mmc config */ #if !defined(CONFIG_UART0_PORT_F) #define CONFIG_MMC -- cgit v0.10.2 From c9f8947e66042b49bba358d4bce531da3bd440be Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 8 Jul 2015 16:44:22 +0200 Subject: sunxi: usb-phy: Never power off the usb ports USB devices are not really designed to get the power bounced off and on at them. Esp. USB powered harddisks do not like this. Currently we power off the USB ports both on a "usb reset" and when booting the kernel, causing the usb-power to bounce off and then back on again. This patch removes the powering off calls, fixing the undesirable power bouncing. Note this requires some special handling for the OTG port: 1) We must skip the external vbus check if we've already enabled our own vbus to avoid false positives 2) If on an usb reset we no longer detect that the id-pin is grounded, turn off vbus as that means an external vbus may be present now Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/arch/arm/cpu/armv7/sunxi/usb_phy.c b/arch/arm/cpu/armv7/sunxi/usb_phy.c index 4d63a74..b7ca5d4 100644 --- a/arch/arm/cpu/armv7/sunxi/usb_phy.c +++ b/arch/arm/cpu/armv7/sunxi/usb_phy.c @@ -232,6 +232,13 @@ void sunxi_usb_phy_power_off(int index) gpio_set_value(phy->gpio_vbus, 0); } +int sunxi_usb_phy_power_is_on(int index) +{ + struct sunxi_usb_phy *phy = &sunxi_usb_phy[index]; + + return phy->power_on_count > 0; +} + int sunxi_usb_phy_vbus_detect(int index) { struct sunxi_usb_phy *phy = &sunxi_usb_phy[index]; diff --git a/arch/arm/include/asm/arch-sunxi/usb_phy.h b/arch/arm/include/asm/arch-sunxi/usb_phy.h index cef6c98..6a14cad 100644 --- a/arch/arm/include/asm/arch-sunxi/usb_phy.h +++ b/arch/arm/include/asm/arch-sunxi/usb_phy.h @@ -16,6 +16,7 @@ void sunxi_usb_phy_init(int index); void sunxi_usb_phy_exit(int index); void sunxi_usb_phy_power_on(int index); void sunxi_usb_phy_power_off(int index); +int sunxi_usb_phy_power_is_on(int index); int sunxi_usb_phy_vbus_detect(int index); int sunxi_usb_phy_id_detect(int index); void sunxi_usb_phy_enable_squelch_detect(int index, int enable); diff --git a/drivers/usb/host/ehci-sunxi.c b/drivers/usb/host/ehci-sunxi.c index 34130f8..38d5f92 100644 --- a/drivers/usb/host/ehci-sunxi.c +++ b/drivers/usb/host/ehci-sunxi.c @@ -67,7 +67,6 @@ static int ehci_usb_remove(struct udevice *dev) if (ret) return ret; - sunxi_usb_phy_power_off(priv->phy_index); sunxi_usb_phy_exit(priv->phy_index); #ifdef CONFIG_SUNXI_GEN_SUN6I diff --git a/drivers/usb/host/ohci-sunxi.c b/drivers/usb/host/ohci-sunxi.c index e33a8f7..6079272 100644 --- a/drivers/usb/host/ohci-sunxi.c +++ b/drivers/usb/host/ohci-sunxi.c @@ -69,7 +69,6 @@ static int ohci_usb_remove(struct udevice *dev) if (ret) return ret; - sunxi_usb_phy_power_off(priv->phy_index); sunxi_usb_phy_exit(priv->phy_index); #ifdef CONFIG_SUNXI_GEN_SUN6I diff --git a/drivers/usb/musb-new/sunxi.c b/drivers/usb/musb-new/sunxi.c index 16a264a..a146c08 100644 --- a/drivers/usb/musb-new/sunxi.c +++ b/drivers/usb/musb-new/sunxi.c @@ -196,8 +196,6 @@ static bool enabled = false; static int sunxi_musb_enable(struct musb *musb) { - int ret; - pr_debug("%s():\n", __func__); musb_ep_select(musb->mregs, 0); @@ -210,17 +208,26 @@ static int sunxi_musb_enable(struct musb *musb) musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); if (is_host_enabled(musb)) { - ret = sunxi_usb_phy_vbus_detect(0); - if (ret == 1) { - printf("A charger is plugged into the OTG: "); - return -ENODEV; + int id = sunxi_usb_phy_id_detect(0); + + if (id == 1 && sunxi_usb_phy_power_is_on(0)) + sunxi_usb_phy_power_off(0); + + if (!sunxi_usb_phy_power_is_on(0)) { + int vbus = sunxi_usb_phy_vbus_detect(0); + if (vbus == 1) { + printf("A charger is plugged into the OTG: "); + return -ENODEV; + } } - ret = sunxi_usb_phy_id_detect(0); - if (ret == 1) { + + if (id == 1) { printf("No host cable detected: "); return -ENODEV; } - sunxi_usb_phy_power_on(0); /* port power on */ + + if (!sunxi_usb_phy_power_is_on(0)) + sunxi_usb_phy_power_on(0); } USBC_ForceVbusValidToHigh(musb->mregs); @@ -236,9 +243,6 @@ static void sunxi_musb_disable(struct musb *musb) if (!enabled) return; - if (is_host_enabled(musb)) - sunxi_usb_phy_power_off(0); /* port power off */ - USBC_ForceVbusValidToLow(musb->mregs); mdelay(200); /* Wait for the current session to timeout */ -- cgit v0.10.2 From 211aca4edad7098146d46e715504beedf5f67151 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 1 Aug 2015 14:42:40 +0200 Subject: sunxi: Add CONFIG_USB0_ID_DET setting to 2 more tablets Now that we have code to check the id-pin and detect usb-host adapters plugged into the otg port that way, enable it on the tablets which I own. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/configs/Chuwi_V7_CW0825_defconfig b/configs/Chuwi_V7_CW0825_defconfig index 4011351..f44aede 100644 --- a/configs/Chuwi_V7_CW0825_defconfig +++ b/configs/Chuwi_V7_CW0825_defconfig @@ -5,6 +5,7 @@ CONFIG_DRAM_CLK=408 CONFIG_DRAM_EMR1=4 CONFIG_USB0_VBUS_PIN="PB9" CONFIG_USB0_VBUS_DET="PH5" +CONFIG_USB0_ID_DET="PH4" CONFIG_VIDEO_LCD_MODE="x:1024,y:768,depth:24,pclk_khz:51000,le:19,ri:300,up:6,lo:31,hs:1,vs:1,sync:3,vmode:0" CONFIG_VIDEO_LCD_POWER="PH8" CONFIG_VIDEO_LCD_BL_EN="PH7" diff --git a/configs/UTOO_P66_defconfig b/configs/UTOO_P66_defconfig index 0266299..169c2e8 100644 --- a/configs/UTOO_P66_defconfig +++ b/configs/UTOO_P66_defconfig @@ -7,6 +7,7 @@ CONFIG_MMC0_CD_PIN="PG0" CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_USB0_VBUS_PIN="PB04" CONFIG_USB0_VBUS_DET="PG01" +CONFIG_USB0_ID_DET="PG2" CONFIG_AXP_GPIO=y CONFIG_VIDEO_LCD_MODE="x:480,y:800,depth:18,pclk_khz:25000,le:2,ri:93,up:2,lo:93,hs:1,vs:1,sync:3,vmode:0" CONFIG_VIDEO_LCD_DCLK_PHASE=0 -- cgit v0.10.2 From a5da3c8354de9786ea1e065e40ba74f044dfbfe9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 1 Aug 2015 14:44:29 +0200 Subject: sunxi: Do not add a stdout-path alias to dts on boards without a serial port Do not add a bogus (pointing to a non existing serial port) stdout-path alias to dts on boards without a serial port. Note that we still define CONS_INDEX as this is used by the SPL where we do not use DM_SERIAL and thus CONFIG_REQUIRE_SERIAL_CONSOLE is not honored. We are getting away with this because the sun5i die actually has an uart0, which in the A13 package is not routed to the outside, so we are simply sending SPL bootup messages to the tx pin at the edge of the die, and they go no further from there... And sofar we only have one A13 board which does not have a serial port, all others do have a serial port. This kinda makes sense since the A13 is a much lower pincount package compared to all the other sunxi SoCs. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index 71b2368..a2cbcf5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -262,6 +262,7 @@ extern int soft_i2c_gpio_scl; #define CONFIG_CONS_INDEX 1 /* UART0 */ #endif +#ifdef CONFIG_REQUIRE_SERIAL_CONSOLE #if CONFIG_CONS_INDEX == 1 #ifdef CONFIG_MACH_SUN9I #define OF_STDOUT_PATH "/soc/serial@07000000:115200" @@ -277,6 +278,7 @@ extern int soft_i2c_gpio_scl; #else #error Unsupported console port nr. Please fix stdout-path in sunxi-common.h. #endif +#endif /* ifdef CONFIG_REQUIRE_SERIAL_CONSOLE */ /* GPIO */ #define CONFIG_SUNXI_GPIO -- cgit v0.10.2 From 5fa9b6f0ff289946a677892be64d4d19788632a4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 4 Aug 2015 23:49:17 +0200 Subject: sunxi: Enable musb in host mode on the Jesurun Q5 The Jesurun Q5 has the musb hooked up to an usb-a receptacle, enable it in host-only mode. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/configs/jesurun_q5_defconfig b/configs/jesurun_q5_defconfig index a2115b6..b0d8621c 100644 --- a/configs/jesurun_q5_defconfig +++ b/configs/jesurun_q5_defconfig @@ -10,3 +10,5 @@ CONFIG_SYS_EXTRA_OPTIONS="AXP209_POWER,SUNXI_EMAC,MACPWR=SUNXI_GPH(19)" # CONFIG_CMD_FLASH is not set # CONFIG_CMD_FPGA is not set CONFIG_USB_EHCI_HCD=y +CONFIG_USB_MUSB_HOST=y +CONFIG_USB0_VBUS_PIN="PB9" -- cgit v0.10.2 From bf689342a3b6c7dd9eb5a81fc9559cef84d9009a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Aug 2015 23:01:38 +0200 Subject: sunxi: display: Replace #ifdef-ery with helper functions All the #ifdef-ery in selecting the default and fallback monitor type is becoming unyielding and makes the code hard to read, replace it with a few helper functions. This will also be useful with the upcoming CHIP board which has display adapter daughterboards which should be runtime detectable. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index 269083b..3942d2f 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -1101,6 +1101,43 @@ ulong board_get_usable_ram_top(ulong total_size) return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE; } +static bool sunxi_has_hdmi(void) +{ +#ifdef CONFIG_VIDEO_HDMI + return true; +#else + return false; +#endif +} + +static bool sunxi_has_lcd(void) +{ + char *lcd_mode = CONFIG_VIDEO_LCD_MODE; + + return lcd_mode[0] != 0; +} + +static bool sunxi_has_vga(void) +{ +#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_VGA_VIA_LCD + return true; +#else + return false; +#endif +} + +static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi) +{ + if (allow_hdmi && sunxi_has_hdmi()) + return sunxi_monitor_dvi; + else if (sunxi_has_lcd()) + return sunxi_monitor_lcd; + else if (sunxi_has_vga()) + return sunxi_monitor_vga; + else + return sunxi_monitor_none; +} + void *video_hw_init(void) { static GraphicDevice *graphic_device = &sunxi_display.graphic_device; @@ -1122,12 +1159,8 @@ void *video_hw_init(void) hpd = video_get_option_int(options, "hpd", 1); hpd_delay = video_get_option_int(options, "hpd_delay", 500); edid = video_get_option_int(options, "edid", 1); - sunxi_display.monitor = sunxi_monitor_dvi; -#elif defined CONFIG_VIDEO_VGA_VIA_LCD - sunxi_display.monitor = sunxi_monitor_vga; -#else - sunxi_display.monitor = sunxi_monitor_lcd; #endif + sunxi_display.monitor = sunxi_get_default_mon(true); video_get_option_string(options, "monitor", mon, sizeof(mon), sunxi_get_mon_desc(sunxi_display.monitor)); for (i = 0; i <= SUNXI_MONITOR_LAST; i++) { @@ -1152,16 +1185,7 @@ void *video_hw_init(void) mode = &custom; } else if (hpd) { sunxi_hdmi_shutdown(); - /* Fallback to lcd / vga / none */ - if (lcd_mode[0]) { - sunxi_display.monitor = sunxi_monitor_lcd; - } else { -#if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA - sunxi_display.monitor = sunxi_monitor_vga; -#else - sunxi_display.monitor = sunxi_monitor_none; -#endif - } + sunxi_display.monitor = sunxi_get_default_mon(false); } /* else continue with hdmi/dvi without a cable connected */ } #endif @@ -1171,31 +1195,29 @@ void *video_hw_init(void) return NULL; case sunxi_monitor_dvi: case sunxi_monitor_hdmi: -#ifdef CONFIG_VIDEO_HDMI + if (!sunxi_has_hdmi()) { + printf("HDMI/DVI not supported on this board\n"); + sunxi_display.monitor = sunxi_monitor_none; + return NULL; + } break; -#else - printf("HDMI/DVI not supported on this board\n"); - sunxi_display.monitor = sunxi_monitor_none; - return NULL; -#endif case sunxi_monitor_lcd: - if (lcd_mode[0]) { - sunxi_display.depth = video_get_params(&custom, lcd_mode); - mode = &custom; - break; + if (!sunxi_has_lcd()) { + printf("LCD not supported on this board\n"); + sunxi_display.monitor = sunxi_monitor_none; + return NULL; } - printf("LCD not supported on this board\n"); - sunxi_display.monitor = sunxi_monitor_none; - return NULL; + sunxi_display.depth = video_get_params(&custom, lcd_mode); + mode = &custom; + break; case sunxi_monitor_vga: -#if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA + if (!sunxi_has_vga()) { + printf("VGA not supported on this board\n"); + sunxi_display.monitor = sunxi_monitor_none; + return NULL; + } sunxi_display.depth = 18; break; -#else - printf("VGA not supported on this board\n"); - sunxi_display.monitor = sunxi_monitor_none; - return NULL; -#endif } if (mode->vmode != FB_VMODE_NONINTERLACED) { -- cgit v0.10.2 From ab2a2be988b571bf6b96bb00b6edf122c1cf9ddc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 2 Aug 2015 17:38:43 +0200 Subject: sunxi: display: Correct clk_delay calculations for lcd displays We should only subtract 2 from the vblank time when using tcon1. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index 3942d2f..fa4241e 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -663,11 +663,14 @@ static void sunxi_lcdc_backlight_enable(void) gpio_direction_output(pin, PWM_ON); } -static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode) +static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode, int tcon) { int delay; - delay = mode->lower_margin + mode->vsync_len + mode->upper_margin - 2; + delay = mode->lower_margin + mode->vsync_len + mode->upper_margin; + if (tcon == 1) + delay -= 2; + return (delay > 30) ? 30 : delay; } @@ -692,7 +695,7 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK, SUNXI_LCDC_CTRL_IO_MAP_TCON0); - clk_delay = sunxi_lcdc_get_clk_delay(mode); + clk_delay = sunxi_lcdc_get_clk_delay(mode, 0); writel(SUNXI_LCDC_TCON0_CTRL_ENABLE | SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl); @@ -770,7 +773,7 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK, SUNXI_LCDC_CTRL_IO_MAP_TCON1); - clk_delay = sunxi_lcdc_get_clk_delay(mode); + clk_delay = sunxi_lcdc_get_clk_delay(mode, 1); writel(SUNXI_LCDC_TCON1_CTRL_ENABLE | SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl); -- cgit v0.10.2 From 0ecb43a8d054f0252256d44f3a06e56fd8370446 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Aug 2015 19:45:37 +0200 Subject: sunxi: display: Add a few extra register and constant defines Add a few extra sunxi display registers and constant defines. Also rename some existing defines (e.g. dropping _GCTRL) and make some more generic (e.g. dropping the 2x scaling from SUNXI_LCDC_TCON1_TIMING_V_TOTAL). This is a preparation patch for adding composite video out support. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h index 01d3e28..a7e25f4 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h @@ -291,7 +291,7 @@ struct sunxi_ccm_reg { #define CCM_LCD_CH0_CTRL_GATE (0x1 << 31) #define CCM_LCD_CH1_CTRL_M(n) ((((n) - 1) & 0xf) << 0) -/* We leave bit 11 set to 0, so sclk1 == sclk2 */ +#define CCM_LCD_CH1_CTRL_HALF_SCLK1 (1 << 11) #define CCM_LCD_CH1_CTRL_PLL3 (0 << 24) #define CCM_LCD_CH1_CTRL_PLL7 (1 << 24) #define CCM_LCD_CH1_CTRL_PLL3_2X (2 << 24) diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 8a26b9f..06c6feb 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -290,6 +290,7 @@ struct sunxi_ccm_reg { #define CCM_LCD_CH0_CTRL_GATE (0x1 << 31) #define CCM_LCD_CH1_CTRL_M(n) ((((n) - 1) & 0xf) << 0) +#define CCM_LCD_CH1_CTRL_HALF_SCLK1 0 /* no seperate sclk1 & 2 on sun6i */ #define CCM_LCD_CH1_CTRL_PLL3 (0 << 24) #define CCM_LCD_CH1_CTRL_PLL7 (1 << 24) #define CCM_LCD_CH1_CTRL_PLL3_2X (2 << 24) diff --git a/arch/arm/include/asm/arch-sunxi/display.h b/arch/arm/include/asm/arch-sunxi/display.h index ae95417..2cc3916 100644 --- a/arch/arm/include/asm/arch-sunxi/display.h +++ b/arch/arm/include/asm/arch-sunxi/display.h @@ -151,6 +151,10 @@ struct sunxi_de_be_reg { u32 layer1_attr1_ctrl; /* 0x8a4 */ u32 layer2_attr1_ctrl; /* 0x8a8 */ u32 layer3_attr1_ctrl; /* 0x8ac */ + u8 res5[0x110]; /* 0x8b0 */ + u32 output_color_ctrl; /* 0x9c0 */ + u8 res6[0xc]; /* 0x9c4 */ + u32 output_color_coef[12]; /* 0x9d0 */ }; struct sunxi_lcdc_reg { @@ -298,7 +302,7 @@ struct sunxi_tve_reg { u32 cbr_level; /* 0x10c */ u32 burst_phase; /* 0x110 */ u32 burst_width; /* 0x114 */ - u8 res2[0x04]; /* 0x118 */ + u32 unknown2; /* 0x118 */ u32 sync_vbi_level; /* 0x11c */ u32 white_level; /* 0x120 */ u32 active_num; /* 0x124 */ @@ -331,11 +335,14 @@ struct sunxi_tve_reg { #define SUNXI_DE_BE_HEIGHT(y) (((y) - 1) << 16) #define SUNXI_DE_BE_MODE_ENABLE (1 << 0) #define SUNXI_DE_BE_MODE_START (1 << 1) +#define SUNXI_DE_BE_MODE_DEFLICKER_ENABLE (1 << 4) #define SUNXI_DE_BE_MODE_LAYER0_ENABLE (1 << 8) +#define SUNXI_DE_BE_MODE_INTERLACE_ENABLE (1 << 28) #define SUNXI_DE_BE_LAYER_STRIDE(x) ((x) << 5) #define SUNXI_DE_BE_REG_CTRL_LOAD_REGS (1 << 0) #define SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0 0x00000002 #define SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888 (0x09 << 8) +#define SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE 1 /* * LCDC register constants. @@ -372,11 +379,12 @@ struct sunxi_tve_reg { #define SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE (1 << 31) #define SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(x) ((x) << 28) #define SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(n) (((n) & 0x1f) << 4) +#define SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE (1 << 20) #define SUNXI_LCDC_TCON1_CTRL_ENABLE (1 << 31) #define SUNXI_LCDC_TCON1_TIMING_H_BP(n) (((n) - 1) << 0) #define SUNXI_LCDC_TCON1_TIMING_H_TOTAL(n) (((n) - 1) << 16) #define SUNXI_LCDC_TCON1_TIMING_V_BP(n) (((n) - 1) << 0) -#define SUNXI_LCDC_TCON1_TIMING_V_TOTAL(n) (((n) * 2) << 16) +#define SUNXI_LCDC_TCON1_TIMING_V_TOTAL(n) ((n) << 16) #ifdef CONFIG_SUNXI_GEN_SUN6I #define SUNXI_LCDC_LVDS_ANA0 0x40040320 #define SUNXI_LCDC_LVDS_ANA0_EN_MB (1 << 31) @@ -494,9 +502,22 @@ struct sunxi_tve_reg { */ #define SUNXI_TVE_GCTRL_DAC_INPUT_MASK(dac) (0xf << (((dac) + 1) * 4)) #define SUNXI_TVE_GCTRL_DAC_INPUT(dac, sel) ((sel) << (((dac) + 1) * 4)) -#define SUNXI_TVE_GCTRL_CFG0_VGA 0x20000000 -#define SUNXI_TVE_GCTRL_DAC_CFG0_VGA 0x403e1ac7 -#define SUNXI_TVE_GCTRL_UNKNOWN1_VGA 0x00000000 +#define SUNXI_TVE_CFG0_VGA 0x20000000 +#define SUNXI_TVE_CFG0_PAL 0x07030001 +#define SUNXI_TVE_CFG0_NTSC 0x07030000 +#define SUNXI_TVE_DAC_CFG0_VGA 0x403e1ac7 +#define SUNXI_TVE_DAC_CFG0_COMPOSITE 0x403f0008 +#define SUNXI_TVE_FILTER_COMPOSITE 0x00000120 +#define SUNXI_TVE_CHROMA_FREQ_PAL_M 0x21e6efe3 +#define SUNXI_TVE_CHROMA_FREQ_PAL_NC 0x21f69446 +#define SUNXI_TVE_PORCH_NUM_PAL 0x008a0018 +#define SUNXI_TVE_PORCH_NUM_NTSC 0x00760020 +#define SUNXI_TVE_LINE_NUM_PAL 0x00160271 +#define SUNXI_TVE_LINE_NUM_NTSC 0x0016020d +#define SUNXI_TVE_BLANK_BLACK_LEVEL_PAL 0x00fc00fc +#define SUNXI_TVE_BLANK_BLACK_LEVEL_NTSC 0x00f0011a +#define SUNXI_TVE_UNKNOWN1_VGA 0x00000000 +#define SUNXI_TVE_UNKNOWN1_COMPOSITE 0x18181818 #define SUNXI_TVE_AUTO_DETECT_EN_DET_EN(dac) (1 << ((dac) + 0)) #define SUNXI_TVE_AUTO_DETECT_EN_INT_EN(dac) (1 << ((dac) + 16)) #define SUNXI_TVE_AUTO_DETECT_INT_STATUS(dac) (1 << ((dac) + 0)) @@ -512,6 +533,20 @@ struct sunxi_tve_reg { #define SUNXI_TVE_CSC_REG1 0x3b6dace1 #define SUNXI_TVE_CSC_REG2 0x0e1d13dc #define SUNXI_TVE_CSC_REG3 0x00108080 +#define SUNXI_TVE_COLOR_BURST_PAL_M 0x00000000 +#define SUNXI_TVE_CBR_LEVEL_PAL 0x00002828 +#define SUNXI_TVE_CBR_LEVEL_NTSC 0x0000004f +#define SUNXI_TVE_BURST_PHASE_NTSC 0x00000000 +#define SUNXI_TVE_BURST_WIDTH_COMPOSITE 0x0016447e +#define SUNXI_TVE_UNKNOWN2_PAL 0x0000e0e0 +#define SUNXI_TVE_UNKNOWN2_NTSC 0x0000a0a0 +#define SUNXI_TVE_SYNC_VBI_LEVEL_NTSC 0x001000f0 +#define SUNXI_TVE_ACTIVE_NUM_COMPOSITE 0x000005a0 +#define SUNXI_TVE_CHROMA_BW_GAIN_COMP 0x00000002 +#define SUNXI_TVE_NOTCH_WIDTH_COMPOSITE 0x00000101 +#define SUNXI_TVE_RESYNC_NUM_PAL 0x800d000c +#define SUNXI_TVE_RESYNC_NUM_NTSC 0x000e000c +#define SUNXI_TVE_SLAVE_PARA_COMPOSITE 0x00000000 int sunxi_simplefb_setup(void *blob); diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index fa4241e..74a4280 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -791,7 +791,7 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, bp = mode->vsync_len + mode->upper_margin; total = mode->yres + mode->lower_margin + bp; - writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) | + writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(2 * total) | SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v); writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len), @@ -944,9 +944,9 @@ static void sunxi_vga_mode_set(void) writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl); - writel(SUNXI_TVE_GCTRL_CFG0_VGA, &tve->cfg0); - writel(SUNXI_TVE_GCTRL_DAC_CFG0_VGA, &tve->dac_cfg0); - writel(SUNXI_TVE_GCTRL_UNKNOWN1_VGA, &tve->unknown1); + writel(SUNXI_TVE_CFG0_VGA, &tve->cfg0); + writel(SUNXI_TVE_DAC_CFG0_VGA, &tve->dac_cfg0); + writel(SUNXI_TVE_UNKNOWN1_VGA, &tve->unknown1); } static void sunxi_vga_enable(void) -- cgit v0.10.2 From f6d9d32462f1aa00e525e8778429f20d72beb23a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 2 Aug 2015 16:49:29 +0200 Subject: sunxi: display: Add support for interlaced modes Add support for interlaced modes, this is a preparation patch for adding composite out support. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index 74a4280..f3cc06e 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -445,6 +445,10 @@ static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode, writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl); setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE); + if (mode->vmode == FB_VMODE_INTERLACED) + setbits_le32(&de_be->mode, + SUNXI_DE_BE_MODE_DEFLICKER_ENABLE | + SUNXI_DE_BE_MODE_INTERLACE_ENABLE); } static void sunxi_composer_enable(void) @@ -668,6 +672,8 @@ static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode, int tcon) int delay; delay = mode->lower_margin + mode->vsync_len + mode->upper_margin; + if (mode->vmode == FB_VMODE_INTERLACED) + delay /= 2; if (tcon == 1) delay -= 2; @@ -767,7 +773,7 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, { struct sunxi_lcdc_reg * const lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; - int bp, clk_delay, total, val; + int bp, clk_delay, total, val, yres; /* Use tcon1 */ clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK, @@ -775,13 +781,18 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, clk_delay = sunxi_lcdc_get_clk_delay(mode, 1); writel(SUNXI_LCDC_TCON1_CTRL_ENABLE | + ((mode->vmode == FB_VMODE_INTERLACED) ? + SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE : 0) | SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl); - writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres), + yres = mode->yres; + if (mode->vmode == FB_VMODE_INTERLACED) + yres /= 2; + writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres), &lcdc->tcon1_timing_source); - writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres), + writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres), &lcdc->tcon1_timing_scale); - writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres), + writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres), &lcdc->tcon1_timing_out); bp = mode->hsync_len + mode->left_margin; @@ -791,7 +802,9 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, bp = mode->vsync_len + mode->upper_margin; total = mode->yres + mode->lower_margin + bp; - writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(2 * total) | + if (mode->vmode == FB_VMODE_NONINTERLACED) + total *= 2; + writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) | SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v); writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len), @@ -1223,14 +1236,6 @@ void *video_hw_init(void) break; } - if (mode->vmode != FB_VMODE_NONINTERLACED) { - printf("Only non-interlaced modes supported, falling back to 1024x768\n"); - mode = &res_mode_init[RES_MODE_1024x768]; - } else { - printf("Setting up a %dx%d %s console\n", mode->xres, - mode->yres, sunxi_get_mon_desc(sunxi_display.monitor)); - } - sunxi_display.fb_size = (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff; if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) { @@ -1240,6 +1245,10 @@ void *video_hw_init(void) return NULL; } + printf("Setting up a %dx%d%s %s console\n", mode->xres, mode->yres, + (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "", + sunxi_get_mon_desc(sunxi_display.monitor)); + gd->fb_base = gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sunxi_display.fb_size; sunxi_engines_init(); -- cgit v0.10.2 From 39920c81ce4431b7ea08f5e80feb5ec8b156864e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Aug 2015 19:20:26 +0200 Subject: sunxi: display: Add composite video out support Add composite video out support. This only gets enabled on the Mele M3 for now, since that is were it was tested. It will be enabled on more boards after testing. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index c4b5a85..13f4537 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -410,6 +410,13 @@ config VIDEO_VGA_EXTERNAL_DAC_EN Set the enable pin for the external VGA DAC. This takes a string in the format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. +config VIDEO_COMPOSITE + boolean "Composite video output support" + depends on VIDEO && (MACH_SUN4I || MACH_SUN5I || MACH_SUN7I) + default n + ---help--- + Say Y here to add support for outputting composite video. + config VIDEO_LCD_MODE string "LCD panel timing details" depends on VIDEO diff --git a/configs/Mele_M3_defconfig b/configs/Mele_M3_defconfig index d498269..5c9796a 100644 --- a/configs/Mele_M3_defconfig +++ b/configs/Mele_M3_defconfig @@ -5,6 +5,7 @@ CONFIG_DRAM_CLK=384 CONFIG_MMC0_CD_PIN="PH1" CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_VIDEO_VGA=y +CONFIG_VIDEO_COMPOSITE=y CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-m3" # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL=y diff --git a/doc/README.video b/doc/README.video index d0a3ad6..4f7a4b5 100644 --- a/doc/README.video +++ b/doc/README.video @@ -40,12 +40,15 @@ requires the CONFIG_VIDEO_LCD_MODE Kconfig value to be set. The sunxi u-boot driver supports the following video-mode options: -- monitor=[none|dvi|hdmi|lcd] - Select the video output to use +- monitor=[none|dvi|hdmi|lcd|vga|composite-*] - Select the video output to use none: Disable video output. dvi/hdmi: Selects output over the hdmi connector with dvi resp. hdmi output format, if edid is used the format is automatically selected. lcd: Selects video output to a LCD screen. - vga: Selects bideo output over the VGA connector. + vga: Selects video output over the VGA connector. + composite-pal/composite-ntsc/composite-pal-m/composite-pal-nc: + Selects composite video output, note the specified resolution is + ignored with composite video output. Defaults to monitor=dvi. - hpd=[0|1] - Enable use of the hdmi HotPlug Detect feature diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index f3cc06e..1868185 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -2,7 +2,7 @@ * Display driver for Allwinner SoCs. * * (C) Copyright 2013-2014 Luc Verhaegen - * (C) Copyright 2014 Hans de Goede + * (C) Copyright 2014-2015 Hans de Goede * * SPDX-License-Identifier: GPL-2.0+ */ @@ -40,8 +40,12 @@ enum sunxi_monitor { sunxi_monitor_hdmi, sunxi_monitor_lcd, sunxi_monitor_vga, + sunxi_monitor_composite_pal, + sunxi_monitor_composite_ntsc, + sunxi_monitor_composite_pal_m, + sunxi_monitor_composite_pal_nc, }; -#define SUNXI_MONITOR_LAST sunxi_monitor_vga +#define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc struct sunxi_display { GraphicDevice graphic_device; @@ -50,6 +54,12 @@ struct sunxi_display { unsigned int fb_size; } sunxi_display; +const struct ctfb_res_modes composite_video_modes[2] = { + /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */ + { 720, 576, 50, 37037, 27000, 137, 5, 20, 27, 2, 2, 0, FB_VMODE_INTERLACED }, + { 720, 480, 60, 37037, 27000, 116, 20, 16, 27, 2, 2, 0, FB_VMODE_INTERLACED }, +}; + #ifdef CONFIG_VIDEO_HDMI /* @@ -390,6 +400,25 @@ static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode, static void sunxi_frontend_enable(void) {} #endif +static bool sunxi_is_composite(void) +{ + switch (sunxi_display.monitor) { + case sunxi_monitor_none: + case sunxi_monitor_dvi: + case sunxi_monitor_hdmi: + case sunxi_monitor_lcd: + case sunxi_monitor_vga: + return false; + case sunxi_monitor_composite_pal: + case sunxi_monitor_composite_ntsc: + case sunxi_monitor_composite_pal_m: + case sunxi_monitor_composite_pal_nc: + return true; + } + + return false; /* Never reached */ +} + /* * This is the entity that mixes and matches the different layers and inputs. * Allwinner calls it the back-end, but i like composer better. @@ -423,11 +452,18 @@ static void sunxi_composer_init(void) setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE); } +static u32 sunxi_rgb2yuv_coef[12] = { + 0x00000107, 0x00000204, 0x00000064, 0x00000108, + 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808, + 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808 +}; + static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode, unsigned int address) { struct sunxi_de_be_reg * const de_be = (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; + int i; sunxi_frontend_mode_set(mode, address); @@ -449,6 +485,14 @@ static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode, setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_DEFLICKER_ENABLE | SUNXI_DE_BE_MODE_INTERLACE_ENABLE); + + if (sunxi_is_composite()) { + writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE, + &de_be->output_color_ctrl); + for (i = 0; i < 12; i++) + writel(sunxi_rgb2yuv_coef[i], + &de_be->output_color_coef[i]); + } } static void sunxi_composer_enable(void) @@ -539,6 +583,9 @@ static void sunxi_lcdc_pll_set(int tcon, int dotclock, (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X : CCM_LCD_CH1_CTRL_PLL3) | CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg); + if (sunxi_is_composite()) + setbits_le32(&ccm->lcd0_ch1_clk_cfg, + CCM_LCD_CH1_CTRL_HALF_SCLK1); } *clk_div = best_m; @@ -766,7 +813,7 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, writel(0, &lcdc->tcon0_io_tristate); } -#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA +#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, int *clk_div, int *clk_double, bool use_portd_hvsync) @@ -827,7 +874,7 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, } sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double); } -#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA */ +#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */ #ifdef CONFIG_VIDEO_HDMI @@ -941,9 +988,9 @@ static void sunxi_hdmi_enable(void) #endif /* CONFIG_VIDEO_HDMI */ -#ifdef CONFIG_VIDEO_VGA +#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE -static void sunxi_vga_mode_set(void) +static void sunxi_tvencoder_mode_set(void) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; @@ -953,16 +1000,75 @@ static void sunxi_vga_mode_set(void) /* Clock on */ setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0); - /* Set TVE in VGA mode */ - writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | - SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | - SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl); - writel(SUNXI_TVE_CFG0_VGA, &tve->cfg0); - writel(SUNXI_TVE_DAC_CFG0_VGA, &tve->dac_cfg0); - writel(SUNXI_TVE_UNKNOWN1_VGA, &tve->unknown1); + switch (sunxi_display.monitor) { + case sunxi_monitor_vga: + writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | + SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | + SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl); + writel(SUNXI_TVE_CFG0_VGA, &tve->cfg0); + writel(SUNXI_TVE_DAC_CFG0_VGA, &tve->dac_cfg0); + writel(SUNXI_TVE_UNKNOWN1_VGA, &tve->unknown1); + break; + case sunxi_monitor_composite_pal_nc: + writel(SUNXI_TVE_CHROMA_FREQ_PAL_NC, &tve->chroma_freq); + /* Fall through */ + case sunxi_monitor_composite_pal: + writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | + SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | + SUNXI_TVE_GCTRL_DAC_INPUT(2, 3) | + SUNXI_TVE_GCTRL_DAC_INPUT(3, 4), &tve->gctrl); + writel(SUNXI_TVE_CFG0_PAL, &tve->cfg0); + writel(SUNXI_TVE_DAC_CFG0_COMPOSITE, &tve->dac_cfg0); + writel(SUNXI_TVE_FILTER_COMPOSITE, &tve->filter); + writel(SUNXI_TVE_PORCH_NUM_PAL, &tve->porch_num); + writel(SUNXI_TVE_LINE_NUM_PAL, &tve->line_num); + writel(SUNXI_TVE_BLANK_BLACK_LEVEL_PAL, &tve->blank_black_level); + writel(SUNXI_TVE_UNKNOWN1_COMPOSITE, &tve->unknown1); + writel(SUNXI_TVE_CBR_LEVEL_PAL, &tve->cbr_level); + writel(SUNXI_TVE_BURST_WIDTH_COMPOSITE, &tve->burst_width); + writel(SUNXI_TVE_UNKNOWN2_PAL, &tve->unknown2); + writel(SUNXI_TVE_ACTIVE_NUM_COMPOSITE, &tve->active_num); + writel(SUNXI_TVE_CHROMA_BW_GAIN_COMP, &tve->chroma_bw_gain); + writel(SUNXI_TVE_NOTCH_WIDTH_COMPOSITE, &tve->notch_width); + writel(SUNXI_TVE_RESYNC_NUM_PAL, &tve->resync_num); + writel(SUNXI_TVE_SLAVE_PARA_COMPOSITE, &tve->slave_para); + break; + case sunxi_monitor_composite_pal_m: + writel(SUNXI_TVE_CHROMA_FREQ_PAL_M, &tve->chroma_freq); + writel(SUNXI_TVE_COLOR_BURST_PAL_M, &tve->color_burst); + /* Fall through */ + case sunxi_monitor_composite_ntsc: + writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | + SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | + SUNXI_TVE_GCTRL_DAC_INPUT(2, 3) | + SUNXI_TVE_GCTRL_DAC_INPUT(3, 4), &tve->gctrl); + writel(SUNXI_TVE_CFG0_NTSC, &tve->cfg0); + writel(SUNXI_TVE_DAC_CFG0_COMPOSITE, &tve->dac_cfg0); + writel(SUNXI_TVE_FILTER_COMPOSITE, &tve->filter); + writel(SUNXI_TVE_PORCH_NUM_NTSC, &tve->porch_num); + writel(SUNXI_TVE_LINE_NUM_NTSC, &tve->line_num); + writel(SUNXI_TVE_BLANK_BLACK_LEVEL_NTSC, &tve->blank_black_level); + writel(SUNXI_TVE_UNKNOWN1_COMPOSITE, &tve->unknown1); + writel(SUNXI_TVE_CBR_LEVEL_NTSC, &tve->cbr_level); + writel(SUNXI_TVE_BURST_PHASE_NTSC, &tve->burst_phase); + writel(SUNXI_TVE_BURST_WIDTH_COMPOSITE, &tve->burst_width); + writel(SUNXI_TVE_UNKNOWN2_NTSC, &tve->unknown2); + writel(SUNXI_TVE_SYNC_VBI_LEVEL_NTSC, &tve->sync_vbi_level); + writel(SUNXI_TVE_ACTIVE_NUM_COMPOSITE, &tve->active_num); + writel(SUNXI_TVE_CHROMA_BW_GAIN_COMP, &tve->chroma_bw_gain); + writel(SUNXI_TVE_NOTCH_WIDTH_COMPOSITE, &tve->notch_width); + writel(SUNXI_TVE_RESYNC_NUM_NTSC, &tve->resync_num); + writel(SUNXI_TVE_SLAVE_PARA_COMPOSITE, &tve->slave_para); + break; + case sunxi_monitor_none: + case sunxi_monitor_dvi: + case sunxi_monitor_hdmi: + case sunxi_monitor_lcd: + break; + } } -static void sunxi_vga_enable(void) +static void sunxi_tvencoder_enable(void) { struct sunxi_tve_reg * const tve = (struct sunxi_tve_reg *)SUNXI_TVE0_BASE; @@ -970,7 +1076,7 @@ static void sunxi_vga_enable(void) setbits_le32(&tve->gctrl, SUNXI_TVE_GCTRL_ENABLE); } -#endif /* CONFIG_VIDEO_VGA */ +#endif /* CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE */ static void sunxi_drc_init(void) { @@ -1085,10 +1191,10 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode, #ifdef CONFIG_VIDEO_VGA sunxi_composer_mode_set(mode, address); sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1); - sunxi_vga_mode_set(); + sunxi_tvencoder_mode_set(); sunxi_composer_enable(); sunxi_lcdc_enable(); - sunxi_vga_enable(); + sunxi_tvencoder_enable(); #elif defined CONFIG_VIDEO_VGA_VIA_LCD sunxi_composer_mode_set(mode, address); sunxi_lcdc_tcon0_mode_set(mode, true); @@ -1097,17 +1203,34 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode, sunxi_vga_external_dac_enable(); #endif break; + case sunxi_monitor_composite_pal: + case sunxi_monitor_composite_ntsc: + case sunxi_monitor_composite_pal_m: + case sunxi_monitor_composite_pal_nc: +#ifdef CONFIG_VIDEO_COMPOSITE + sunxi_composer_mode_set(mode, address); + sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0); + sunxi_tvencoder_mode_set(); + sunxi_composer_enable(); + sunxi_lcdc_enable(); + sunxi_tvencoder_enable(); +#endif + break; } } static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor) { switch (monitor) { - case sunxi_monitor_none: return "none"; - case sunxi_monitor_dvi: return "dvi"; - case sunxi_monitor_hdmi: return "hdmi"; - case sunxi_monitor_lcd: return "lcd"; - case sunxi_monitor_vga: return "vga"; + case sunxi_monitor_none: return "none"; + case sunxi_monitor_dvi: return "dvi"; + case sunxi_monitor_hdmi: return "hdmi"; + case sunxi_monitor_lcd: return "lcd"; + case sunxi_monitor_vga: return "vga"; + case sunxi_monitor_composite_pal: return "composite-pal"; + case sunxi_monitor_composite_ntsc: return "composite-ntsc"; + case sunxi_monitor_composite_pal_m: return "composite-pal-m"; + case sunxi_monitor_composite_pal_nc: return "composite-pal-nc"; } return NULL; /* never reached */ } @@ -1142,6 +1265,15 @@ static bool sunxi_has_vga(void) #endif } +static bool sunxi_has_composite(void) +{ +#ifdef CONFIG_VIDEO_COMPOSITE + return true; +#else + return false; +#endif +} + static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi) { if (allow_hdmi && sunxi_has_hdmi()) @@ -1150,6 +1282,8 @@ static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi) return sunxi_monitor_lcd; else if (sunxi_has_vga()) return sunxi_monitor_vga; + else if (sunxi_has_composite()) + return sunxi_monitor_composite_pal; else return sunxi_monitor_none; } @@ -1234,6 +1368,22 @@ void *video_hw_init(void) } sunxi_display.depth = 18; break; + case sunxi_monitor_composite_pal: + case sunxi_monitor_composite_ntsc: + case sunxi_monitor_composite_pal_m: + case sunxi_monitor_composite_pal_nc: + if (!sunxi_has_composite()) { + printf("Composite video not supported on this board\n"); + sunxi_display.monitor = sunxi_monitor_none; + return NULL; + } + if (sunxi_display.monitor == sunxi_monitor_composite_pal || + sunxi_display.monitor == sunxi_monitor_composite_pal_nc) + mode = &composite_video_modes[0]; + else + mode = &composite_video_modes[1]; + sunxi_display.depth = 24; + break; } sunxi_display.fb_size = @@ -1302,6 +1452,12 @@ int sunxi_simplefb_setup(void *blob) pipeline = PIPELINE_PREFIX "de_be0-lcd0"; #endif break; + case sunxi_monitor_composite_pal: + case sunxi_monitor_composite_ntsc: + case sunxi_monitor_composite_pal_m: + case sunxi_monitor_composite_pal_nc: + pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0"; + break; } /* Find a prefilled simpefb node, matching out pipeline config */ -- cgit v0.10.2