summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/sunxi_gpio.c22
-rw-r--r--drivers/power/pmic/Kconfig7
-rw-r--r--drivers/power/pmic/pfuze100.c96
-rw-r--r--drivers/power/pmic/pmic_pfuze100.c2
-rw-r--r--drivers/video/Kconfig8
-rw-r--r--drivers/video/Makefile1
-rwxr-xr-xdrivers/video/anx9804.c188
-rw-r--r--drivers/video/anx9804.h25
-rw-r--r--drivers/video/cfb_console.c89
-rw-r--r--drivers/video/sunxi_display.c122
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");