diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpio/sunxi_gpio.c | 22 | ||||
-rw-r--r-- | drivers/power/pmic/Kconfig | 7 | ||||
-rw-r--r-- | drivers/power/pmic/pfuze100.c | 96 | ||||
-rw-r--r-- | drivers/power/pmic/pmic_pfuze100.c | 2 | ||||
-rw-r--r-- | drivers/video/Kconfig | 8 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rwxr-xr-x | drivers/video/anx9804.c | 188 | ||||
-rw-r--r-- | drivers/video/anx9804.h | 25 | ||||
-rw-r--r-- | drivers/video/cfb_console.c | 89 | ||||
-rw-r--r-- | drivers/video/sunxi_display.c | 122 |
10 files changed, 489 insertions, 71 deletions
diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c index afa165a..57b78e5 100644 --- a/drivers/gpio/sunxi_gpio.c +++ b/drivers/gpio/sunxi_gpio.c @@ -266,16 +266,28 @@ static int gpio_sunxi_bind(struct udevice *parent) { struct sunxi_gpio_platdata *plat = parent->platdata; struct sunxi_gpio_reg *ctlr; - int bank; - int ret; + int bank, no_banks, ret, start; /* If this is a child device, there is nothing to do here */ if (plat) return 0; + if (fdt_node_check_compatible(gd->fdt_blob, parent->of_offset, + "allwinner,sun6i-a31-r-pinctrl") == 0) { + start = 'L' - 'A'; + no_banks = 2; /* L & M */ + } else if (fdt_node_check_compatible(gd->fdt_blob, parent->of_offset, + "allwinner,sun8i-a23-r-pinctrl") == 0) { + start = 'L' - 'A'; + no_banks = 1; /* L only */ + } else { + start = 0; + no_banks = SUNXI_GPIO_BANKS; + } + ctlr = (struct sunxi_gpio_reg *)fdtdec_get_addr(gd->fdt_blob, parent->of_offset, "reg"); - for (bank = 0; bank < SUNXI_GPIO_BANKS; bank++) { + for (bank = 0; bank < no_banks; bank++) { struct sunxi_gpio_platdata *plat; struct udevice *dev; @@ -283,7 +295,7 @@ static int gpio_sunxi_bind(struct udevice *parent) if (!plat) return -ENOMEM; plat->regs = &ctlr->gpio_bank[bank]; - plat->bank_name = gpio_bank_name(bank); + plat->bank_name = gpio_bank_name(start + bank); plat->gpio_count = SUNXI_GPIOS_PER_BANK; ret = device_bind(parent, parent->driver, @@ -306,6 +318,8 @@ static const struct udevice_id sunxi_gpio_ids[] = { { .compatible = "allwinner,sun8i-a23-pinctrl" }, { .compatible = "allwinner,sun8i-a33-pinctrl" }, { .compatible = "allwinner,sun9i-a80-pinctrl" }, + { .compatible = "allwinner,sun6i-a31-r-pinctrl" }, + { .compatible = "allwinner,sun8i-a23-r-pinctrl" }, { } }; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 7b98189..fc6a374 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -10,6 +10,13 @@ config DM_PMIC - 'drivers/power/pmic/pmic-uclass.c' - 'include/power/pmic.h' +config DM_PMIC_PFUZE100 + bool "Enable Driver Model for PMIC PFUZE100" + depends on DM_PMIC + ---help--- + This config enables implementation of driver-model pmic uclass features + for PMIC PFUZE100. The driver implements read/write operations. + config DM_PMIC_MAX77686 bool "Enable Driver Model for PMIC MAX77686" depends on DM_PMIC diff --git a/drivers/power/pmic/pfuze100.c b/drivers/power/pmic/pfuze100.c new file mode 100644 index 0000000..3beb48e --- /dev/null +++ b/drivers/power/pmic/pfuze100.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 Freescale Semiconductor, Inc + * Peng Fan <Peng.Fan@freescale.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/pfuze100_pmic.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const struct pmic_child_info pmic_children_info[] = { + /* sw[x], swbst */ + { .prefix = "s", .driver = PFUZE100_REGULATOR_DRIVER }, + /* vgen[x], vsnvs, vcc, v33, vcc_sd */ + { .prefix = "v", .driver = PFUZE100_REGULATOR_DRIVER }, + { }, +}; + +static int pfuze100_reg_count(struct udevice *dev) +{ + return PFUZE100_NUM_OF_REGS; +} + +static int pfuze100_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + if (dm_i2c_write(dev, reg, buff, len)) { + error("write error to device: %p register: %#x!", dev, reg); + return -EIO; + } + + return 0; +} + +static int pfuze100_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + if (dm_i2c_read(dev, reg, buff, len)) { + error("read error from device: %p register: %#x!", dev, reg); + return -EIO; + } + + return 0; +} + +static int pfuze100_bind(struct udevice *dev) +{ + int children; + int regulators_node; + const void *blob = gd->fdt_blob; + + regulators_node = fdt_subnode_offset(blob, dev->of_offset, + "regulators"); + if (regulators_node <= 0) { + debug("%s: %s regulators subnode not found!", __func__, + dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, pmic_children_info); + if (!children) + debug("%s: %s - no child found\n", __func__, dev->name); + + /* Always return success for this device */ + return 0; +} + +static struct dm_pmic_ops pfuze100_ops = { + .reg_count = pfuze100_reg_count, + .read = pfuze100_read, + .write = pfuze100_write, +}; + +static const struct udevice_id pfuze100_ids[] = { + { .compatible = "fsl,pfuze100", .data = PFUZE100, }, + { .compatible = "fsl,pfuze200", .data = PFUZE200, }, + { .compatible = "fsl,pfuze3000", .data = PFUZE3000, }, + { } +}; + +U_BOOT_DRIVER(pmic_pfuze100) = { + .name = "pfuze100 pmic", + .id = UCLASS_PMIC, + .of_match = pfuze100_ids, + .bind = pfuze100_bind, + .ops = &pfuze100_ops, +}; diff --git a/drivers/power/pmic/pmic_pfuze100.c b/drivers/power/pmic/pmic_pfuze100.c index 22a04c0..259b349 100644 --- a/drivers/power/pmic/pmic_pfuze100.c +++ b/drivers/power/pmic/pmic_pfuze100.c @@ -23,7 +23,7 @@ int power_pfuze100_init(unsigned char bus) p->name = name; p->interface = PMIC_I2C; - p->number_of_regs = PMIC_NUM_OF_REGS; + p->number_of_regs = PFUZE100_NUM_OF_REGS; p->hw.i2c.addr = CONFIG_POWER_PFUZE100_I2C_ADDR; p->hw.i2c.tx_num = 1; p->bus = bus; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 955b0b7..caf1efc 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -151,6 +151,14 @@ config FRAMEBUFFER_VESA_MODE default 0x11B if FRAMEBUFFER_VESA_MODE_11B default 0x117 if FRAMEBUFFER_VESA_MODE_USER +config VIDEO_LCD_ANX9804 + bool "ANX9804 bridge chip" + default n + ---help--- + Support for the ANX9804 bridge chip, which can take pixel data coming + from a parallel LCD interface and translate it on the fy into a DP + interface for driving eDP TFT displays. It uses I2C for configuration. + config VIDEO_LCD_SSD2828 bool "SSD2828 bridge chip" default n diff --git a/drivers/video/Makefile b/drivers/video/Makefile index c2c4dfc..e85fd8a 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_VIDEO_COREBOOT) += coreboot_fb.o obj-$(CONFIG_VIDEO_CT69000) += ct69000.o videomodes.o obj-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o obj-$(CONFIG_VIDEO_IMX25LCDC) += imx25lcdc.o videomodes.o +obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o obj-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o diff --git a/drivers/video/anx9804.c b/drivers/video/anx9804.c new file mode 100755 index 0000000..83d60d6 --- /dev/null +++ b/drivers/video/anx9804.c @@ -0,0 +1,188 @@ +/* + * (C) 2015 Hans de Goede <hdegoede@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Support for the ANX9804 bridge chip, which can take pixel data coming + * from a parallel LCD interface and translate it on the flight into a DP + * interface for driving eDP TFT displays. + */ + +#include <common.h> +#include <i2c.h> +#include "anx9804.h" + +#define BIT(x) (1 << (x)) + +/* Registers at i2c address 0x38 */ + +#define ANX9804_HDCP_CONTROL_0_REG 0x01 + +#define ANX9804_SYS_CTRL2_REG 0x81 +#define ANX9804_SYS_CTRL2_CHA_STA 0x04 + +#define ANX9804_SYS_CTRL3_REG 0x82 +#define ANX9804_SYS_CTRL3_VALID_CTRL BIT(0) +#define ANX9804_SYS_CTRL3_F_VALID BIT(1) +#define ANX9804_SYS_CTRL3_HPD_CTRL BIT(4) +#define ANX9804_SYS_CTRL3_F_HPD BIT(5) + +#define ANX9804_LINK_BW_SET_REG 0xa0 +#define ANX9804_LANE_COUNT_SET_REG 0xa1 +#define ANX9804_TRAINING_PTN_SET_REG 0xa2 +#define ANX9804_TRAINING_LANE0_SET_REG 0xa3 +#define ANX9804_TRAINING_LANE1_SET_REG 0xa4 +#define ANX9804_TRAINING_LANE2_SET_REG 0xa5 +#define ANX9804_TRAINING_LANE3_SET_REG 0xa6 + +#define ANX9804_LINK_TRAINING_CTRL_REG 0xa8 +#define ANX9804_LINK_TRAINING_CTRL_EN BIT(0) + +#define ANX9804_LINK_DEBUG_REG 0xb8 +#define ANX9804_PLL_CTRL_REG 0xc7 +#define ANX9804_ANALOG_POWER_DOWN_REG 0xc8 + +/* Registers at i2c address 0x39 */ + +#define ANX9804_DEV_IDH_REG 0x03 + +#define ANX9804_POWERD_CTRL_REG 0x05 +#define ANX9804_POWERD_AUDIO BIT(4) + +#define ANX9804_RST_CTRL_REG 0x06 + +#define ANX9804_RST_CTRL2_REG 0x07 +#define ANX9804_RST_CTRL2_AUX BIT(2) +#define ANX9804_RST_CTRL2_AC_MODE BIT(6) + +#define ANX9804_VID_CTRL1_REG 0x08 +#define ANX9804_VID_CTRL1_VID_EN BIT(7) +#define ANX9804_VID_CTRL1_EDGE BIT(0) + +#define ANX9804_VID_CTRL2_REG 0x09 +#define ANX9804_ANALOG_DEBUG_REG1 0xdc +#define ANX9804_ANALOG_DEBUG_REG3 0xde +#define ANX9804_PLL_FILTER_CTRL1 0xdf +#define ANX9804_PLL_FILTER_CTRL3 0xe1 +#define ANX9804_PLL_FILTER_CTRL 0xe2 +#define ANX9804_PLL_CTRL3 0xe6 + +/** + * anx9804_init() - Init anx9804 parallel lcd to edp bridge chip + * + * This function will init an anx9804 parallel lcd to dp bridge chip + * using the passed in parameters. + * + * @i2c_bus: Number of the i2c bus to which the anx9804 is connected. + * @lanes: Number of displayport lanes to use + * @data_rate: Register value for the bandwidth reg 0x06: 1.62G, 0x0a: 2.7G + * @bpp: Bits per pixel, must be 18 or 24 + */ +void anx9804_init(unsigned int i2c_bus, u8 lanes, u8 data_rate, int bpp) +{ + unsigned int orig_i2c_bus = i2c_get_bus_num(); + u8 c, colordepth; + int i; + + i2c_set_bus_num(i2c_bus); + + if (bpp == 18) + colordepth = 0x00; /* 6 bit */ + else + colordepth = 0x10; /* 8 bit */ + + /* Reset */ + i2c_reg_write(0x39, ANX9804_RST_CTRL_REG, 1); + mdelay(100); + i2c_reg_write(0x39, ANX9804_RST_CTRL_REG, 0); + + /* Write 0 to the powerdown reg (powerup everything) */ + i2c_reg_write(0x39, ANX9804_POWERD_CTRL_REG, 0); + + c = i2c_reg_read(0x39, ANX9804_DEV_IDH_REG); + if (c != 0x98) { + printf("Error anx9804 chipid mismatch\n"); + i2c_set_bus_num(orig_i2c_bus); + return; + } + + for (i = 0; i < 100; i++) { + c = i2c_reg_read(0x38, ANX9804_SYS_CTRL2_REG); + i2c_reg_write(0x38, ANX9804_SYS_CTRL2_REG, c); + c = i2c_reg_read(0x38, ANX9804_SYS_CTRL2_REG); + if ((c & ANX9804_SYS_CTRL2_CHA_STA) == 0) + break; + + mdelay(5); + } + if (i == 100) + printf("Error anx9804 clock is not stable\n"); + + i2c_reg_write(0x39, ANX9804_VID_CTRL2_REG, colordepth); + + /* Set a bunch of analog related register values */ + i2c_reg_write(0x38, ANX9804_PLL_CTRL_REG, 0x07); + i2c_reg_write(0x39, ANX9804_PLL_FILTER_CTRL3, 0x19); + i2c_reg_write(0x39, ANX9804_PLL_CTRL3, 0xd9); + i2c_reg_write(0x39, ANX9804_RST_CTRL2_REG, ANX9804_RST_CTRL2_AC_MODE); + i2c_reg_write(0x39, ANX9804_ANALOG_DEBUG_REG1, 0xf0); + i2c_reg_write(0x39, ANX9804_ANALOG_DEBUG_REG3, 0x99); + i2c_reg_write(0x39, ANX9804_PLL_FILTER_CTRL1, 0x7b); + i2c_reg_write(0x38, ANX9804_LINK_DEBUG_REG, 0x30); + i2c_reg_write(0x39, ANX9804_PLL_FILTER_CTRL, 0x06); + + /* Force HPD */ + i2c_reg_write(0x38, ANX9804_SYS_CTRL3_REG, + ANX9804_SYS_CTRL3_F_HPD | ANX9804_SYS_CTRL3_HPD_CTRL); + + /* Power up and configure lanes */ + i2c_reg_write(0x38, ANX9804_ANALOG_POWER_DOWN_REG, 0x00); + i2c_reg_write(0x38, ANX9804_TRAINING_LANE0_SET_REG, 0x00); + i2c_reg_write(0x38, ANX9804_TRAINING_LANE1_SET_REG, 0x00); + i2c_reg_write(0x38, ANX9804_TRAINING_LANE2_SET_REG, 0x00); + i2c_reg_write(0x38, ANX9804_TRAINING_LANE3_SET_REG, 0x00); + + /* Reset AUX CH */ + i2c_reg_write(0x39, ANX9804_RST_CTRL2_REG, + ANX9804_RST_CTRL2_AC_MODE | ANX9804_RST_CTRL2_AUX); + i2c_reg_write(0x39, ANX9804_RST_CTRL2_REG, + ANX9804_RST_CTRL2_AC_MODE); + + /* Powerdown audio and some other unused bits */ + i2c_reg_write(0x39, ANX9804_POWERD_CTRL_REG, ANX9804_POWERD_AUDIO); + i2c_reg_write(0x38, ANX9804_HDCP_CONTROL_0_REG, 0x00); + i2c_reg_write(0x38, 0xa7, 0x00); + + /* Set data-rate / lanes */ + i2c_reg_write(0x38, ANX9804_LINK_BW_SET_REG, data_rate); + i2c_reg_write(0x38, ANX9804_LANE_COUNT_SET_REG, lanes); + + /* Link training */ + i2c_reg_write(0x38, ANX9804_LINK_TRAINING_CTRL_REG, + ANX9804_LINK_TRAINING_CTRL_EN); + mdelay(5); + for (i = 0; i < 100; i++) { + c = i2c_reg_read(0x38, ANX9804_LINK_TRAINING_CTRL_REG); + if ((c & 0x01) == 0) + break; + + mdelay(5); + } + if(i == 100) { + printf("Error anx9804 link training timeout\n"); + i2c_set_bus_num(orig_i2c_bus); + return; + } + + /* Enable */ + i2c_reg_write(0x39, ANX9804_VID_CTRL1_REG, + ANX9804_VID_CTRL1_VID_EN | ANX9804_VID_CTRL1_EDGE); + /* Force stream valid */ + i2c_reg_write(0x38, ANX9804_SYS_CTRL3_REG, + ANX9804_SYS_CTRL3_F_HPD | ANX9804_SYS_CTRL3_HPD_CTRL | + ANX9804_SYS_CTRL3_F_VALID | ANX9804_SYS_CTRL3_VALID_CTRL); + + i2c_set_bus_num(orig_i2c_bus); +} diff --git a/drivers/video/anx9804.h b/drivers/video/anx9804.h new file mode 100644 index 0000000..6c5daf9 --- /dev/null +++ b/drivers/video/anx9804.h @@ -0,0 +1,25 @@ +/* + * (C) 2015 Hans de Goede <hdegoede@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Support for the ANX9804 bridge chip, which can take pixel data coming + * from a parallel LCD interface and translate it on the flight into a DP + * interface for driving eDP TFT displays. + */ + +#ifndef _ANX9804_H +#define _ANX9804_H + +#define ANX9804_DATA_RATE_1620M 0x06 +#define ANX9804_DATA_RATE_2700M 0x0a + +#ifdef CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804 +void anx9804_init(unsigned int i2c_bus, u8 lanes, u8 data_rate, int bpp); +#else +static inline void anx9804_init(unsigned int i2c_bus, u8 lanes, u8 data_rate, + int bpp) {} +#endif +#endif diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index 7f2ddc1..30e0317 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -283,9 +283,10 @@ void console_cursor(int state); #define VIDEO_COLS VIDEO_VISIBLE_COLS #define VIDEO_ROWS VIDEO_VISIBLE_ROWS -#define VIDEO_SIZE (VIDEO_ROWS*VIDEO_COLS*VIDEO_PIXEL_SIZE) -#define VIDEO_PIX_BLOCKS (VIDEO_SIZE >> 2) -#define VIDEO_LINE_LEN (VIDEO_COLS*VIDEO_PIXEL_SIZE) +#ifndef VIDEO_LINE_LEN +#define VIDEO_LINE_LEN (VIDEO_COLS * VIDEO_PIXEL_SIZE) +#endif +#define VIDEO_SIZE (VIDEO_ROWS * VIDEO_LINE_LEN) #define VIDEO_BURST_LEN (VIDEO_COLS/8) #ifdef CONFIG_VIDEO_LOGO @@ -1306,7 +1307,7 @@ static int display_rle8_bitmap(struct bmp_image *img, int xoff, int yoff, struct palette p[256]; struct bmp_color_table_entry cte; int green_shift, red_off; - int limit = VIDEO_COLS * VIDEO_ROWS; + int limit = (VIDEO_LINE_LEN / VIDEO_PIXEL_SIZE) * VIDEO_ROWS; int pixels = 0; x = 0; @@ -1314,7 +1315,8 @@ static int display_rle8_bitmap(struct bmp_image *img, int xoff, int yoff, ncolors = __le32_to_cpu(img->header.colors_used); bpp = VIDEO_PIXEL_SIZE; fbp = (unsigned char *) ((unsigned int) video_fb_address + - (((y + yoff) * VIDEO_COLS) + xoff) * bpp); + (y + yoff) * VIDEO_LINE_LEN + + xoff * bpp); bm = (uchar *) img + __le32_to_cpu(img->header.data_offset); @@ -1368,8 +1370,8 @@ static int display_rle8_bitmap(struct bmp_image *img, int xoff, int yoff, y--; fbp = (unsigned char *) ((unsigned int) video_fb_address + - (((y + yoff) * VIDEO_COLS) + - xoff) * bpp); + (y + yoff) * VIDEO_LINE_LEN + + xoff * bpp); continue; case 1: /* end of bitmap data marker */ @@ -1381,8 +1383,8 @@ static int display_rle8_bitmap(struct bmp_image *img, int xoff, int yoff, y -= bm[3]; fbp = (unsigned char *) ((unsigned int) video_fb_address + - (((y + yoff) * VIDEO_COLS) + - x + xoff) * bpp); + (y + yoff) * VIDEO_LINE_LEN + + xoff * bpp); bm += 4; break; default: @@ -1561,7 +1563,7 @@ int video_display_bitmap(ulong bmp_image, int x, int y) bmap = (uchar *) bmp + le32_to_cpu(bmp->header.data_offset); fb = (uchar *) (video_fb_address + - ((y + height - 1) * VIDEO_COLS * VIDEO_PIXEL_SIZE) + + ((y + height - 1) * VIDEO_LINE_LEN) + x * VIDEO_PIXEL_SIZE); #ifdef CONFIG_VIDEO_BMP_RLE8 @@ -1597,7 +1599,7 @@ int video_display_bitmap(ulong bmp_image, int x, int y) cte.blue); } bmap += padded_line; - fb -= (VIDEO_VISIBLE_COLS + width) * + fb -= VIDEO_LINE_LEN + width * VIDEO_PIXEL_SIZE; } break; @@ -1628,8 +1630,8 @@ int video_display_bitmap(ulong bmp_image, int x, int y) *fb++ = *bmap++; } bmap += padded_line; - fb -= (VIDEO_VISIBLE_COLS + width) * - VIDEO_PIXEL_SIZE; + fb -= VIDEO_LINE_LEN + width * + VIDEO_PIXEL_SIZE; } break; case GDF__8BIT_332RGB: @@ -1642,8 +1644,8 @@ int video_display_bitmap(ulong bmp_image, int x, int y) cte.blue); } bmap += padded_line; - fb -= (VIDEO_VISIBLE_COLS + width) * - VIDEO_PIXEL_SIZE; + fb -= VIDEO_LINE_LEN + width * + VIDEO_PIXEL_SIZE; } break; case GDF_15BIT_555RGB: @@ -1666,8 +1668,8 @@ int video_display_bitmap(ulong bmp_image, int x, int y) #endif } bmap += padded_line; - fb -= (VIDEO_VISIBLE_COLS + width) * - VIDEO_PIXEL_SIZE; + fb -= VIDEO_LINE_LEN + width * + VIDEO_PIXEL_SIZE; } break; case GDF_16BIT_565RGB: @@ -1680,8 +1682,8 @@ int video_display_bitmap(ulong bmp_image, int x, int y) cte.blue); } bmap += padded_line; - fb -= (VIDEO_VISIBLE_COLS + width) * - VIDEO_PIXEL_SIZE; + fb -= VIDEO_LINE_LEN + width * + VIDEO_PIXEL_SIZE; } break; case GDF_32BIT_X888RGB: @@ -1694,8 +1696,8 @@ int video_display_bitmap(ulong bmp_image, int x, int y) cte.blue); } bmap += padded_line; - fb -= (VIDEO_VISIBLE_COLS + width) * - VIDEO_PIXEL_SIZE; + fb -= VIDEO_LINE_LEN + width * + VIDEO_PIXEL_SIZE; } break; case GDF_24BIT_888RGB: @@ -1708,8 +1710,8 @@ int video_display_bitmap(ulong bmp_image, int x, int y) cte.blue); } bmap += padded_line; - fb -= (VIDEO_VISIBLE_COLS + width) * - VIDEO_PIXEL_SIZE; + fb -= VIDEO_LINE_LEN + width * + VIDEO_PIXEL_SIZE; } break; } @@ -1728,8 +1730,8 @@ int video_display_bitmap(ulong bmp_image, int x, int y) bmap += 3; } bmap += padded_line; - fb -= (VIDEO_VISIBLE_COLS + width) * - VIDEO_PIXEL_SIZE; + fb -= VIDEO_LINE_LEN + width * + VIDEO_PIXEL_SIZE; } break; case GDF_15BIT_555RGB: @@ -1751,8 +1753,8 @@ int video_display_bitmap(ulong bmp_image, int x, int y) bmap += 3; } bmap += padded_line; - fb -= (VIDEO_VISIBLE_COLS + width) * - VIDEO_PIXEL_SIZE; + fb -= VIDEO_LINE_LEN + width * + VIDEO_PIXEL_SIZE; } break; case GDF_16BIT_565RGB: @@ -1765,8 +1767,8 @@ int video_display_bitmap(ulong bmp_image, int x, int y) bmap += 3; } bmap += padded_line; - fb -= (VIDEO_VISIBLE_COLS + width) * - VIDEO_PIXEL_SIZE; + fb -= VIDEO_LINE_LEN + width * + VIDEO_PIXEL_SIZE; } break; case GDF_32BIT_X888RGB: @@ -1779,8 +1781,8 @@ int video_display_bitmap(ulong bmp_image, int x, int y) bmap += 3; } bmap += padded_line; - fb -= (VIDEO_VISIBLE_COLS + width) * - VIDEO_PIXEL_SIZE; + fb -= VIDEO_LINE_LEN + width * + VIDEO_PIXEL_SIZE; } break; case GDF_24BIT_888RGB: @@ -1793,8 +1795,8 @@ int video_display_bitmap(ulong bmp_image, int x, int y) bmap += 3; } bmap += padded_line; - fb -= (VIDEO_VISIBLE_COLS + width) * - VIDEO_PIXEL_SIZE; + fb -= VIDEO_LINE_LEN + width * + VIDEO_PIXEL_SIZE; } break; default: @@ -1826,20 +1828,16 @@ int video_display_bitmap(ulong bmp_image, int x, int y) static int video_logo_xpos; static int video_logo_ypos; -static void plot_logo_or_black(void *screen, int width, int x, int y, \ - int black); +static void plot_logo_or_black(void *screen, int x, int y, int black); -static void logo_plot(void *screen, int width, int x, int y) +static void logo_plot(void *screen, int x, int y) { - plot_logo_or_black(screen, width, x, y, 0); + plot_logo_or_black(screen, x, y, 0); } static void logo_black(void) { - plot_logo_or_black(video_fb_address, \ - VIDEO_COLS, \ - video_logo_xpos, \ - video_logo_ypos, \ + plot_logo_or_black(video_fb_address, video_logo_xpos, video_logo_ypos, 1); } @@ -1858,11 +1856,11 @@ U_BOOT_CMD( " " ); -static void plot_logo_or_black(void *screen, int width, int x, int y, int black) +static void plot_logo_or_black(void *screen, int x, int y, int black) { int xcount, i; - int skip = (width - VIDEO_LOGO_WIDTH) * VIDEO_PIXEL_SIZE; + int skip = VIDEO_LINE_LEN - VIDEO_LOGO_WIDTH * VIDEO_PIXEL_SIZE; int ycount = video_logo_height; unsigned char r, g, b, *logo_red, *logo_blue, *logo_green; unsigned char *source; @@ -1880,7 +1878,7 @@ static void plot_logo_or_black(void *screen, int width, int x, int y, int black) y = max(0, (int)(VIDEO_VISIBLE_ROWS - VIDEO_LOGO_HEIGHT + y + 1)); #endif /* CONFIG_SPLASH_SCREEN_ALIGN */ - dest = (unsigned char *)screen + (y * width + x) * VIDEO_PIXEL_SIZE; + dest = (unsigned char *)screen + y * VIDEO_LINE_LEN + x * VIDEO_PIXEL_SIZE; #ifdef CONFIG_VIDEO_BMP_LOGO source = bmp_logo_bitmap; @@ -2009,8 +2007,7 @@ static void *video_logo(void) } #endif /* CONFIG_SPLASH_SCREEN */ - logo_plot(video_fb_address, VIDEO_COLS, - video_logo_xpos, video_logo_ypos); + logo_plot(video_fb_address, video_logo_xpos, video_logo_ypos); #ifdef CONFIG_SPLASH_SCREEN_ALIGN /* diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index 1868185..fc1aea3 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -15,12 +15,15 @@ #include <asm/global_data.h> #include <asm/gpio.h> #include <asm/io.h> +#include <axp221.h> #include <errno.h> #include <fdtdec.h> #include <fdt_support.h> #include <i2c.h> +#include <malloc.h> #include <video_fb.h> #include "videomodes.h" +#include "anx9804.h" #include "hitachi_tx18d42vm_lcd.h" #include "ssd2828.h" @@ -51,6 +54,7 @@ struct sunxi_display { GraphicDevice graphic_device; enum sunxi_monitor monitor; unsigned int depth; + unsigned int fb_addr; unsigned int fb_size; } sunxi_display; @@ -483,7 +487,9 @@ static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode, setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE); if (mode->vmode == FB_VMODE_INTERLACED) setbits_le32(&de_be->mode, +#ifndef CONFIG_MACH_SUN5I SUNXI_DE_BE_MODE_DEFLICKER_ENABLE | +#endif SUNXI_DE_BE_MODE_INTERLACE_ENABLE); if (sunxi_is_composite()) { @@ -517,6 +523,7 @@ static void sunxi_lcdc_pll_set(int tcon, int dotclock, int value, n, m, min_m, max_m, diff; int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF; int best_double = 0; + bool use_mipi_pll = false; if (tcon == 0) { #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL @@ -567,16 +574,42 @@ static void sunxi_lcdc_pll_set(int tcon, int dotclock, } } - debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n", - dotclock, (best_double + 1) * 3000 * best_n / best_m, - best_double + 1, best_n, best_m); +#ifdef CONFIG_MACH_SUN6I + /* + * Use the MIPI pll if we've been unable to find any matching setting + * for PLL3, this happens with high dotclocks because of min_m = 6. + */ + if (tcon == 0 && best_n == 0) { + use_mipi_pll = true; + best_m = 6; /* Minimum m for tcon0 */ + } - clock_set_pll3(best_n * 3000000); + if (use_mipi_pll) { + clock_set_pll3(297000000); /* Fix the video pll at 297 MHz */ + clock_set_mipi_pll(best_m * dotclock * 1000); + debug("dotclock: %dkHz = %dkHz via mipi pll\n", + dotclock, clock_get_mipi_pll() / best_m / 1000); + } else +#endif + { + clock_set_pll3(best_n * 3000000); + debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n", + dotclock, + (best_double + 1) * clock_get_pll3() / best_m / 1000, + best_double + 1, best_n, best_m); + } if (tcon == 0) { - writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | - (best_double ? CCM_LCD_CH0_CTRL_PLL3_2X : - CCM_LCD_CH0_CTRL_PLL3), + u32 pll; + + if (use_mipi_pll) + pll = CCM_LCD_CH0_CTRL_MIPI_PLL; + else if (best_double) + pll = CCM_LCD_CH0_CTRL_PLL3_2X; + else + pll = CCM_LCD_CH0_CTRL_PLL3; + + writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | pll, &ccm->lcd0_ch0_clk_cfg); } else { writel(CCM_LCD_CH1_CTRL_GATE | @@ -734,13 +767,17 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; int bp, clk_delay, clk_div, clk_double, pin, total, val; - for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) + for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) { #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0); #endif #ifdef CONFIG_VIDEO_LCD_IF_LVDS sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LVDS0); #endif +#ifdef CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804 + sunxi_gpio_set_drv(pin, 3); +#endif + } sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double); @@ -872,6 +909,13 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, SUNXI_LCDC_TCON_VSYNC_MASK | SUNXI_LCDC_TCON_HSYNC_MASK); } + +#ifdef CONFIG_MACH_SUN5I + if (sunxi_is_composite()) + clrsetbits_le32(&lcdc->mux_ctrl, SUNXI_LCDC_MUX_CTRL_SRC0_MASK, + SUNXI_LCDC_MUX_CTRL_SRC0(1)); +#endif + sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double); } #endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */ @@ -997,6 +1041,8 @@ static void sunxi_tvencoder_mode_set(void) struct sunxi_tve_reg * const tve = (struct sunxi_tve_reg *)SUNXI_TVE0_BASE; + /* Reset off */ + setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_TVE_RST); /* Clock on */ setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0); @@ -1168,6 +1214,17 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode, break; case sunxi_monitor_lcd: sunxi_lcdc_panel_enable(); + if (IS_ENABLED(CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804)) { + /* + * The anx9804 needs 1.8V from eldo3, we do this here + * and not via CONFIG_AXP221_ELDO3 from board_init() + * to avoid turning this on when using hdmi output. + */ + axp221_set_eldo(3, 1800); + anx9804_init(CONFIG_VIDEO_LCD_I2C_BUS, 4, + ANX9804_DATA_RATE_1620M, + sunxi_display.depth); + } if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) { mdelay(50); /* Wait for lcd controller power on */ hitachi_tx18d42vm_init(); @@ -1297,9 +1354,10 @@ void *video_hw_init(void) #ifdef CONFIG_VIDEO_HDMI int ret, hpd, hpd_delay, edid; #endif + int i, overscan_offset, overscan_x, overscan_y; + unsigned int fb_dma_addr; char mon[16]; char *lcd_mode = CONFIG_VIDEO_LCD_MODE; - int i; memset(&sunxi_display, 0, sizeof(struct sunxi_display)); @@ -1310,6 +1368,8 @@ void *video_hw_init(void) hpd_delay = video_get_option_int(options, "hpd_delay", 500); edid = video_get_option_int(options, "edid", 1); #endif + overscan_x = video_get_option_int(options, "overscan_x", -1); + overscan_y = video_get_option_int(options, "overscan_y", -1); sunxi_display.monitor = sunxi_get_default_mon(true); video_get_option_string(options, "monitor", mon, sizeof(mon), sunxi_get_mon_desc(sunxi_display.monitor)); @@ -1386,8 +1446,20 @@ void *video_hw_init(void) break; } + /* Yes these defaults are quite high, overscan on composite sucks... */ + if (overscan_x == -1) + overscan_x = sunxi_is_composite() ? 32 : 0; + if (overscan_y == -1) + overscan_y = sunxi_is_composite() ? 20 : 0; + sunxi_display.fb_size = (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff; + overscan_offset = (overscan_y * mode->xres + overscan_x) * 4; + /* We want to keep the fb_base for simplefb page aligned, where as + * the sunxi dma engines will happily accept an unaligned address. */ + if (overscan_offset) + sunxi_display.fb_size += 0x1000; + if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) { printf("Error need %dkB for fb, but only %dkB is reserved\n", sunxi_display.fb_size >> 10, @@ -1395,25 +1467,36 @@ void *video_hw_init(void) return NULL; } - printf("Setting up a %dx%d%s %s console\n", mode->xres, mode->yres, + printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n", + mode->xres, mode->yres, (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "", - sunxi_get_mon_desc(sunxi_display.monitor)); + sunxi_get_mon_desc(sunxi_display.monitor), + overscan_x, overscan_y); gd->fb_base = gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sunxi_display.fb_size; sunxi_engines_init(); - sunxi_mode_set(mode, gd->fb_base - CONFIG_SYS_SDRAM_BASE); + + fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE; + sunxi_display.fb_addr = gd->fb_base; + if (overscan_offset) { + fb_dma_addr += 0x1000 - (overscan_offset & 0xfff); + sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff; + memset((void *)gd->fb_base, 0, sunxi_display.fb_size); + flush_cache(gd->fb_base, sunxi_display.fb_size); + } + sunxi_mode_set(mode, fb_dma_addr); /* * These are the only members of this structure that are used. All the - * others are driver specific. There is nothing to decribe pitch or - * stride, but we are lucky with our hw. + * others are driver specific. The pitch is stored in plnSizeX. */ - graphic_device->frameAdrs = gd->fb_base; + graphic_device->frameAdrs = sunxi_display.fb_addr; graphic_device->gdfIndex = GDF_32BIT_X888RGB; graphic_device->gdfBytesPP = 4; - graphic_device->winSizeX = mode->xres; - graphic_device->winSizeY = mode->yres; + graphic_device->winSizeX = mode->xres - 2 * overscan_x; + graphic_device->winSizeY = mode->yres - 2 * overscan_y; + graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP; return graphic_device; } @@ -1490,10 +1573,9 @@ int sunxi_simplefb_setup(void *blob) return ret; } - ret = fdt_setup_simplefb_node(blob, offset, gd->fb_base, + ret = fdt_setup_simplefb_node(blob, offset, sunxi_display.fb_addr, graphic_device->winSizeX, graphic_device->winSizeY, - graphic_device->winSizeX * graphic_device->gdfBytesPP, - "x8r8g8b8"); + graphic_device->plnSizeX, "x8r8g8b8"); if (ret) eprintf("Cannot setup simplefb: Error setting properties\n"); |