* [U-Boot] [PATCH] video: Add new driver for Silicon Motion SM501/SM502 Part 2/2
@ 2008-12-04 21:10 Stefan Althoefer
2008-12-07 0:33 ` Anatolij Gustschin
0 siblings, 1 reply; 2+ messages in thread
From: Stefan Althoefer @ 2008-12-04 21:10 UTC (permalink / raw)
To: u-boot
[PATCH] video: Add new driver for Silicon Motion SM501/SM502
This patch adds a new driver for SM501/SM502. Compared to the
existing driver it allows dynamic selection of resolution
(environment: videomode).
The drive is based on Vincent Sanders and Ben Dooks' linux
kernel driver.
Use CONFIG_VIDEO_SM501NEW to enable the driver.
This has been tested on Janz emPC-A400. On this platform
the SM501 is connected via PCI.
The patch is against "latest" u-boot git-repository
Please (still) be patient if style of submission or patches are
offending.
Signed-off-by: Stefan Althoefer <stefan.althoefer@web.de>
----
diff -uprN u-boot-orig//drivers/video/Makefile u-boot/drivers/video/Makefile
--- u-boot-orig//drivers/video/Makefile 2008-12-02 17:25:31.000000000 +0100
+++ u-boot/drivers/video/Makefile 2008-12-02 18:29:14.000000000 +0100
@@ -33,6 +33,7 @@ COBJS-$(CONFIG_VIDEO_MB862xx) += mb862xx
COBJS-$(CONFIG_VIDEO_SED13806) += sed13806.o
COBJS-$(CONFIG_SED156X) += sed156x.o
COBJS-$(CONFIG_VIDEO_SM501) += sm501.o
+COBJS-$(CONFIG_VIDEO_SM501NEW) += sm501new.o
COBJS-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o
COBJS-y += videomodes.o
diff -uprN u-boot-orig//drivers/video/sm501new.c u-boot/drivers/video/sm501new.c
--- u-boot-orig//drivers/video/sm501new.c 1970-01-01 01:00:00.000000000 +0100
+++ u-boot/drivers/video/sm501new.c 2008-12-03 11:47:22.000000000 +0100
@@ -0,0 +1,1533 @@
+/* Large parts of this have been taken from the linux kernel source code
+ * with the following licencse:
+ *
+ * Copyright (c) 2006 Simtec Electronics
+ * Vincent Sanders <vince@simtec.co.uk>
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Framebuffer driver for the Silicon Motion SM501
+ */
+
+/* Ported to u-boot:
+ *
+ * Copyright (c) 2008 StefanAlthoefer (as at janz.de)
+ */
+
+
+#include <common.h>
+#include <exports.h>
+#include <config.h>
+#include <watchdog.h>
+#include <command.h>
+#include <image.h>
+#include <asm/byteorder.h>
+#include <pci.h>
+#include <video_fb.h>
+#include <video.h>
+#include "videomodes.h"
+#include "sm501new-regs.h"
+#include "sm501new.h"
+
+
+#ifdef CONFIG_VIDEO_SM501NEW
+
+#undef DEBUG
+
+/* this should be in pci_ids.h */
+#define PCI_DEVICE_ID_SMI_501 0x0501
+
+#define BIG_ENDIAN_HOST
+#define VIDEO_MEM_SIZE (4*1024*1024)
+
+#define SM501_FRAMEBUFFER_ADDR 0
+
+#define HEAD_CRT 0
+#define HEAD_PANEL 1
+
+#if defined(BIG_ENDIAN_HOST)
+static inline unsigned int LONGSWAP(unsigned int x)
+{
+ return (
+ ((x<<24) & 0xff000000) |
+ ((x<< 8) & 0x00ff0000) |
+ ((x>> 8) & 0x0000ff00) |
+ ((x>>24) & 0x000000ff) );
+}
+static inline unsigned int readl(void *addr)
+{
+ return LONGSWAP((*(volatile unsigned int *)(addr)));
+}
+static inline void writel(unsigned int data, void *addr)
+{
+ /*printf("%p <- %x\n", addr, data); */
+ *(volatile unsigned int *)(addr) = LONGSWAP(data);
+}
+#else
+#define readl(addr) (*(volatile unsigned int*)(addr))
+#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b))
+#endif
+
+/*
+ * Export Graphic Device
+ */
+GraphicDevice smi;
+
+
+
+static struct pci_device_id supported[] = {
+ { PCI_VENDOR_ID_SMI, PCI_DEVICE_ID_SMI_501 },
+ { }
+};
+
+
+struct sm501_devdata {
+ struct sm501_platdata *platdata;
+ unsigned long pm_misc;
+ int unit_power[20];
+ unsigned int pdev_id;
+ unsigned int irq;
+ void *regs;
+ void *dc;
+ void *vmem;
+
+ GraphicDevice *gd;
+ struct ctfb_res_modes *mode;
+ int bits_per_pixel;
+ int xres_virtual;
+ int yres_virtual;
+};
+
+struct sm501_devdata smi_devd;
+
+
+static void mdelay(unsigned int delay)
+{
+ while( delay-- > 0 ){
+ udelay(1000);
+ }
+}
+
+#define MHZ (1000 * 1000)
+
+
+#ifdef DEBUG
+#define dev_dbg(XXX,...) printf(__VA_ARGS__)
+#define dev_info(XXX,...) printf(__VA_ARGS__)
+#define dev_err(XXX,...) printf(__VA_ARGS__)
+
+static const unsigned int misc_div[] = {
+ [0] = 1,
+ [1] = 2,
+ [2] = 4,
+ [3] = 8,
+ [4] = 16,
+ [5] = 32,
+ [6] = 64,
+ [7] = 128,
+ [8] = 3,
+ [9] = 6,
+ [10] = 12,
+ [11] = 24,
+ [12] = 48,
+ [13] = 96,
+ [14] = 192,
+ [15] = 384,
+};
+
+static const unsigned int px_div[] = {
+ [0] = 1,
+ [1] = 2,
+ [2] = 4,
+ [3] = 8,
+ [4] = 16,
+ [5] = 32,
+ [6] = 64,
+ [7] = 128,
+ [8] = 3,
+ [9] = 6,
+ [10] = 12,
+ [11] = 24,
+ [12] = 48,
+ [13] = 96,
+ [14] = 192,
+ [15] = 384,
+ [16] = 5,
+ [17] = 10,
+ [18] = 20,
+ [19] = 40,
+ [20] = 80,
+ [21] = 160,
+ [22] = 320,
+ [23] = 604,
+};
+
+static unsigned long decode_div(unsigned long pll2, unsigned long val,
+ unsigned int lshft, unsigned int selbit,
+ unsigned long mask, const unsigned int *dtab)
+{
+ if (val & selbit)
+ pll2 = 288 * MHZ;
+
+ return pll2 / dtab[(val >> lshft) & mask];
+}
+
+#define fmt_freq(x) ((x) / MHZ), ((x) % MHZ), (x)
+
+/* sm501_dump_clk
+ *
+ * Print out the current clock configuration for the device
+*/
+
+static void sm501_dump_clk(struct sm501_devdata *sm)
+{
+ unsigned long misct = readl(sm->regs + SM501_MISC_TIMING);
+ unsigned long pm0 = readl(sm->regs + SM501_POWER_MODE_0_CLOCK);
+ unsigned long pm1 = readl(sm->regs + SM501_POWER_MODE_1_CLOCK);
+ unsigned long pmc = readl(sm->regs + SM501_POWER_MODE_CONTROL);
+ unsigned long sdclk0, sdclk1;
+ unsigned long pll2 = 0;
+
+ switch (misct & 0x30) {
+ case 0x00:
+ pll2 = 336 * MHZ;
+ break;
+ case 0x10:
+ pll2 = 288 * MHZ;
+ break;
+ case 0x20:
+ pll2 = 240 * MHZ;
+ break;
+ case 0x30:
+ pll2 = 192 * MHZ;
+ break;
+ }
+
+ sdclk0 = (misct & (1<<12)) ? pll2 : 288 * MHZ;
+ sdclk0 /= misc_div[((misct >> 8) & 0xf)];
+
+ sdclk1 = (misct & (1<<20)) ? pll2 : 288 * MHZ;
+ sdclk1 /= misc_div[((misct >> 16) & 0xf)];
+
+ dev_dbg(sm->dev, "MISCT=%08lx, PM0=%08lx, PM1=%08lx\n",
+ misct, pm0, pm1);
+
+ dev_dbg(sm->dev, "PLL2 = %ld.%ld MHz (%ld), SDCLK0=%08lx, SDCLK1=%08lx\n",
+ fmt_freq(pll2), sdclk0, sdclk1);
+
+ dev_dbg(sm->dev, "SDRAM: PM0=%ld, PM1=%ld\n", sdclk0, sdclk1);
+
+ dev_dbg(sm->dev, "PM0[%c]: "
+ "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), "
+ "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n",
+ (pmc & 3 ) == 0 ? '*' : '-',
+ fmt_freq(decode_div(pll2, pm0, 24, 1<<29, 31, px_div)),
+ fmt_freq(decode_div(pll2, pm0, 16, 1<<20, 15, misc_div)),
+ fmt_freq(decode_div(pll2, pm0, 8, 1<<12, 15, misc_div)),
+ fmt_freq(decode_div(pll2, pm0, 0, 1<<4, 15, misc_div)));
+
+ dev_dbg(sm->dev, "PM1[%c]: "
+ "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), "
+ "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n",
+ (pmc & 3 ) == 1 ? '*' : '-',
+ fmt_freq(decode_div(pll2, pm1, 24, 1<<29, 31, px_div)),
+ fmt_freq(decode_div(pll2, pm1, 16, 1<<20, 15, misc_div)),
+ fmt_freq(decode_div(pll2, pm1, 8, 1<<12, 15, misc_div)),
+ fmt_freq(decode_div(pll2, pm1, 0, 1<<4, 15, misc_div)));
+}
+
+static void sm501_dump_regs(struct sm501_devdata *sm)
+{
+ void *regs = sm->regs;
+
+ dev_info(sm->dev, "System Control %08x\n",
+ readl(regs + SM501_SYSTEM_CONTROL));
+ dev_info(sm->dev, "Misc Control %08x\n",
+ readl(regs + SM501_MISC_CONTROL));
+ dev_info(sm->dev, "GPIO Control Low %08x\n",
+ readl(regs + SM501_GPIO31_0_CONTROL));
+ dev_info(sm->dev, "GPIO Control Hi %08x\n",
+ readl(regs + SM501_GPIO63_32_CONTROL));
+ dev_info(sm->dev, "DRAM Control %08x\n",
+ readl(regs + SM501_DRAM_CONTROL));
+ dev_info(sm->dev, "Arbitration Ctrl %08x\n",
+ readl(regs + SM501_ARBTRTN_CONTROL));
+ dev_info(sm->dev, "Misc Timing %08x\n",
+ readl(regs + SM501_MISC_TIMING));
+}
+
+static void sm501_dump_gate(struct sm501_devdata *sm)
+{
+ dev_info(sm->dev, "CurrentGate %08x\n",
+ readl(sm->regs + SM501_CURRENT_GATE));
+ dev_info(sm->dev, "CurrentClock %08x\n",
+ readl(sm->regs + SM501_CURRENT_CLOCK));
+ dev_info(sm->dev, "PowerModeControl %08x\n",
+ readl(sm->regs + SM501_POWER_MODE_CONTROL));
+}
+#else
+static inline void sm501_dump_gate(struct sm501_devdata *sm) { }
+static inline void sm501_dump_regs(struct sm501_devdata *sm) { }
+static inline void sm501_dump_clk(struct sm501_devdata *sm) { }
+
+#define dev_dbg(XXX,...)
+#define dev_info(XXX,...)
+#define dev_err(XXX,...)
+#endif
+
+/* sm501_sync_regs
+ *
+ * ensure the
+*/
+
+static void sm501_sync_regs(struct sm501_devdata *sm)
+{
+ readl(sm->regs);
+}
+
+static inline void sm501_mdelay(struct sm501_devdata *sm, unsigned int delay)
+{
+ mdelay(delay);
+}
+
+/* sm501_misc_control
+ *
+ * alters the miscellaneous control parameters
+*/
+
+int sm501_misc_control(struct sm501_devdata *sm,
+ unsigned long set, unsigned long clear)
+{
+ unsigned long misc;
+ unsigned long to;
+
+ misc = readl(sm->regs + SM501_MISC_CONTROL);
+ to = (misc & ~clear) | set;
+
+ if (to != misc) {
+ writel(to, sm->regs + SM501_MISC_CONTROL);
+ sm501_sync_regs(sm);
+
+ dev_dbg(sm->dev, "MISC_CONTROL %08lx\n", misc);
+ }
+
+ return to;
+}
+
+
+/* sm501_modify_reg
+ *
+ * Modify a register in the SM501 which may be shared with other
+ * drivers.
+*/
+
+unsigned long sm501_modify_reg(struct sm501_devdata *sm,
+ unsigned long reg,
+ unsigned long set,
+ unsigned long clear)
+{
+ unsigned long data;
+
+ data = readl(sm->regs + reg);
+ data |= set;
+ data &= ~clear;
+
+ writel(data, sm->regs + reg);
+ sm501_sync_regs(sm);
+
+ return data;
+}
+
+
+unsigned long sm501_gpio_get(struct sm501_devdata *sm,
+ unsigned long gpio)
+{
+ unsigned long result;
+ unsigned long reg;
+
+ reg = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW;
+ result = readl(sm->regs + reg);
+
+ result >>= (gpio & 31);
+ return result & 1UL;
+}
+
+
+void sm501_gpio_set(struct sm501_devdata *sm,
+ unsigned long gpio,
+ unsigned int to,
+ unsigned int dir)
+{
+ unsigned long bit = 1 << (gpio & 31);
+ unsigned long base;
+ unsigned long val;
+
+ base = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW;
+ base += SM501_GPIO;
+
+ val = readl(sm->regs + base) & ~bit;
+ if (to)
+ val |= bit;
+ writel(val, sm->regs + base);
+
+ val = readl(sm->regs + SM501_GPIO_DDR_LOW) & ~bit;
+ if (dir)
+ val |= bit;
+
+ writel(val, sm->regs + SM501_GPIO_DDR_LOW);
+ sm501_sync_regs(sm);
+}
+
+
+/* sm501_unit_power
+ *
+ * alters the power active gate to set specific units on or off
+ */
+
+int sm501_unit_power(struct sm501_devdata *sm, unsigned int unit, unsigned int to)
+{
+ unsigned long mode;
+ unsigned long gate;
+ unsigned long clock;
+
+ mode = readl(sm->regs + SM501_POWER_MODE_CONTROL);
+ gate = readl(sm->regs + SM501_CURRENT_GATE);
+ clock = readl(sm->regs + SM501_CURRENT_CLOCK);
+
+ mode &= 3; /* get current power mode */
+
+ if (unit >= ARRAY_SIZE(sm->unit_power)) {
+ dev_err(dev, "%s: bad unit %d\n", __FUNCTION__, unit);
+ goto already;
+ }
+
+ dev_dbg(sm->dev, "%s: unit %d, cur %d, to %d\n", __FUNCTION__, unit,
+ sm->unit_power[unit], to);
+
+ if (to == 0 && sm->unit_power[unit] == 0) {
+ dev_err(sm->dev, "unit %d is already shutdown\n", unit);
+ goto already;
+ }
+
+ sm->unit_power[unit] += to ? 1 : -1;
+ to = sm->unit_power[unit] ? 1 : 0;
+
+ if (to) {
+ if (gate & (1 << unit))
+ goto already;
+ gate |= (1 << unit);
+ } else {
+ if (!(gate & (1 << unit)))
+ goto already;
+ gate &= ~(1 << unit);
+ }
+
+ switch (mode) {
+ case 1:
+ writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
+ writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
+ mode = 0;
+ break;
+ case 2:
+ case 0:
+ writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
+ writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
+ mode = 1;
+ break;
+
+ default:
+ return -1;
+ }
+
+ writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
+ sm501_sync_regs(sm);
+
+ dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n",
+ gate, clock, mode);
+
+ sm501_mdelay(sm, 16);
+
+ already:
+ return gate;
+}
+
+
+/* Perform a rounded division. */
+static long sm501fb_round_div(long num, long denom)
+{
+ /* n / d + 1 / 2 = (2n + d) / 2d */
+ return (2 * num + denom) / (2 * denom);
+}
+
+/* clock value structure. */
+struct sm501_clock {
+ unsigned long mclk;
+ int divider;
+ int shift;
+};
+
+/* sm501_select_clock
+ *
+ * selects nearest discrete clock frequency the SM501 can achive
+ * the maximum divisor is 3 or 5
+ */
+static unsigned long sm501_select_clock(unsigned long freq,
+ struct sm501_clock *clock,
+ int max_div)
+{
+ unsigned long mclk;
+ int divider;
+ int shift;
+ long diff;
+ long best_diff = 999999999;
+
+ /* Try 288MHz and 336MHz clocks. */
+ for (mclk = 288000000; mclk <= 336000000; mclk += 48000000) {
+ /* try dividers 1 and 3 for CRT and for panel,
+ try divider 5 for panel only.*/
+
+ for (divider = 1; divider <= max_div; divider += 2) {
+ /* try all 8 shift values.*/
+ for (shift = 0; shift < 8; shift++) {
+ /* Calculate difference to requested clock */
+ diff = sm501fb_round_div(mclk, divider << shift) - freq;
+ if (diff < 0)
+ diff = -diff;
+
+ /* If it is less than the current, use it */
+ if (diff < best_diff) {
+ best_diff = diff;
+
+ clock->mclk = mclk;
+ clock->divider = divider;
+ clock->shift = shift;
+ }
+ }
+ }
+ }
+
+ /* Return best clock. */
+ return clock->mclk / (clock->divider << clock->shift);
+}
+
+/* sm501_set_clock
+ *
+ * set one of the four clock sources to the closest available frequency to
+ * the one specified
+*/
+
+unsigned long sm501_set_clock(struct sm501_devdata *sm,
+ int clksrc,
+ unsigned long req_freq)
+{
+ unsigned long mode = readl(sm->regs + SM501_POWER_MODE_CONTROL);
+ unsigned long gate = readl(sm->regs + SM501_CURRENT_GATE);
+ unsigned long clock = readl(sm->regs + SM501_CURRENT_CLOCK);
+ unsigned char reg;
+ unsigned long sm501_freq; /* the actual frequency acheived */
+
+ struct sm501_clock to;
+
+ /* find achivable discrete frequency and setup register value
+ * accordingly, V2XCLK, MCLK and M1XCLK are the same P2XCLK
+ * has an extra bit for the divider */
+
+ switch (clksrc) {
+ case SM501_CLOCK_P2XCLK:
+ /* This clock is divided in half so to achive the
+ * requested frequency the value must be multiplied by
+ * 2. This clock also has an additional pre divisor */
+
+ sm501_freq = (sm501_select_clock(2 * req_freq, &to, 5) / 2);
+ reg=to.shift & 0x07;/* bottom 3 bits are shift */
+ if (to.divider == 3)
+ reg |= 0x08; /* /3 divider required */
+ else if (to.divider == 5)
+ reg |= 0x10; /* /5 divider required */
+ if (to.mclk != 288000000)
+ reg |= 0x20; /* which mclk pll is source */
+ break;
+
+ case SM501_CLOCK_V2XCLK:
+ /* This clock is divided in half so to achive the
+ * requested frequency the value must be multiplied by 2. */
+
+ sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2);
+ reg=to.shift & 0x07; /* bottom 3 bits are shift */
+ if (to.divider == 3)
+ reg |= 0x08; /* /3 divider required */
+ if (to.mclk != 288000000)
+ reg |= 0x10; /* which mclk pll is source */
+ break;
+
+ case SM501_CLOCK_MCLK:
+ case SM501_CLOCK_M1XCLK:
+ /* These clocks are the same and not further divided */
+
+ sm501_freq = sm501_select_clock( req_freq, &to, 3);
+ reg=to.shift & 0x07; /* bottom 3 bits are shift */
+ if (to.divider == 3)
+ reg |= 0x08; /* /3 divider required */
+ if (to.mclk != 288000000)
+ reg |= 0x10; /* which mclk pll is source */
+ break;
+
+ default:
+ return 0; /* this is bad */
+ }
+
+ mode = readl(sm->regs + SM501_POWER_MODE_CONTROL);
+ gate = readl(sm->regs + SM501_CURRENT_GATE);
+ clock = readl(sm->regs + SM501_CURRENT_CLOCK);
+
+ clock = clock & ~(0xFF << clksrc);
+ clock |= reg<<clksrc;
+
+ mode &= 3; /* find current mode */
+
+ switch (mode) {
+ case 1:
+ writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
+ writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
+ mode = 0;
+ break;
+ case 2:
+ case 0:
+ writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
+ writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
+ mode = 1;
+ break;
+
+ default:
+ return -1;
+ }
+
+ writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
+ sm501_sync_regs(sm);
+
+ dev_info(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n",
+ gate, clock, mode);
+
+ sm501_mdelay(sm, 16);
+
+ sm501_dump_clk(sm);
+
+ return sm501_freq;
+}
+
+/* sm501_find_clock
+ *
+ * finds the closest available frequency for a given clock
+*/
+
+unsigned long sm501_find_clock(int clksrc,
+ unsigned long req_freq)
+{
+ unsigned long sm501_freq; /* the frequency achiveable by the 501 */
+ struct sm501_clock to;
+
+ switch (clksrc) {
+ case SM501_CLOCK_P2XCLK:
+ sm501_freq = (sm501_select_clock(2 * req_freq, &to, 5) / 2);
+ break;
+
+ case SM501_CLOCK_V2XCLK:
+ sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2);
+ break;
+
+ case SM501_CLOCK_MCLK:
+ case SM501_CLOCK_M1XCLK:
+ sm501_freq = sm501_select_clock(req_freq, &to, 3);
+ break;
+
+ default:
+ sm501_freq = 0; /* error */
+ }
+
+ return sm501_freq;
+}
+
+
+/* sm501_init_reg
+ *
+ * Helper function for the init code to setup a register
+ *
+ * clear the bits which are set in r->mask, and then set
+ * the bits set in r->set.
+*/
+
+static inline void sm501_init_reg(struct sm501_devdata *sm,
+ unsigned long reg,
+ struct sm501_reg_init *r)
+{
+ unsigned long tmp;
+
+ tmp = readl(sm->regs + reg);
+ tmp &= ~r->mask;
+ tmp |= r->set;
+ writel(tmp, sm->regs + reg);
+}
+
+/* sm501_init_regs
+ *
+ * Setup core register values
+*/
+
+static void sm501_init_regs(struct sm501_devdata *sm,
+ struct sm501_initdata *init)
+{
+ sm501_misc_control(sm,
+ init->misc_control.set,
+ init->misc_control.mask);
+
+ sm501_init_reg(sm, SM501_MISC_TIMING, &init->misc_timing);
+ sm501_init_reg(sm, SM501_GPIO31_0_CONTROL, &init->gpio_low);
+ sm501_init_reg(sm, SM501_GPIO63_32_CONTROL, &init->gpio_high);
+
+ if (init->m1xclk) {
+ dev_info(sm->dev, "setting M1XCLK to %ld\n", init->m1xclk);
+ sm501_set_clock(sm, SM501_CLOCK_M1XCLK, init->m1xclk);
+ }
+
+ if (init->mclk) {
+ dev_info(sm->dev, "setting MCLK to %ld\n", init->mclk);
+ sm501_set_clock(sm, SM501_CLOCK_MCLK, init->mclk);
+ }
+
+}
+
+/* Check the PLL sources for the M1CLK and M1XCLK
+ *
+ * If the M1CLK and M1XCLKs are not sourced from the same PLL, then
+ * there is a risk (see errata AB-5) that the SM501 will cease proper
+ * function. If this happens, then it is likely the SM501 will
+ * hang the system.
+*/
+
+static int sm501_check_clocks(struct sm501_devdata *sm)
+{
+ unsigned long pwrmode = readl(sm->regs + SM501_CURRENT_CLOCK);
+ unsigned long msrc = (pwrmode & SM501_POWERMODE_M_SRC);
+ unsigned long m1src = (pwrmode & SM501_POWERMODE_M1_SRC);
+
+ return ((msrc == 0 && m1src != 0) || (msrc != 0 && m1src == 0));
+}
+
+static unsigned int sm501_mem_local[] = {
+ [0] = 4*1024*1024,
+ [1] = 8*1024*1024,
+ [2] = 16*1024*1024,
+ [3] = 32*1024*1024,
+ [4] = 64*1024*1024,
+ [5] = 2*1024*1024,
+};
+
+/* sm501_init_dev
+ *
+ * Common init code for an SM501
+*/
+
+static int sm501_init_dev(struct sm501_devdata *sm)
+{
+ unsigned long mem_avail;
+ unsigned long dramctrl;
+ unsigned long devid;
+ int ret;
+
+ devid = readl(sm->regs + SM501_DEVICEID);
+
+ if ((devid & SM501_DEVICEID_IDMASK) != SM501_DEVICEID_SM501) {
+ dev_err(sm->dev, "incorrect device id %08lx\n", devid);
+ return -1;
+ }
+
+ dramctrl = readl(sm->regs + SM501_DRAM_CONTROL);
+ mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7];
+
+ dev_info(sm->dev, "SM501 At %p: Version %08lx, %ld Mb, IRQ %d\n",
+ sm->regs, devid, (unsigned long)mem_avail >> 20, sm->irq);
+
+ sm501_dump_gate(sm);
+ sm501_dump_clk(sm);
+
+ /* check to see if we have some device initialisation */
+
+ if (sm->platdata) {
+ struct sm501_platdata *pdata = sm->platdata;
+
+ if (pdata->init) {
+ sm501_init_regs(sm, sm->platdata->init);
+ }
+ }
+
+ ret = sm501_check_clocks(sm);
+ if (ret) {
+ dev_err(sm->dev, "M1X and M clocks sourced from different "
+ "PLLs\n");
+ return -1;
+ }
+
+
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+/* Initialisation data for PCI devices */
+
+
+static struct sm501_initdata sm501_pci_initdata = {
+ .gpio_high = {
+ .set = 0x3F000000, /* 24bit panel */
+ .mask = 0x0,
+ },
+ .misc_timing = {
+ .set = 0x010100, /* SDRAM timing */
+ .mask = 0x1F1F00,
+ },
+ .misc_control = {
+ .set = SM501_MISC_PNL_24BIT,
+ .mask = 0,
+ },
+
+ .devices = SM501_USE_ALL,
+
+ /* Errata AB-3 says that 72MHz is the fastest available
+ * for 33MHZ PCI with proper bus-mastering operation */
+
+ .mclk = 72 * MHZ,
+ .m1xclk = 144 * MHZ,
+};
+
+static struct sm501_platdata_fbsub sm501_pdata_fbsub = {
+ .flags = (SM501FB_FLAG_USE_INIT_MODE |
+ SM501FB_FLAG_USE_HWCURSOR |
+ SM501FB_FLAG_USE_HWACCEL |
+ SM501FB_FLAG_DISABLE_AT_EXIT),
+};
+
+static struct sm501_platdata_fb sm501_fb_pdata = {
+ .fb_route = SM501_FB_OWN,
+ .fb_crt = &sm501_pdata_fbsub,
+ .fb_pnl = &sm501_pdata_fbsub,
+};
+
+static struct sm501_platdata sm501_pci_platdata = {
+ .init = &sm501_pci_initdata,
+ .fb = &sm501_fb_pdata,
+};
+
+
+
+/* Helper functions */
+
+static inline int h_total(struct ctfb_res_modes *var)
+{
+ return var->xres + var->left_margin +
+ var->right_margin + var->hsync_len;
+}
+
+static inline int v_total(struct ctfb_res_modes *var)
+{
+ return var->yres + var->upper_margin +
+ var->lower_margin + var->vsync_len;
+}
+
+/* sm501fb_sync_regs()
+ *
+ * This call is mainly for PCI bus systems where we need to
+ * ensure that any writes to the bus are completed before the
+ * next phase, or after completing a function.
+*/
+
+static inline void sm501fb_sync_regs(struct sm501_devdata *sm)
+{
+ readl(sm->dc);
+}
+
+/* sm501fb_ps_to_hz
+ *
+ * Converts a period in picoseconds to Hz.
+ *
+ * Note, we try to keep this in Hz to minimise rounding with
+ * the limited PLL settings on the SM501.
+*/
+
+static unsigned long sm501fb_ps_to_hz(unsigned long psvalue)
+{
+ unsigned long numerator=1000000000ULL;
+
+ /* 10^12 / picosecond period gives frequency in Hz */
+ numerator /= psvalue;
+ numerator *= 1000;
+ return (unsigned long)numerator;
+}
+
+/* sm501fb_hz_to_ps is identical to the oposite transform */
+
+#define sm501fb_hz_to_ps(x) sm501fb_ps_to_hz(x)
+
+
+/* sm501fb_set_par_common
+ *
+ * set common registers for framebuffers
+*/
+
+static int sm501fb_set_par_common(struct sm501_devdata *sm, int head)
+{
+ struct ctfb_res_modes *var = sm->mode;
+ unsigned long pixclock; /* pixelclock in Hz */
+ unsigned long sm501pixclock; /* pixelclock the 501 can achive in Hz */
+ /*unsigned int mem_type;*/
+ unsigned int clock_type;
+ unsigned int head_addr;
+
+ dev_dbg(fbi->dev, "%s: %dx%d, bpp = %d, virtual %dx%d\n",
+ __func__, var->xres, var->yres, sm->bits_per_pixel,
+ sm->xres_virtual, sm->yres_virtual);
+
+ switch (head) {
+ case HEAD_CRT:
+ /*mem_type = SM501_MEMF_CRT;*/
+ clock_type = SM501_CLOCK_V2XCLK;
+ head_addr = SM501_DC_CRT_FB_ADDR;
+ break;
+
+ case HEAD_PANEL:
+ /*mem_type = SM501_MEMF_PANEL;*/
+ clock_type = SM501_CLOCK_P2XCLK;
+ head_addr = SM501_DC_PANEL_FB_ADDR;
+ break;
+
+ default:
+ /*mem_type = 0;*/ /* stop compiler warnings */
+ head_addr = 0;
+ clock_type = 0;
+ }
+
+#if 0
+ switch (sm->bits_per_pixel) {
+ case 8:
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ break;
+
+ case 16:
+ info->fix.visual = FB_VISUAL_DIRECTCOLOR;
+ break;
+
+ case 32:
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ break;
+ }
+
+ /* allocate fb memory within 501 */
+ info->fix.line_length = (var->xres_virtual * var->bits_per_pixel)/8;
+ info->fix.smem_len = info->fix.line_length * var->yres_virtual;
+
+ dev_dbg(fbi->dev, "%s: line length = %u\n", __func__,
+ info->fix.line_length);
+
+ if (sm501_alloc_mem(fbi, &par->screen, mem_type,
+ info->fix.smem_len)) {
+ dev_err(fbi->dev, "no memory available\n");
+ return -ENOMEM;
+ }
+
+ info->fix.smem_start = fbi->fbmem_res->start + par->screen.sm_addr;
+
+ info->screen_base = fbi->fbmem + par->screen.sm_addr;
+ info->screen_size = info->fix.smem_len;
+#endif
+
+ /* set start of framebuffer to the screen = start of video mem */
+
+ writel(SM501_FRAMEBUFFER_ADDR | SM501_ADDR_FLIP, sm->dc + head_addr);
+
+ /* program CRT clock */
+
+ pixclock = sm501fb_ps_to_hz(var->pixclock);
+
+ sm501pixclock = sm501_set_clock(sm, clock_type,
+ pixclock);
+
+ /* update fb layer with actual clock used */
+ var->pixclock = sm501fb_hz_to_ps(sm501pixclock);
+
+ dev_dbg(fbi->dev, "%s: pixclock(ps) = %u, pixclock(Hz) = %lu, "
+ "sm501pixclock = %lu, error = %ld%%\n",
+ __func__, var->pixclock, pixclock, sm501pixclock,
+ ((pixclock - sm501pixclock)*100)/pixclock);
+
+ return 0;
+}
+
+/* sm501fb_set_par_geometry
+ *
+ * set the geometry registers for specified framebuffer.
+*/
+
+static void sm501fb_set_par_geometry(struct sm501_devdata *sm, int head)
+{
+ struct ctfb_res_modes *var = sm->mode;
+ void *base = sm->dc;
+ unsigned long reg;
+
+ if (head == HEAD_CRT)
+ base += SM501_DC_CRT_H_TOT;
+ else
+ base += SM501_DC_PANEL_H_TOT;
+
+ /* set framebuffer width and display width */
+
+ reg = (sm->xres_virtual * sm->bits_per_pixel)/8;
+ reg |= ((var->xres * sm->bits_per_pixel)/8) << 16;
+
+ writel(reg, sm->dc + (head == HEAD_CRT ?
+ SM501_DC_CRT_FB_OFFSET : SM501_DC_PANEL_FB_OFFSET));
+
+ /* program horizontal total */
+
+ reg = (h_total(var) - 1) << 16;
+ reg |= (var->xres - 1);
+
+ writel(reg, base + SM501_OFF_DC_H_TOT);
+
+ /* program horizontal sync */
+
+ reg = var->hsync_len << 16;
+ reg |= var->xres + var->right_margin - 1;
+
+ writel(reg, base + SM501_OFF_DC_H_SYNC);
+
+ /* program vertical total */
+
+ reg = (v_total(var) - 1) << 16;
+ reg |= (var->yres - 1);
+
+ writel(reg, base + SM501_OFF_DC_V_TOT);
+
+ /* program vertical sync */
+ reg = var->vsync_len << 16;
+ reg |= var->yres + var->lower_margin - 1;
+
+ writel(reg, base + SM501_OFF_DC_V_SYNC);
+}
+
+/* sm501fb_pan_crt
+ *
+ * pan the CRT display output within an virtual framebuffer
+*/
+#if 0
+static int sm501fb_pan_crt(struct sm501_devdata *sm)
+{
+ struct ctfb_res_modes *var = sm->mode;
+ unsigned int bytes_pixel = sm->bits_per_pixel / 8;
+ unsigned long reg;
+ unsigned long xoffs;
+
+ xoffs = /*var->xoffset*/0 * bytes_pixel;
+
+ reg = readl(sm->dc + SM501_DC_CRT_CONTROL);
+
+ reg &= ~SM501_DC_CRT_CONTROL_PIXEL_MASK;
+ reg |= ((xoffs & 15) / bytes_pixel) << 4;
+ writel(reg, sm->dc + SM501_DC_CRT_CONTROL);
+
+ reg = (par->screen.sm_addr + xoffs +
+ /*var->yoffset*/0 * info->fix.line_length);
+ writel(reg | SM501_ADDR_FLIP, fbi->dc + SM501_DC_CRT_FB_ADDR);
+
+ sm501fb_sync_regs(sm);
+ return 0;
+}
+#endif
+
+/* sm501fb_pan_pnl
+ *
+ * pan the panel display output within an virtual framebuffer
+*/
+static int sm501fb_pan_pnl(struct sm501_devdata *sm)
+{
+ /*struct ctfb_res_modes *var = sm->mode;*/
+ unsigned long reg;
+
+ reg = /*var->xoffset*/0 | (sm->xres_virtual << 16);
+ writel(reg, sm->dc + SM501_DC_PANEL_FB_WIDTH);
+
+ reg = /*var->yoffset*/0 | (sm->yres_virtual << 16);
+ writel(reg, sm->dc + SM501_DC_PANEL_FB_HEIGHT);
+
+ sm501fb_sync_regs(sm);
+ return 0;
+}
+
+/* sm501fb_set_par_crt
+ *
+ * Set the CRT video mode from the fb_info structure
+*/
+
+static int sm501fb_set_par_crt(struct sm501_devdata *sm)
+{
+ struct ctfb_res_modes *var = sm->mode;
+ unsigned long control; /* control register */
+ int ret;
+
+ /* activate new configuration */
+
+ dev_dbg(fbi->dev, "%s(%p)\n", __func__, sm);
+
+ /* enable CRT DAC - note 0 is on!*/
+ sm501_misc_control(sm, 0, SM501_MISC_DAC_POWER);
+
+ control = readl(sm->dc + SM501_DC_CRT_CONTROL);
+ dev_dbg(fbi->dev, "old control is %08lx\n", control);
+
+ control &= (SM501_DC_CRT_CONTROL_PIXEL_MASK |
+ SM501_DC_CRT_CONTROL_GAMMA |
+ SM501_DC_CRT_CONTROL_BLANK |
+ SM501_DC_CRT_CONTROL_SEL |
+ SM501_DC_CRT_CONTROL_CP |
+ SM501_DC_CRT_CONTROL_TVP);
+
+ /* set the sync polarities before we check data source */
+
+ if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0)
+ control |= SM501_DC_CRT_CONTROL_HSP;
+
+ if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
+ control |= SM501_DC_CRT_CONTROL_VSP;
+
+ if ((control & SM501_DC_CRT_CONTROL_SEL) == 0) {
+ /* the head is displaying panel data... */
+
+ dev_dbg(fbi->dev, "%s CRT display panel data\n", __func__);
+ /*sm501_alloc_mem(fbi, &par->screen, SM501_MEMF_CRT, 0);*/
+ goto out_update;
+ }
+ dev_dbg(fbi->dev, "%s CRT not display panel data\n", __func__);
+
+ ret = sm501fb_set_par_common(sm, HEAD_CRT);
+ if (ret) {
+ dev_err(fbi->dev, "failed to set common parameters\n");
+ return ret;
+ }
+
+ /*sm501fb_pan_crt(var, info);*/
+ sm501fb_set_par_geometry(sm, HEAD_CRT);
+
+ control |= SM501_FIFO_3; /* fill if >3 free slots */
+
+ switch(sm->bits_per_pixel) {
+ case 8:
+ control |= SM501_DC_CRT_CONTROL_8BPP;
+ break;
+
+ case 16:
+ control |= SM501_DC_CRT_CONTROL_16BPP;
+ break;
+
+ case 32:
+ control |= SM501_DC_CRT_CONTROL_32BPP;
+ /*sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE);*/
+ break;
+
+ default:
+ dev_dbg(fbi->dev, "unkown pixel format\n");
+ }
+
+ control |= SM501_DC_CRT_CONTROL_SEL; /* CRT displays CRT data */
+ control |= SM501_DC_CRT_CONTROL_TE; /* enable CRT timing */
+ control |= SM501_DC_CRT_CONTROL_ENABLE; /* enable CRT plane */
+
+ out_update:
+ dev_dbg(fbi->dev, "new control is %08lx\n", control);
+
+ writel(control, sm->dc + SM501_DC_CRT_CONTROL);
+ sm501fb_sync_regs(sm);
+
+ return 0;
+}
+
+static void sm501fb_panel_power(struct sm501_devdata *sm, int to)
+{
+ unsigned long control;
+ void *ctrl_reg = sm->dc + SM501_DC_PANEL_CONTROL;
+
+ control = readl(ctrl_reg);
+
+ if (to && (control & SM501_DC_PANEL_CONTROL_VDD) == 0) {
+ /* enable panel power */
+
+ control |= SM501_DC_PANEL_CONTROL_VDD; /* FPVDDEN */
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+
+ control |= SM501_DC_PANEL_CONTROL_DATA; /* DATA */
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+
+ control |= SM501_DC_PANEL_CONTROL_BIAS; /* VBIASEN */
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+
+ control |= SM501_DC_PANEL_CONTROL_FPEN;
+ writel(control, ctrl_reg);
+
+ } else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) {
+ /* disable panel power */
+
+ control &= ~SM501_DC_PANEL_CONTROL_FPEN;
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+
+ control &= ~SM501_DC_PANEL_CONTROL_BIAS;
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+
+ control &= ~SM501_DC_PANEL_CONTROL_DATA;
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+
+ control &= ~SM501_DC_PANEL_CONTROL_VDD;
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+ }
+
+ sm501fb_sync_regs(sm);
+}
+
+
+/* sm501fb_set_par_pnl
+ *
+ * Set the panel video mode from the fb_info structure
+*/
+
+static int sm501fb_set_par_pnl(struct sm501_devdata *sm)
+{
+ struct ctfb_res_modes *var = sm->mode;
+ unsigned long control;
+ unsigned long reg;
+ int ret;
+
+ dev_dbg(fbi->dev, "%s(%p)\n", __func__, sm);
+
+ /* activate this new configuration */
+
+ ret = sm501fb_set_par_common(sm, HEAD_PANEL);
+ if (ret)
+ return ret;
+
+ sm501fb_pan_pnl(sm);
+ sm501fb_set_par_geometry(sm, HEAD_PANEL);
+
+ /* update control register */
+
+ control = readl(sm->dc + SM501_DC_PANEL_CONTROL);
+ control &= (SM501_DC_PANEL_CONTROL_GAMMA |
+ SM501_DC_PANEL_CONTROL_VDD |
+ SM501_DC_PANEL_CONTROL_DATA |
+ SM501_DC_PANEL_CONTROL_BIAS |
+ SM501_DC_PANEL_CONTROL_FPEN |
+ SM501_DC_PANEL_CONTROL_CP |
+ SM501_DC_PANEL_CONTROL_CK |
+ SM501_DC_PANEL_CONTROL_HP |
+ SM501_DC_PANEL_CONTROL_VP |
+ SM501_DC_PANEL_CONTROL_HPD |
+ SM501_DC_PANEL_CONTROL_VPD);
+
+ control |= SM501_FIFO_3; /* fill if >3 free slots */
+
+ switch(sm->bits_per_pixel) {
+ case 8:
+ control |= SM501_DC_PANEL_CONTROL_8BPP;
+ break;
+
+ case 16:
+ control |= SM501_DC_PANEL_CONTROL_16BPP;
+ break;
+
+ case 32:
+ control |= SM501_DC_PANEL_CONTROL_32BPP;
+ /*sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE);*/
+ break;
+
+ default:
+ dev_dbg(fbi->dev, "unkown pixel format\n");
+ }
+
+ writel(0x0, sm->dc + SM501_DC_PANEL_PANNING_CONTROL);
+
+ /* panel plane top left and bottom right location */
+
+ writel(0x00, sm->dc + SM501_DC_PANEL_TL_LOC);
+
+ reg = var->xres - 1;
+ reg |= (var->yres - 1) << 16;
+
+ writel(reg, sm->dc + SM501_DC_PANEL_BR_LOC);
+
+ /* program panel control register */
+
+ control |= SM501_DC_PANEL_CONTROL_TE; /* enable PANEL timing */
+ control |= SM501_DC_PANEL_CONTROL_EN; /* enable PANEL gfx plane */
+
+ if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0)
+ control |= SM501_DC_PANEL_CONTROL_HSP;
+
+ if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
+ control |= SM501_DC_PANEL_CONTROL_VSP;
+
+ writel(control, sm->dc + SM501_DC_PANEL_CONTROL);
+ sm501fb_sync_regs(sm);
+
+ /* power the panel up */
+ sm501fb_panel_power(sm, 1);
+ return 0;
+}
+
+void video_set_lut(
+ unsigned int index, /* color number */
+ unsigned char r, /* red */
+ unsigned char g, /* green */
+ unsigned char b /* blue */
+ )
+{
+ /* FIXME: to be done */
+}
+
+/*******************************************************************************
+ *
+ * Init video chip with common Linux graphic modes (lilo)
+ */
+void *video_hw_init(void)
+{
+ GraphicDevice *pGD = (GraphicDevice *)&smi;
+ unsigned short device_id;
+ pci_dev_t devbusfn;
+ int videomode;
+ unsigned long t1, hsynch, vsynch;
+ unsigned int pci_mem_base, *vm;
+ unsigned int pci_reg_base;
+ char *penv;
+ int tmp, i, bits_per_pixel;
+ int vmem_size;
+ struct ctfb_res_modes *res_mode;
+ struct ctfb_res_modes var_mode;
+
+ /* Search for video chip */
+ printf("Video: ");
+
+ if ((devbusfn = pci_find_devices(supported, 0)) < 0)
+ {
+ printf ("Controller not found !\n");
+ return (NULL);
+ }
+
+ /* PCI setup */
+ pci_write_config_dword (devbusfn, PCI_COMMAND, (PCI_COMMAND_MEMORY | PCI_COMMAND_IO));
+ pci_read_config_word (devbusfn, PCI_DEVICE_ID, &device_id);
+ pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, &pci_mem_base);
+ /*pci_mem_base = pci_mem_to_phys (devbusfn, pci_mem_base);*/
+ pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_1, &pci_reg_base);
+ /*pci_reg_base = pci_mem_to_phys (devbusfn, pci_reg_base);*/
+
+ dev_dbg(xxx, "mem_base=0x%x reg_base=0x%x\n", pci_mem_base, pci_reg_base);
+
+ tmp = 0;
+
+ videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE;
+ /* get video mode via environment */
+ if ((penv = getenv ("videomode")) != NULL) {
+ /* deceide if it is a string */
+ if (penv[0] <= '9') {
+ videomode = (int) simple_strtoul (penv, NULL, 16);
+ tmp = 1;
+ }
+ } else {
+ tmp = 1;
+ }
+ if (tmp) {
+ /* parameter are vesa modes */
+ /* search params */
+ for (i = 0; i < VESA_MODES_COUNT; i++) {
+ if (vesa_modes[i].vesanr == videomode)
+ break;
+ }
+ if (i == VESA_MODES_COUNT) {
+ printf ("no VESA Mode found, switching to mode 0x%x ", CONFIG_SYS_DEFAULT_VIDEO_MODE);
+ i = 0;
+ }
+ res_mode =
+ (struct ctfb_res_modes *) &res_mode_init[vesa_modes[i].
+ resindex];
+ bits_per_pixel = vesa_modes[i].bits_per_pixel;
+ } else {
+
+ res_mode = (struct ctfb_res_modes *) &var_mode;
+ bits_per_pixel = video_get_params (res_mode, penv);
+ }
+
+ /* calculate hsynch and vsynch freq (info only) */
+ t1 = (res_mode->left_margin + res_mode->xres +
+ res_mode->right_margin + res_mode->hsync_len) / 8;
+ t1 *= 8;
+ t1 *= res_mode->pixclock;
+ t1 /= 1000;
+ hsynch = 1000000000L / t1;
+ t1 *=
+ (res_mode->upper_margin + res_mode->yres +
+ res_mode->lower_margin + res_mode->vsync_len);
+ t1 /= 1000;
+ vsynch = 1000000000L / t1;
+
+ /* fill in Graphic device struct */
+ sprintf (pGD->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres,
+ res_mode->yres, bits_per_pixel, (hsynch / 1000),
+ (vsynch / 1000));
+ printf ("%s\n", pGD->modeIdent);
+ pGD->winSizeX = res_mode->xres;
+ pGD->winSizeY = res_mode->yres;
+ pGD->plnSizeX = res_mode->xres;
+ pGD->plnSizeY = res_mode->yres;
+ switch (bits_per_pixel) {
+ case 8:
+ pGD->gdfBytesPP = 1;
+ pGD->gdfIndex = GDF__8BIT_INDEX;
+ break;
+ case 15:
+ pGD->gdfBytesPP = 2;
+ pGD->gdfIndex = GDF_15BIT_555RGB;
+ break;
+ case 16:
+ pGD->gdfBytesPP = 2;
+ pGD->gdfIndex = GDF_16BIT_565RGB;
+ break;
+ case 24:
+ pGD->gdfBytesPP = 4;
+ pGD->gdfIndex = GDF_32BIT_X888RGB;
+ break;
+ case 32:
+ pGD->gdfBytesPP = 4;
+ pGD->gdfIndex = GDF_32BIT_X888RGB;
+ break;
+ }
+
+ pGD->isaBase = 0;
+ pGD->pciBase = pci_mem_base;
+ pGD->dprBase = pci_reg_base;
+ pGD->vprBase = 0;
+ pGD->cprBase = 0;
+ pGD->frameAdrs = pci_mem_base;
+ pGD->memSize = VIDEO_MEM_SIZE;
+
+ smi_devd.platdata = &sm501_pci_platdata;
+ smi_devd.regs = (void *)pci_reg_base;
+ smi_devd.dc = (void *)pci_reg_base + 0x80000;
+ smi_devd.vmem = (void *)pci_mem_base;
+ smi_devd.gd = pGD;
+ smi_devd.mode = res_mode;
+ switch (bits_per_pixel) {
+ case 8: smi_devd.bits_per_pixel = 8; break;
+ case 16: smi_devd.bits_per_pixel = 16; break;
+ case 24: smi_devd.bits_per_pixel = 32; break;
+ default: smi_devd.bits_per_pixel = 32; break;
+ }
+ smi_devd.xres_virtual = pGD->plnSizeX;
+ smi_devd.yres_virtual = pGD->plnSizeY;
+
+ switch( (readl(smi_devd.regs + SM501_DRAM_CONTROL) >> 13) & 0x7 ){
+ case 0: vmem_size = 4*1024*1024; break;
+ case 1: vmem_size = 8*1024*1024; break;
+ case 2: vmem_size = 16*1024*1024; break;
+ case 3: vmem_size = 32*1024*1024; break;
+ case 4: vmem_size = 64*1024*1024; break;
+ case 5: vmem_size = 2*1024*1024; break;
+ default: vmem_size = 2*1024*1024; break;
+ }
+ printf("SM501: %d MB Video memory\n", vmem_size/(1024*1024));
+
+ /* Blank video memory */
+ vm = (unsigned int *)pGD->frameAdrs;
+ for(i=0; i < vmem_size/sizeof(int); i++){
+ *vm++ = 0;
+ }
+
+ sm501_init_dev(&smi_devd);
+
+ /* enable display controller */
+ sm501_unit_power(&smi_devd, SM501_GATE_DISPLAY, 1);
+
+#if 0
+ /* setup cursors */
+
+ sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR);
+ sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR);
+#endif
+
+ /*
+ * Panel setup
+ */
+ sm501fb_set_par_pnl(&smi_devd);
+
+ /*
+ * CRT setup (we want CRT display panel data)
+ */
+ sm501fb_set_par_crt(&smi_devd);
+
+ return ((void*)&smi);
+}
+
+
+#if 0
+int t_smi (int argc, char *argv[])
+{
+ int i;
+ ulong ret;
+ ulong t,t1;
+
+ app_startup(argv);
+
+ pci_init();
+ drv_video_init();
+
+#if 0
+ video_puts("loading ...\n");
+ video_puts("be patient\n");
+#endif
+
+#if 0
+ if (argc == 1) {
+ /* Print the ABI version */
+
+ printf("Video hw Init\n");
+ pci_init();
+ video_hw_init();
+
+ return 0;
+ } else {
+ printf("Video Init\n");
+ pci_init();
+ drv_video_init();
+ }
+#endif
+}
+#endif
+
+#endif
^ permalink raw reply [flat|nested] 2+ messages in thread
* [U-Boot] [PATCH] video: Add new driver for Silicon Motion SM501/SM502 Part 2/2
2008-12-04 21:10 [U-Boot] [PATCH] video: Add new driver for Silicon Motion SM501/SM502 Part 2/2 Stefan Althoefer
@ 2008-12-07 0:33 ` Anatolij Gustschin
0 siblings, 0 replies; 2+ messages in thread
From: Anatolij Gustschin @ 2008-12-07 0:33 UTC (permalink / raw)
To: u-boot
Hello Stefan,
Stefan Althoefer wrote:
> [PATCH] video: Add new driver for Silicon Motion SM501/SM502
>
> This patch adds a new driver for SM501/SM502. Compared to the
> existing driver it allows dynamic selection of resolution
> (environment: videomode).
>
> The drive is based on Vincent Sanders and Ben Dooks' linux
> kernel driver.
>
> Use CONFIG_VIDEO_SM501NEW to enable the driver.
>
> This has been tested on Janz emPC-A400. On this platform
> the SM501 is connected via PCI.
>
>
> The patch is against "latest" u-boot git-repository
>
> Please (still) be patient if style of submission or patches are
> offending.
>
> Signed-off-by: Stefan Althoefer <stefan.althoefer@web.de>
> ----
see patch header comments in previous email, same applies here.
<snip>
> diff -uprN u-boot-orig//drivers/video/sm501new.c u-boot/drivers/video/sm501new.c
> --- u-boot-orig//drivers/video/sm501new.c 1970-01-01 01:00:00.000000000 +0100
> +++ u-boot/drivers/video/sm501new.c 2008-12-03 11:47:22.000000000 +0100
> @@ -0,0 +1,1533 @@
> +/* Large parts of this have been taken from the linux kernel source code
> + * with the following licencse:
> + *
> + * Copyright (c) 2006 Simtec Electronics
> + * Vincent Sanders <vince@simtec.co.uk>
> + * Ben Dooks <ben@simtec.co.uk>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Framebuffer driver for the Silicon Motion SM501
> + */
> +
> +/* Ported to u-boot:
> + *
> + * Copyright (c) 2008 StefanAlthoefer (as at janz.de)
> + */
> +
> +
> +#include <common.h>
> +#include <exports.h>
is exports.h needed here?
> +#include <config.h>
> +#include <watchdog.h>
is watchdog.h really needed?
> +#include <command.h>
> +#include <image.h>
image.h ?
> +#include <asm/byteorder.h>
> +#include <pci.h>
> +#include <video_fb.h>
> +#include <video.h>
> +#include "videomodes.h"
> +#include "sm501new-regs.h"
> +#include "sm501new.h"
> +
> +
> +#ifdef CONFIG_VIDEO_SM501NEW
please, remove above #ifdef and corresponding #endif. Conditional
compiling is already handled by Makefile.
> +#undef DEBUG
> +
> +/* this should be in pci_ids.h */
> +#define PCI_DEVICE_ID_SMI_501 0x0501
please, remove above comment and move PCI_DEVICE_ID_SMI_501
definition to include/pci_ids.h
> +#define BIG_ENDIAN_HOST
> +#define VIDEO_MEM_SIZE (4*1024*1024)
> +
> +#define SM501_FRAMEBUFFER_ADDR 0
> +
> +#define HEAD_CRT 0
> +#define HEAD_PANEL 1
> +
> +#if defined(BIG_ENDIAN_HOST)
> +static inline unsigned int LONGSWAP(unsigned int x)
> +{
> + return (
> + ((x<<24) & 0xff000000) |
> + ((x<< 8) & 0x00ff0000) |
> + ((x>> 8) & 0x0000ff00) |
> + ((x>>24) & 0x000000ff) );
> +}
> +static inline unsigned int readl(void *addr)
> +{
> + return LONGSWAP((*(volatile unsigned int *)(addr)));
> +}
> +static inline void writel(unsigned int data, void *addr)
> +{
> + /*printf("%p <- %x\n", addr, data); */
> + *(volatile unsigned int *)(addr) = LONGSWAP(data);
> +}
> +#else
> +#define readl(addr) (*(volatile unsigned int*)(addr))
> +#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b))
> +#endif
please, use existing code for readl, writel and byte swapping.
<snip>
> +struct sm501_devdata {
> + struct sm501_platdata *platdata;
> + unsigned long pm_misc;
> + int unit_power[20];
> + unsigned int pdev_id;
> + unsigned int irq;
> + void *regs;
> + void *dc;
> + void *vmem;
pm_misc, pdev_id and irq are not used in the code, so
remove them.
> + GraphicDevice *gd;
gd is asigned but not dereferenced in the code, another
candidate to remove?
> + struct ctfb_res_modes *mode;
> + int bits_per_pixel;
> + int xres_virtual;
> + int yres_virtual;
> +};
> +
> +struct sm501_devdata smi_devd;
> +
> +
> +static void mdelay(unsigned int delay)
> +{
> + while( delay-- > 0 ){
> + udelay(1000);
> + }
> +}
> +
> +#define MHZ (1000 * 1000)
> +
> +
> +#ifdef DEBUG
> +#define dev_dbg(XXX,...) printf(__VA_ARGS__)
> +#define dev_info(XXX,...) printf(__VA_ARGS__)
> +#define dev_err(XXX,...) printf(__VA_ARGS__)
please, use existing debug() macro for debugging purposes,
it's in include/common.h.
Debug is debug, info is info, error is error. It is a good idea
to report errors unconditionally, I mean independent of DEBUG
definition. Errors should always be reported, so I suggest using
printf() for error messages. Many dev_info() calls in this driver
should be changed to debug() calls, see appropriate comments below.
For some short info messages (chip version info, video memory size)
printf() is enough, I think. Also, first argument isn't used, so
simply drop it.
<snip>
> +static void sm501_dump_regs(struct sm501_devdata *sm)
> +{
sm501_dump_regs() is defined but not used. Please, use it or drop it
entirely.
> + void *regs = sm->regs;
> +
> + dev_info(sm->dev, "System Control %08x\n",
> + readl(regs + SM501_SYSTEM_CONTROL));
Actually, this is debug output, so please use debug() macro.
This comment applies if sm501_dump_regs() is going to be used.
<snip>
> +static void sm501_dump_gate(struct sm501_devdata *sm)
> +{
> + dev_info(sm->dev, "CurrentGate %08x\n",
> + readl(sm->regs + SM501_CURRENT_GATE));
> + dev_info(sm->dev, "CurrentClock %08x\n",
> + readl(sm->regs + SM501_CURRENT_CLOCK));
> + dev_info(sm->dev, "PowerModeControl %08x\n",
> + readl(sm->regs + SM501_POWER_MODE_CONTROL));
please, use debug() instead of dev_info() here.
<snip>
> +static inline void sm501_mdelay(struct sm501_devdata *sm, unsigned int delay)
> +{
> + mdelay(delay);
> +}
please, drop sm501_mdelay() and simply use mdelay().
<snip>
> +unsigned long sm501_gpio_get(struct sm501_devdata *sm,
> + unsigned long gpio)
> +{
unused code, please remove it.
<snip>
> +void sm501_gpio_set(struct sm501_devdata *sm,
> + unsigned long gpio,
> + unsigned int to,
> + unsigned int dir)
> +{
ditto.
<snip>
> +/* sm501_init_dev
> + *
> + * Common init code for an SM501
> +*/
> +
> +static int sm501_init_dev(struct sm501_devdata *sm)
> +{
> + unsigned long mem_avail;
> + unsigned long dramctrl;
> + unsigned long devid;
> + int ret;
> +
> + devid = readl(sm->regs + SM501_DEVICEID);
> +
> + if ((devid & SM501_DEVICEID_IDMASK) != SM501_DEVICEID_SM501) {
> + dev_err(sm->dev, "incorrect device id %08lx\n", devid);
printf() for error messages is ok.
> + return -1;
> + }
> +
> + dramctrl = readl(sm->regs + SM501_DRAM_CONTROL);
> + mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7];
> +
> + dev_info(sm->dev, "SM501 At %p: Version %08lx, %ld Mb, IRQ %d\n",
> + sm->regs, devid, (unsigned long)mem_avail >> 20, sm->irq);
IRQ doesn't make sense here, so I suggest to drop it. Also use debug()
instead of dev_info().
> + sm501_dump_gate(sm);
> + sm501_dump_clk(sm);
> +
> + /* check to see if we have some device initialisation */
> +
> + if (sm->platdata) {
> + struct sm501_platdata *pdata = sm->platdata;
> +
> + if (pdata->init) {
> + sm501_init_regs(sm, sm->platdata->init);
> + }
> + }
> +
> + ret = sm501_check_clocks(sm);
> + if (ret) {
> + dev_err(sm->dev, "M1X and M clocks sourced from different "
> + "PLLs\n");
> + return -1;
> + }
> +
> +
> + return 0;
> +}
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
please remove empty lines, max. 2 empty lines allowed.
> +/* Initialisation data for PCI devices */
> +
> +
> +static struct sm501_initdata sm501_pci_initdata = {
> + .gpio_high = {
> + .set = 0x3F000000, /* 24bit panel */
> + .mask = 0x0,
> + },
> + .misc_timing = {
> + .set = 0x010100, /* SDRAM timing */
> + .mask = 0x1F1F00,
> + },
> + .misc_control = {
> + .set = SM501_MISC_PNL_24BIT,
> + .mask = 0,
> + },
> +
> + .devices = SM501_USE_ALL,
"devices" isn't used in the code, a candidate to remove?
> +
> + /* Errata AB-3 says that 72MHz is the fastest available
> + * for 33MHZ PCI with proper bus-mastering operation */
> +
> + .mclk = 72 * MHZ,
> + .m1xclk = 144 * MHZ,
> +};
> +
> +static struct sm501_platdata_fbsub sm501_pdata_fbsub = {
> + .flags = (SM501FB_FLAG_USE_INIT_MODE |
> + SM501FB_FLAG_USE_HWCURSOR |
> + SM501FB_FLAG_USE_HWACCEL |
> + SM501FB_FLAG_DISABLE_AT_EXIT),
> +};
> +
> +static struct sm501_platdata_fb sm501_fb_pdata = {
> + .fb_route = SM501_FB_OWN,
> + .fb_crt = &sm501_pdata_fbsub,
> + .fb_pnl = &sm501_pdata_fbsub,
> +};
> +
> +static struct sm501_platdata sm501_pci_platdata = {
> + .init = &sm501_pci_initdata,
> + .fb = &sm501_fb_pdata,
> +};
sm501_fb_pdata isn't referenced in the code, so please
remove ".fb = &sm501_fb_pdata," in the sm501_pci_platdata
declaration. Also drop sm501_fb_pdata and sm501_pdata_fbsub
declarations above.
> +
> +
> +
max. 2 empty lines.
<snip>
> +static int sm501fb_set_par_common(struct sm501_devdata *sm, int head)
> +{
> + struct ctfb_res_modes *var = sm->mode;
> + unsigned long pixclock; /* pixelclock in Hz */
> + unsigned long sm501pixclock; /* pixelclock the 501 can achive in Hz */
> + /*unsigned int mem_type;*/
> + unsigned int clock_type;
> + unsigned int head_addr;
> +
> + dev_dbg(fbi->dev, "%s: %dx%d, bpp = %d, virtual %dx%d\n",
> + __func__, var->xres, var->yres, sm->bits_per_pixel,
> + sm->xres_virtual, sm->yres_virtual);
> +
> + switch (head) {
> + case HEAD_CRT:
> + /*mem_type = SM501_MEMF_CRT;*/
> + clock_type = SM501_CLOCK_V2XCLK;
> + head_addr = SM501_DC_CRT_FB_ADDR;
> + break;
> +
> + case HEAD_PANEL:
> + /*mem_type = SM501_MEMF_PANEL;*/
> + clock_type = SM501_CLOCK_P2XCLK;
> + head_addr = SM501_DC_PANEL_FB_ADDR;
> + break;
> +
> + default:
> + /*mem_type = 0;*/ /* stop compiler warnings */
> + head_addr = 0;
> + clock_type = 0;
> + }
> +
> +#if 0
> + switch (sm->bits_per_pixel) {
> + case 8:
> + info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
> + break;
> +
> + case 16:
> + info->fix.visual = FB_VISUAL_DIRECTCOLOR;
> + break;
> +
> + case 32:
> + info->fix.visual = FB_VISUAL_TRUECOLOR;
> + break;
> + }
> +
> + /* allocate fb memory within 501 */
> + info->fix.line_length = (var->xres_virtual * var->bits_per_pixel)/8;
> + info->fix.smem_len = info->fix.line_length * var->yres_virtual;
> +
> + dev_dbg(fbi->dev, "%s: line length = %u\n", __func__,
> + info->fix.line_length);
> +
> + if (sm501_alloc_mem(fbi, &par->screen, mem_type,
> + info->fix.smem_len)) {
> + dev_err(fbi->dev, "no memory available\n");
> + return -ENOMEM;
> + }
> +
> + info->fix.smem_start = fbi->fbmem_res->start + par->screen.sm_addr;
> +
> + info->screen_base = fbi->fbmem + par->screen.sm_addr;
> + info->screen_size = info->fix.smem_len;
> +#endif
please remove unused code (between #if 0 and #endif).
<snip>
> +/* sm501fb_pan_crt
> + *
> + * pan the CRT display output within an virtual framebuffer
> +*/
> +#if 0
> +static int sm501fb_pan_crt(struct sm501_devdata *sm)
> +{
> + struct ctfb_res_modes *var = sm->mode;
> + unsigned int bytes_pixel = sm->bits_per_pixel / 8;
> + unsigned long reg;
> + unsigned long xoffs;
> +
> + xoffs = /*var->xoffset*/0 * bytes_pixel;
> +
> + reg = readl(sm->dc + SM501_DC_CRT_CONTROL);
> +
> + reg &= ~SM501_DC_CRT_CONTROL_PIXEL_MASK;
> + reg |= ((xoffs & 15) / bytes_pixel) << 4;
> + writel(reg, sm->dc + SM501_DC_CRT_CONTROL);
> +
> + reg = (par->screen.sm_addr + xoffs +
> + /*var->yoffset*/0 * info->fix.line_length);
> + writel(reg | SM501_ADDR_FLIP, fbi->dc + SM501_DC_CRT_FB_ADDR);
> +
> + sm501fb_sync_regs(sm);
> + return 0;
> +}
> +#endif
unused code, please remove.
> +
> +/* sm501fb_pan_pnl
> + *
> + * pan the panel display output within an virtual framebuffer
> +*/
> +static int sm501fb_pan_pnl(struct sm501_devdata *sm)
> +{
> + /*struct ctfb_res_modes *var = sm->mode;*/
drop above unused code.
> + unsigned long reg;
> +
> + reg = /*var->xoffset*/0 | (sm->xres_virtual << 16);
remove /*var->xoffset*/.
> + writel(reg, sm->dc + SM501_DC_PANEL_FB_WIDTH);
> +
> + reg = /*var->yoffset*/0 | (sm->yres_virtual << 16);
Ditto.
> + writel(reg, sm->dc + SM501_DC_PANEL_FB_HEIGHT);
> +
> + sm501fb_sync_regs(sm);
> + return 0;
> +}
> +
> +/* sm501fb_set_par_crt
> + *
> + * Set the CRT video mode from the fb_info structure
> +*/
> +
> +static int sm501fb_set_par_crt(struct sm501_devdata *sm)
> +{
> + struct ctfb_res_modes *var = sm->mode;
> + unsigned long control; /* control register */
> + int ret;
> +
> + /* activate new configuration */
> +
> + dev_dbg(fbi->dev, "%s(%p)\n", __func__, sm);
> +
> + /* enable CRT DAC - note 0 is on!*/
> + sm501_misc_control(sm, 0, SM501_MISC_DAC_POWER);
> +
> + control = readl(sm->dc + SM501_DC_CRT_CONTROL);
> + dev_dbg(fbi->dev, "old control is %08lx\n", control);
> +
> + control &= (SM501_DC_CRT_CONTROL_PIXEL_MASK |
> + SM501_DC_CRT_CONTROL_GAMMA |
> + SM501_DC_CRT_CONTROL_BLANK |
> + SM501_DC_CRT_CONTROL_SEL |
> + SM501_DC_CRT_CONTROL_CP |
> + SM501_DC_CRT_CONTROL_TVP);
> +
> + /* set the sync polarities before we check data source */
> +
> + if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0)
> + control |= SM501_DC_CRT_CONTROL_HSP;
> +
> + if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
> + control |= SM501_DC_CRT_CONTROL_VSP;
> +
> + if ((control & SM501_DC_CRT_CONTROL_SEL) == 0) {
> + /* the head is displaying panel data... */
> +
> + dev_dbg(fbi->dev, "%s CRT display panel data\n", __func__);
> + /*sm501_alloc_mem(fbi, &par->screen, SM501_MEMF_CRT, 0);*/
drop commented out sm501_alloc_mem code (elsewhere in the file too).
<snip>
> +/* sm501fb_set_par_pnl
> + *
> + * Set the panel video mode from the fb_info structure
> +*/
> +
> +static int sm501fb_set_par_pnl(struct sm501_devdata *sm)
> +{
> + struct ctfb_res_modes *var = sm->mode;
> + unsigned long control;
> + unsigned long reg;
> + int ret;
> +
> + dev_dbg(fbi->dev, "%s(%p)\n", __func__, sm);
> +
> + /* activate this new configuration */
> +
> + ret = sm501fb_set_par_common(sm, HEAD_PANEL);
> + if (ret)
> + return ret;
> +
> + sm501fb_pan_pnl(sm);
> + sm501fb_set_par_geometry(sm, HEAD_PANEL);
> +
> + /* update control register */
> +
> + control = readl(sm->dc + SM501_DC_PANEL_CONTROL);
> + control &= (SM501_DC_PANEL_CONTROL_GAMMA |
> + SM501_DC_PANEL_CONTROL_VDD |
> + SM501_DC_PANEL_CONTROL_DATA |
> + SM501_DC_PANEL_CONTROL_BIAS |
> + SM501_DC_PANEL_CONTROL_FPEN |
> + SM501_DC_PANEL_CONTROL_CP |
> + SM501_DC_PANEL_CONTROL_CK |
> + SM501_DC_PANEL_CONTROL_HP |
> + SM501_DC_PANEL_CONTROL_VP |
> + SM501_DC_PANEL_CONTROL_HPD |
> + SM501_DC_PANEL_CONTROL_VPD);
> +
> + control |= SM501_FIFO_3; /* fill if >3 free slots */
> +
> + switch(sm->bits_per_pixel) {
> + case 8:
> + control |= SM501_DC_PANEL_CONTROL_8BPP;
> + break;
> +
> + case 16:
> + control |= SM501_DC_PANEL_CONTROL_16BPP;
> + break;
> +
> + case 32:
> + control |= SM501_DC_PANEL_CONTROL_32BPP;
> + /*sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE);*/
drop commented out sm501fb_setup_gamma, also elsewhere in the file.
> + break;
> +
> + default:
> + dev_dbg(fbi->dev, "unkown pixel format\n");
> + }
> +
> + writel(0x0, sm->dc + SM501_DC_PANEL_PANNING_CONTROL);
> +
> + /* panel plane top left and bottom right location */
> +
> + writel(0x00, sm->dc + SM501_DC_PANEL_TL_LOC);
> +
> + reg = var->xres - 1;
> + reg |= (var->yres - 1) << 16;
> +
> + writel(reg, sm->dc + SM501_DC_PANEL_BR_LOC);
> +
> + /* program panel control register */
> +
> + control |= SM501_DC_PANEL_CONTROL_TE; /* enable PANEL timing */
> + control |= SM501_DC_PANEL_CONTROL_EN; /* enable PANEL gfx plane */
> +
> + if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0)
> + control |= SM501_DC_PANEL_CONTROL_HSP;
> +
> + if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
> + control |= SM501_DC_PANEL_CONTROL_VSP;
> +
> + writel(control, sm->dc + SM501_DC_PANEL_CONTROL);
> + sm501fb_sync_regs(sm);
> +
> + /* power the panel up */
> + sm501fb_panel_power(sm, 1);
> + return 0;
> +}
> +
> +void video_set_lut(
> + unsigned int index, /* color number */
> + unsigned char r, /* red */
> + unsigned char g, /* green */
> + unsigned char b /* blue */
> + )
> +{
> + /* FIXME: to be done */
> +}
> +
> +/*******************************************************************************
> + *
> + * Init video chip with common Linux graphic modes (lilo)
> + */
> +void *video_hw_init(void)
> +{
> + GraphicDevice *pGD = (GraphicDevice *)&smi;
> + unsigned short device_id;
> + pci_dev_t devbusfn;
> + int videomode;
> + unsigned long t1, hsynch, vsynch;
> + unsigned int pci_mem_base, *vm;
> + unsigned int pci_reg_base;
> + char *penv;
> + int tmp, i, bits_per_pixel;
> + int vmem_size;
> + struct ctfb_res_modes *res_mode;
> + struct ctfb_res_modes var_mode;
> +
> + /* Search for video chip */
> + printf("Video: ");
> +
> + if ((devbusfn = pci_find_devices(supported, 0)) < 0)
> + {
> + printf ("Controller not found !\n");
> + return (NULL);
> + }
> +
> + /* PCI setup */
> + pci_write_config_dword (devbusfn, PCI_COMMAND, (PCI_COMMAND_MEMORY | PCI_COMMAND_IO));
> + pci_read_config_word (devbusfn, PCI_DEVICE_ID, &device_id);
> + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, &pci_mem_base);
> + /*pci_mem_base = pci_mem_to_phys (devbusfn, pci_mem_base);*/
> + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_1, &pci_reg_base);
> + /*pci_reg_base = pci_mem_to_phys (devbusfn, pci_reg_base);*/
> +
> + dev_dbg(xxx, "mem_base=0x%x reg_base=0x%x\n", pci_mem_base, pci_reg_base);
> +
> + tmp = 0;
> +
> + videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE;
> + /* get video mode via environment */
> + if ((penv = getenv ("videomode")) != NULL) {
> + /* deceide if it is a string */
> + if (penv[0] <= '9') {
> + videomode = (int) simple_strtoul (penv, NULL, 16);
> + tmp = 1;
> + }
> + } else {
> + tmp = 1;
> + }
> + if (tmp) {
> + /* parameter are vesa modes */
> + /* search params */
> + for (i = 0; i < VESA_MODES_COUNT; i++) {
> + if (vesa_modes[i].vesanr == videomode)
> + break;
> + }
> + if (i == VESA_MODES_COUNT) {
> + printf ("no VESA Mode found, switching to mode 0x%x ", CONFIG_SYS_DEFAULT_VIDEO_MODE);
above line too long, max. 80 characters please.
> + i = 0;
> + }
> + res_mode =
> + (struct ctfb_res_modes *) &res_mode_init[vesa_modes[i].
> + resindex];
> + bits_per_pixel = vesa_modes[i].bits_per_pixel;
> + } else {
> +
> + res_mode = (struct ctfb_res_modes *) &var_mode;
> + bits_per_pixel = video_get_params (res_mode, penv);
> + }
> +
> + /* calculate hsynch and vsynch freq (info only) */
> + t1 = (res_mode->left_margin + res_mode->xres +
> + res_mode->right_margin + res_mode->hsync_len) / 8;
> + t1 *= 8;
> + t1 *= res_mode->pixclock;
> + t1 /= 1000;
> + hsynch = 1000000000L / t1;
> + t1 *=
> + (res_mode->upper_margin + res_mode->yres +
> + res_mode->lower_margin + res_mode->vsync_len);
> + t1 /= 1000;
> + vsynch = 1000000000L / t1;
> +
> + /* fill in Graphic device struct */
> + sprintf (pGD->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres,
> + res_mode->yres, bits_per_pixel, (hsynch / 1000),
> + (vsynch / 1000));
> + printf ("%s\n", pGD->modeIdent);
> + pGD->winSizeX = res_mode->xres;
> + pGD->winSizeY = res_mode->yres;
> + pGD->plnSizeX = res_mode->xres;
> + pGD->plnSizeY = res_mode->yres;
> + switch (bits_per_pixel) {
> + case 8:
> + pGD->gdfBytesPP = 1;
> + pGD->gdfIndex = GDF__8BIT_INDEX;
> + break;
> + case 15:
> + pGD->gdfBytesPP = 2;
> + pGD->gdfIndex = GDF_15BIT_555RGB;
> + break;
> + case 16:
> + pGD->gdfBytesPP = 2;
> + pGD->gdfIndex = GDF_16BIT_565RGB;
> + break;
> + case 24:
> + pGD->gdfBytesPP = 4;
> + pGD->gdfIndex = GDF_32BIT_X888RGB;
> + break;
> + case 32:
> + pGD->gdfBytesPP = 4;
> + pGD->gdfIndex = GDF_32BIT_X888RGB;
> + break;
> + }
> +
> + pGD->isaBase = 0;
> + pGD->pciBase = pci_mem_base;
> + pGD->dprBase = pci_reg_base;
> + pGD->vprBase = 0;
> + pGD->cprBase = 0;
> + pGD->frameAdrs = pci_mem_base;
> + pGD->memSize = VIDEO_MEM_SIZE;
> +
> + smi_devd.platdata = &sm501_pci_platdata;
> + smi_devd.regs = (void *)pci_reg_base;
> + smi_devd.dc = (void *)pci_reg_base + 0x80000;
> + smi_devd.vmem = (void *)pci_mem_base;
> + smi_devd.gd = pGD;
"smi_devd.gd" isn't referenced elsewhere in the code,
a candidate to remove?
> + smi_devd.mode = res_mode;
> + switch (bits_per_pixel) {
> + case 8: smi_devd.bits_per_pixel = 8; break;
> + case 16: smi_devd.bits_per_pixel = 16; break;
> + case 24: smi_devd.bits_per_pixel = 32; break;
> + default: smi_devd.bits_per_pixel = 32; break;
> + }
> + smi_devd.xres_virtual = pGD->plnSizeX;
> + smi_devd.yres_virtual = pGD->plnSizeY;
> +
> + switch( (readl(smi_devd.regs + SM501_DRAM_CONTROL) >> 13) & 0x7 ){
> + case 0: vmem_size = 4*1024*1024; break;
> + case 1: vmem_size = 8*1024*1024; break;
> + case 2: vmem_size = 16*1024*1024; break;
> + case 3: vmem_size = 32*1024*1024; break;
> + case 4: vmem_size = 64*1024*1024; break;
> + case 5: vmem_size = 2*1024*1024; break;
> + default: vmem_size = 2*1024*1024; break;
> + }
> + printf("SM501: %d MB Video memory\n", vmem_size/(1024*1024));
> +
> + /* Blank video memory */
> + vm = (unsigned int *)pGD->frameAdrs;
> + for(i=0; i < vmem_size/sizeof(int); i++){
> + *vm++ = 0;
> + }
> +
> + sm501_init_dev(&smi_devd);
> +
> + /* enable display controller */
> + sm501_unit_power(&smi_devd, SM501_GATE_DISPLAY, 1);
> +
> +#if 0
> + /* setup cursors */
> +
> + sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR);
> + sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR);
> +#endif
unused code, please remove it.
> +
> + /*
> + * Panel setup
> + */
> + sm501fb_set_par_pnl(&smi_devd);
> +
> + /*
> + * CRT setup (we want CRT display panel data)
> + */
> + sm501fb_set_par_crt(&smi_devd);
> +
> + return ((void*)&smi);
> +}
> +
> +
> +#if 0
> +int t_smi (int argc, char *argv[])
> +{
> + int i;
> + ulong ret;
> + ulong t,t1;
> +
> + app_startup(argv);
> +
> + pci_init();
> + drv_video_init();
> +
> +#if 0
> + video_puts("loading ...\n");
> + video_puts("be patient\n");
> +#endif
> +
> +#if 0
> + if (argc == 1) {
> + /* Print the ABI version */
> +
> + printf("Video hw Init\n");
> + pci_init();
> + video_hw_init();
> +
> + return 0;
> + } else {
> + printf("Video Init\n");
> + pci_init();
> + drv_video_init();
> + }
> +#endif
> +}
> +#endif
unused code, please drop it.
Also check for proper multi-line comment style, please.
Best regards,
Anatolij
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office at denx.de
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2008-12-07 0:33 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-12-04 21:10 [U-Boot] [PATCH] video: Add new driver for Silicon Motion SM501/SM502 Part 2/2 Stefan Althoefer
2008-12-07 0:33 ` Anatolij Gustschin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox