[U-Boot] [PATCH] video: Add new driver for Silicon Motion SM501/SM502 Part 2/2

[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@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

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@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
participants (2)
-
Anatolij Gustschin
-
Stefan Althoefer