From: Bill G. <bg...@bi...> - 2006-10-06 16:11:44
|
Guys: Sorry about starting a new thread, but I was referred to this list/thread by a colleague just a few minutes ago. (OT: it isn't in the archives in SF, either!). I hate to play "me too!", but I also have an SM501 framebuffer driver in a partial state of development. I'd love to collaborate with others to get a good version pushed upstream. One of the challenges I've encountered with this chip is dealing with all the "other stuff" it has onboard. In my driver, I addressed that by creating a whole new bus similar to what Nico did for the UCB1400. I was in the process of getting AC97 working when I figured out that that part of the chip is so braindead, it isn't worth using--- at least on ARM platforms that don't have DMA (which was my case). In an upcoming project I need to use the USBH, so I'll be revisiting this issue again soon. I have also cc'd others who have worked with me on this chip. I'd be happy to post code if that makes sense. Or to host a CVS repo or something. Kindest regards, b.g. -- Bill Gatliff bg...@bi... |
From: Ben D. <ben...@fl...> - 2007-01-15 01:31:43
|
This is a resend of the SM501 patch, with all the previous comments addressed, and some more support for items such as blanking and display panning. A number of code paths are now handled by common blocks where possible to make the code easier to change. Hardware acceleration is not included in this patch, and will be sorted out as soon as possible. Signed-off-by: Ben Dooks <ben...@fl...> diff -urpN -X ../dontdiff linux-2.6.19/drivers/video/Kconfig linux-2.6.19-simtec1p21/drivers/video/Kconfig --- linux-2.6.19/drivers/video/Kconfig 2006-11-29 21:57:37.000000000 +0000 +++ linux-2.6.19-simtec1p21/drivers/video/Kconfig 2007-01-15 01:27:34.000000000 +0000 @@ -1596,6 +1596,24 @@ config FB_S3C2410_DEBUG Turn on debugging messages. Note that you can set/unset at run time through sysfs +config FB_SM501 + tristate "Silicon Motion SM501 framebuffer support" + depends on FB && MFD_SM501 + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Frame buffer driver for the CRT and LCD controllers in the Silicon + Motion SM501. + + This driver is also available as a module ( = code which can be + inserted and removed from the running kernel whenever you want). The + module will be called sm501fb. If you want to compile it as a module, + say M here and read <file:Documentation/modules.txt>. + + If unsure, say N. + + config FB_PNX4008_DUM tristate "Display Update Module support on Philips PNX4008 board" depends on FB && ARCH_PNX4008 diff -urpN -X ../dontdiff linux-2.6.19/drivers/video/Makefile linux-2.6.19-simtec1p21/drivers/video/Makefile --- linux-2.6.19/drivers/video/Makefile 2006-11-29 21:57:37.000000000 +0000 +++ linux-2.6.19-simtec1p21/drivers/video/Makefile 2007-01-15 01:26:59.000000000 +0000 @@ -99,6 +99,7 @@ obj-$(CONFIG_FB_IMX) += imx obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o obj-$(CONFIG_FB_PNX4008_DUM) += pnx4008/ obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/ +obj-$(CONFIG_FB_SM501) += sm501fb.o # Platform or fallback drivers go here obj-$(CONFIG_FB_VESA) += vesafb.o diff -urpN -X ../dontdiff linux-2.6.19/drivers/video/sm501fb.c linux-2.6.19-simtec1p21/drivers/video/sm501fb.c --- linux-2.6.19/drivers/video/sm501fb.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19-simtec1p21/drivers/video/sm501fb.c 2007-01-15 01:22:43.000000000 +0000 @@ -0,0 +1,1783 @@ +/* linux/drivers/video/sm501fb.c + * + * Copyright (c) 2006 Simtec Electronics + * Vincent Sanders <vi...@si...> + * Ben Dooks <be...@si...> + * + * 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 + */ + +//#define DEBUG + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/vmalloc.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/wait.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/div64.h> + +#ifdef CONFIG_PM +#include <linux/pm.h> +#endif + +#include <linux/sm501.h> +#include <linux/sm501-regs.h> + +#define NR_PALETTE 256 + +enum sm501_controller { + HEAD_CRT = 0, + HEAD_PANEL = 1, +}; + +/* SM501 memory adress */ +struct sm501_mem { + unsigned long size; + unsigned long sm_addr; + void __iomem *k_addr; +}; + +/* private data that is shared between all frambuffers* */ +struct sm501fb_info { + struct device *dev; + struct fb_info *fb[2]; /* fb info for both heads */ + struct resource *fbmem_res; /* framebuffer resource */ + struct resource *regs_res; /* registers resource */ + struct sm501_platdata_fb *pdata; /* our platform data */ + + int irq; + void __iomem *regs; /* remapped registers */ + void __iomem *fbmem; /* remapped framebuffer */ + size_t fbmem_len; /* length of remapped region */ +}; + +/* per-framebuffer private data */ +struct sm501fb_par { + u32 pseudo_palette[16]; + + enum sm501_controller head; + struct sm501_mem cursor; + struct sm501_mem screen; + struct fb_ops ops; + + void *store_fb; + void *store_cursor; + void __iomem *cursor_regs; + struct sm501fb_info *info; +}; + +/* Helper functions */ + +static inline int h_total(struct fb_var_screeninfo *var) +{ + return var->xres + var->left_margin + + var->right_margin + var->hsync_len; +} + +static inline int v_total(struct fb_var_screeninfo *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 sm501fb_info *info) +{ + (void)readl(info->regs); +} + +/* sm501_alloc_mem + * + * This is an attempt to lay out memory for the two framebuffers and + * everything else + * + * |fbmem_res->start fbmem_res->end| + * | | + * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K | + * |-> fb[0].fix.smem_len <-| spare |-> fb[1].fix.smem_len <-|-> cursors <-| + * + * The "spare" space is for the 2d engine data + * the fixed is space for the cursors (2x1Kbyte) + * + * we need to allocate memory for the 2D acceleration engine + * command list and the data for the engine to deal with. + * + * - all allocations must be 128bit aligned + * - cursors are 64x64x2 bits (1Kbyte) + * + */ + +#define SM501_MEMF_CURSOR (1) +#define SM501_MEMF_PANEL (2) +#define SM501_MEMF_CRT (4) +#define SM501_MEMF_ACCEL (8) + +int sm501_alloc_mem(struct sm501fb_info *inf, struct sm501_mem *mem, + unsigned int why, size_t size) +{ + unsigned int ptr = 0; + + switch (why) { + case SM501_MEMF_CURSOR: + ptr = inf->fbmem_len - size; + inf->fbmem_len = ptr; + break; + + case SM501_MEMF_PANEL: + ptr = inf->fbmem_len - size; + if (ptr < inf->fb[0]->fix.smem_len) + return -ENOMEM; + + break; + + case SM501_MEMF_CRT: + ptr = 0; + break; + + case SM501_MEMF_ACCEL: + ptr = inf->fb[0]->fix.smem_len; + + if ((ptr + size) > + (inf->fb[1]->fix.smem_start - inf->fbmem_res->start)) + return -ENOMEM; + break; + + default: + return -EINVAL; + } + + mem->size = size; + mem->sm_addr = ptr; + mem->k_addr = inf->fbmem + ptr; + + dev_dbg(inf->dev, "%s: result %08lx, %p - %u, %zd\n", + __func__, mem->sm_addr, mem->k_addr, why, size); + + return 0; +} + +/* 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 long numerator=1000000000000ULL; + + /* 10^12 / picosecond period gives frequency in Hz */ + do_div(numerator, psvalue); + 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_setup_gamma + * + * Programs a linear 1.0 gamma ramp in case the gamma + * correction is enabled without programming anything else. +*/ + +static void sm501fb_setup_gamma(struct sm501fb_info *fbi, + unsigned long palette) +{ + unsigned long value = 0; + int offset; + + /* set gamma values */ + for (offset = 0; offset < 256 * 4; offset += 4) { + writel(value, fbi->regs + palette + offset); + value += 0x010101; /* Advance RGB by 1,1,1.*/ + } +} + +/* sm501fb_check_var + * + * check common variables for both panel and crt +*/ + +static int sm501fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + unsigned long tmp; + + /* check we can fit these values into the registers */ + + if (var->hsync_len > 255 || var->vsync_len > 255) + return -EINVAL; + + if ((var->xres + var->right_margin) >= 4096) + return -EINVAL; + + if ((var->yres + var->lower_margin) > 2048) + return -EINVAL; + + /* hard limits of device */ + + if (h_total(var) > 4096 || v_total(var) > 2048) + return -EINVAL; + + /* check our line length is going to be 128 bit aligned */ + + tmp = (var->xres * var->bits_per_pixel) / 8; + if ((tmp & 15) != 0) + return -EINVAL; + + /* check the virtual size */ + + if (var->xres_virtual > 4096 || var->yres_virtual > 2048) + return -EINVAL; + + /* can cope with 8,16 or 32bpp */ + + if (var->bits_per_pixel <= 8) + var->bits_per_pixel = 8; + else if (var->bits_per_pixel <= 16) + var->bits_per_pixel = 16; + else if (var->bits_per_pixel == 24) + var->bits_per_pixel = 32; + + /* set r/g/b positions and validate bpp */ + switch(var->bits_per_pixel) { + case 8: + var->red.length = var->bits_per_pixel; + var->red.offset = 0; + var->green.length = var->bits_per_pixel; + var->green.offset = 0; + var->blue.length = var->bits_per_pixel; + var->blue.offset = 0; + var->transp.length = 0; + + break; + + case 16: + var->red.offset = 11; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + var->transp.length = 0; + + break; + + case 32: + var->transp.offset = 0; + var->transp.length = 0; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * sm501fb_check_var_crt(): + * + * check the parameters for the CRT head, and either bring them + * back into range, or return -EINVAL. +*/ + +static int sm501fb_check_var_crt(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + return sm501fb_check_var(var, info); +} + +/* sm501fb_check_var_pnl(): + * + * check the parameters for the CRT head, and either bring them + * back into range, or return -EINVAL. +*/ + +static int sm501fb_check_var_pnl(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + return sm501fb_check_var(var, info); +} + +/* sm501fb_set_par_common + * + * set common registers for framebuffers +*/ + +static int sm501fb_set_par_common(struct fb_info *info, + struct fb_var_screeninfo *var) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + 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, var->bits_per_pixel, + var->xres_virtual, var->yres_virtual); + + switch (par->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; + } + + switch (var->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; + + /* set start of framebuffer to the screen */ + + writel(par->screen.sm_addr | SM501_ADDR_FLIP, fbi->regs + head_addr); + + /* program CRT clock */ + + pixclock = sm501fb_ps_to_hz(var->pixclock); + + sm501pixclock = sm501_set_clock(fbi->dev->parent, 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 fb_info *info, + struct fb_var_screeninfo *var) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + void __iomem *base = fbi->regs; + unsigned long reg; + + if (par->head == HEAD_CRT) + base += SM501_DC_CRT_H_TOT; + else + base += SM501_DC_PANEL_H_TOT; + + /* set framebuffer width and display width */ + + reg = info->fix.line_length; + reg |= ((var->xres * var->bits_per_pixel)/8) << 16; + + writel(reg, fbi->regs + (par->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_validate_pan + * + * check panning on a var +*/ + +static inline int sm501fb_validate_pan(struct fb_var_screeninfo *var) +{ + if (var->xoffset < 0 || var->yoffset < 0) + return 0; + + if (var->xoffset > (var->xres_virtual - var->xres) || + var->yoffset > (var->yres_virtual - var->yres)) + return 0; + + return 1; +} + +/* sm501fb_pan_crt + * + * pan the CRT display output within an virtual framebuffer +*/ + +static int sm501fb_pan_crt(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + unsigned int bytes_pixel = var->bits_per_pixel / 8; + unsigned long reg; + unsigned long xoffs; + + if (!sm501fb_validate_pan(var)) + return -EINVAL; + + xoffs = var->xoffset * bytes_pixel; + + reg = readl(fbi->regs + SM501_DC_CRT_CONTROL); + + reg &= ~SM501_DC_CRT_CONTROL_PIXEL_MASK; + reg |= ((xoffs & 15) / bytes_pixel) << 4; + writel(reg, fbi->regs + SM501_DC_CRT_CONTROL); + + reg = (par->screen.sm_addr + xoffs + + var->yoffset * info->fix.line_length); + writel(reg | SM501_ADDR_FLIP, fbi->regs + SM501_DC_CRT_FB_ADDR); + + sm501fb_sync_regs(fbi); + return 0; +} + +/* sm501fb_pan_pnl + * + * pan the panel display output within an virtual framebuffer +*/ + +static int sm501fb_pan_pnl(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + unsigned long reg; + + if (!sm501fb_validate_pan(var)) + return -EINVAL; + + reg = var->xoffset | (var->xres_virtual << 16); + writel(reg, fbi->regs + SM501_DC_PANEL_FB_WIDTH); + + reg = var->yoffset | (var->yres_virtual << 16); + writel(reg, fbi->regs + SM501_DC_PANEL_FB_HEIGHT); + + sm501fb_sync_regs(fbi); + return 0; +} + +/* sm501fb_set_par_crt + * + * Set the CRT video mode from the fb_info structure +*/ + +static int sm501fb_set_par_crt(struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + struct fb_var_screeninfo *var = &info->var; + unsigned long control; /* control register */ + int ret; + + /* activate new configuration */ + + dev_dbg(fbi->dev, "%s(%p)\n", __func__, info); + + /* enable CRT DAC - note 0 is on!*/ + sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER); + + control = readl(fbi->regs + SM501_DC_CRT_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... */ + + sm501_alloc_mem(fbi, &par->screen, SM501_MEMF_CRT, 0); + goto out_update; + } + + ret = sm501fb_set_par_common(info, var); + if (ret) { + dev_err(fbi->dev, "failed to set common parameters\n"); + return ret; + } + + sm501fb_pan_crt(var, info); + sm501fb_set_par_geometry(info, var); + + control |= SM501_FIFO_3; /* fill if >3 free slots */ + + switch(var->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: + BUG(); + } + + 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, fbi->regs + SM501_DC_CRT_CONTROL); + sm501fb_sync_regs(fbi); + + return 0; +} + +static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) +{ + unsigned long control; + void __iomem *ctrl_reg = fbi->regs + 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(fbi); + mdelay(10); + + control |= SM501_DC_PANEL_CONTROL_DATA; /* DATA */ + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + + control |= SM501_DC_PANEL_CONTROL_BIAS; /* VBIASEN */ + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + 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(fbi); + mdelay(10); + + control &= ~SM501_DC_PANEL_CONTROL_BIAS; + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + + control &= ~SM501_DC_PANEL_CONTROL_DATA; + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + + control &= ~SM501_DC_PANEL_CONTROL_VDD; + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + } + + sm501fb_sync_regs(fbi); +} + +/* sm501fb_set_par_pnl + * + * Set the panel video mode from the fb_info structure +*/ + +static int sm501fb_set_par_pnl(struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + struct fb_var_screeninfo *var = &info->var; + unsigned long control; + unsigned long reg; + int ret; + + dev_dbg(fbi->dev, "%s(%p)\n", __func__, info); + + /* activate this new configuration */ + + ret = sm501fb_set_par_common(info, var); + if (ret) + return ret; + + sm501fb_pan_pnl(var, info); + sm501fb_set_par_geometry(info, var); + + /* update control register */ + + control = readl(fbi->regs + 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(var->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: + BUG(); + } + + writel(0x0, fbi->regs + SM501_DC_PANEL_PANNING_CONTROL); + + /* panel plane top left and bottom right location */ + + writel(0x00, fbi->regs + SM501_DC_PANEL_TL_LOC); + + reg = var->xres - 1; + reg |= (var->yres - 1) << 16; + + writel(reg, fbi->regs + 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, fbi->regs + SM501_DC_PANEL_CONTROL); + sm501fb_sync_regs(fbi); + + /* power the panel up */ + sm501fb_panel_power(fbi, 1); + return 0; +} + + +/* chan_to_field + * + * convert a colour value into a field position + * + * from pxafb.c +*/ + +static inline unsigned int chan_to_field(unsigned int chan, + struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +/* sm501fb_setcolreg + * + * set the colour mapping for modes that support palettised data +*/ + +static int sm501fb_setcolreg(unsigned regno, + unsigned red, unsigned green, unsigned blue, + unsigned transp, struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + void __iomem *base = fbi->regs; + unsigned int val; + + if (par->head == HEAD_CRT) + base += SM501_DC_CRT_PALETTE; + else + base += SM501_DC_PANEL_PALETTE; + + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + /* true-colour, use pseuo-palette */ + + if (regno < 16) { + u32 *pal = par->pseudo_palette; + + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue, &info->var.blue); + + pal[regno] = val; + } + break; + + case FB_VISUAL_PSEUDOCOLOR: + if (regno < 256) { + val = (red >> 8) << 16; + val |= (green >> 8) << 8; + val |= blue >> 8; + + writel(val, base + (regno * 4)); + } + + break; + + default: + return 1; /* unknown type */ + } + + return 0; +} + +/* sm501fb_blank_pnl + * + * Blank or un-blank the panel interface +*/ + +static int sm501fb_blank_pnl(int blank_mode, struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + unsigned long ctrl; + + dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info); + + switch (blank_mode) { + case FB_BLANK_POWERDOWN: + sm501fb_panel_power(fbi, 0); + break; + + case FB_BLANK_UNBLANK: + sm501fb_panel_power(fbi, 1); + break; + + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + default: + return 1; + } + + return 0; +} + +/* sm501fb_blank_crt + * + * Blank or un-blank the crt interface +*/ + +static int sm501fb_blank_crt(int blank_mode, struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + unsigned long ctrl; + + dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info); + + ctrl = readl(fbi->regs + SM501_DC_CRT_CONTROL); + + switch (blank_mode) { + case FB_BLANK_POWERDOWN: + ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE; + sm501_misc_control(fbi->dev->parent, SM501_MISC_DAC_POWER, 0); + + case FB_BLANK_NORMAL: + ctrl |= SM501_DC_CRT_CONTROL_BLANK; + break; + + case FB_BLANK_UNBLANK: + ctrl &= ~SM501_DC_CRT_CONTROL_BLANK; + ctrl |= SM501_DC_CRT_CONTROL_ENABLE; + sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER); + break; + + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + default: + return 1; + + } + + writel(ctrl, fbi->regs + SM501_DC_CRT_CONTROL); + sm501fb_sync_regs(fbi); + + return 0; +} + +/* sm501fb_cursor + * + * set or change the hardware cursor parameters +*/ + +int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + void __iomem *base = fbi->regs; + unsigned long hwc_addr; + unsigned long fg, bg; + + dev_dbg(fbi->dev, "%s(%p,%p)\n", __func__, info, cursor); + + if (par->head == HEAD_CRT) + base += SM501_DC_CRT_HWC_BASE; + else + base += SM501_DC_PANEL_HWC_BASE; + + /* check not being asked to exceed capabilities */ + + if (cursor->image.width > 64) + return -EINVAL; + + if (cursor->image.height > 64) + return -EINVAL; + + if (cursor->image.depth > 1) + return -EINVAL; + + hwc_addr = readl(base + SM501_OFF_HWC_ADDR); + + if (cursor->enable) + writel(hwc_addr | SM501_HWC_EN, base + SM501_OFF_HWC_ADDR); + else + writel(hwc_addr & ~SM501_HWC_EN, base + SM501_OFF_HWC_ADDR); + + /* set data */ + if (cursor->set & FB_CUR_SETPOS) { + if (cursor->image.dx >= 2048 || + cursor->image.dy >= 2048 ) + return -EINVAL; + + writel(cursor->image.dx | (cursor->image.dy << 16), + fbi->regs + SM501_OFF_HWC_LOC); + } + + if (cursor->set & FB_CUR_SETCMAP) { + unsigned int bg_col = cursor->image.bg_color; + unsigned int fg_col = cursor->image.fg_color; + + dev_dbg(fbi->dev, "%s: update cmap (%08x,%08x)\n", + __func__, bg_col, fg_col); + + bg = ((info->cmap.red[bg_col] & 0xF8) << 8) | + ((info->cmap.green[bg_col] & 0xFC) << 3) | + ((info->cmap.blue[bg_col] & 0xF8) >> 3); + + fg = ((info->cmap.red[fg_col] & 0xF8) << 8) | + ((info->cmap.green[fg_col] & 0xFC) << 3) | + ((info->cmap.blue[fg_col] & 0xF8) >> 3); + + writel(bg, fbi->regs + SM501_OFF_HWC_COLOR_1_2); + writel(fg, fbi->regs + SM501_OFF_HWC_COLOR_3); + } + + if (cursor->set & FB_CUR_SETSIZE || + cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE)) { + /* SM501 cursor is a two bpp 64x64 bitmap this routine + * clears it to transparent then combines the cursor + * shape plane with the colour plane to set the + * cursor */ + int x, y; + const unsigned char *pcol = cursor->image.data; + const unsigned char *pmsk = cursor->mask; + unsigned short *dst = par->cursor.k_addr; + unsigned char dcol = 0; + unsigned char dmsk = 0; + unsigned int op; + unsigned int col1 = 1, col2 = 3; + + dev_dbg(fbi->dev, "%s: setting shape\n", __func__); + + col1 <<= (8-2); + col2 <<= (8-2); + + memset(dst, 0, (64*64*2)/8); + + for (y = 0; y < cursor->image.height; y++) { + op = 0; + + for (x = 0; x < cursor->image.width; x++) { + if ((x >> 3) == 0) { + dcol = *pcol++; + dmsk = *pmsk++; + } else { + dcol >>= 1; + dmsk >>= 1; + } + + if (dmsk & 1) { + op |= (dcol & 1) ? col2 : col1; + } + + if ((x % 8) == 7) { + *dst++ = op; + op = 0; + } else + op >>= 2; + } + } + } + + sm501fb_sync_regs(fbi); /* ensure cursor data flushed */ + return 0; +} + +/* sm501fb_crtsrc_show + * + * device attribute code to show where the crt output is sourced from +*/ + +static int sm501fb_crtsrc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sm501fb_info *info = dev_get_drvdata(dev); + unsigned long ctrl; + + ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); + ctrl &= SM501_DC_CRT_CONTROL_SEL; + + return snprintf(buf, PAGE_SIZE, "%s\n", ctrl ? "crt" : "panel"); +} + +/* sm501fb_crtsrc_show + * + * device attribute code to set where the crt output is sourced from +*/ + +static int sm501fb_crtsrc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct sm501fb_info *info = dev_get_drvdata(dev); + enum sm501_controller head; + unsigned long ctrl; + + if (len < 1) + return -EINVAL; + + if (strnicmp(buf, "crt", sizeof("crt")) == 0) + head = HEAD_CRT; + else if (strnicmp(buf, "panel", sizeof("panel")) == 0) + head = HEAD_PANEL; + else + return -EINVAL; + + dev_info(dev, "setting crt source to head %d\n", head); + + ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); + + if (head == HEAD_CRT) { + ctrl |= SM501_DC_CRT_CONTROL_SEL; + ctrl |= SM501_DC_CRT_CONTROL_ENABLE; + ctrl |= SM501_DC_CRT_CONTROL_TE; + } else { + ctrl &= ~SM501_DC_CRT_CONTROL_SEL; + ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE; + ctrl &= ~SM501_DC_CRT_CONTROL_TE; + } + + writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); + sm501fb_sync_regs(info); + + return (head == HEAD_CRT) ? 3 : 5; +} + +/* Prepare the device_attr for registration with sysfs later */ +static DEVICE_ATTR(crt_src, 0666, sm501fb_crtsrc_show, sm501fb_crtsrc_store); + +/* sm501fb_show_regs + * + * show the primary sm501 registers +*/ +static int sm501fb_show_regs(struct sm501fb_info *info, char *ptr, + unsigned int start, unsigned int len) +{ + void __iomem *mem = info->regs; + char *buf = ptr; + unsigned int reg; + + for (reg = start; reg < (len + start); reg += 4) + ptr += sprintf(ptr, "%08x = %08x\n", reg, readl(mem + reg)); + + return ptr - buf; +} + +/* sm501fb_debug_show_crt + * + * show the crt control and cursor registers +*/ + +static int sm501fb_debug_show_crt(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sm501fb_info *info = dev_get_drvdata(dev); + char *ptr = buf; + + ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_CONTROL, 0x40); + ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_HWC_BASE, 0x10); + + return ptr - buf; +} + +static DEVICE_ATTR(fbregs_crt, 0444, sm501fb_debug_show_crt, NULL); + +/* sm501fb_debug_show_pnl + * + * show the panel control and cursor registers +*/ + +static int sm501fb_debug_show_pnl(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sm501fb_info *info = dev_get_drvdata(dev); + char *ptr = buf; + + ptr += sm501fb_show_regs(info, ptr, 0x0, 0x40); + ptr += sm501fb_show_regs(info, ptr, SM501_DC_PANEL_HWC_BASE, 0x10); + + return ptr - buf; +} + +static DEVICE_ATTR(fbregs_pnl, 0444, sm501fb_debug_show_pnl, NULL); + +/* framebuffer ops */ + +static struct fb_ops sm501fb_ops_crt = { + .owner = THIS_MODULE, + .fb_check_var = sm501fb_check_var_crt, + .fb_set_par = sm501fb_set_par_crt, + .fb_blank = sm501fb_blank_crt, + .fb_setcolreg = sm501fb_setcolreg, + .fb_pan_display = sm501fb_pan_crt, + .fb_cursor = sm501fb_cursor, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static struct fb_ops sm501fb_ops_pnl = { + .owner = THIS_MODULE, + .fb_check_var = sm501fb_check_var_pnl, + .fb_set_par = sm501fb_set_par_pnl, + .fb_pan_display = sm501fb_pan_pnl, + .fb_blank = sm501fb_blank_pnl, + .fb_setcolreg = sm501fb_setcolreg, + .fb_cursor = sm501fb_cursor, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +/* sm501fb_info_alloc + * + * creates and initialises an sm501fb_info structure +*/ + +static struct sm501fb_info *sm501fb_info_alloc(struct fb_info *fbinfo_crt, + struct fb_info *fbinfo_pnl) +{ + struct sm501fb_info *info; + struct sm501fb_par *par; + + info = kzalloc(sizeof(struct sm501fb_info), GFP_KERNEL); + if (info) { + /* set the references back */ + + par = fbinfo_crt->par; + par->info = info; + par->head = HEAD_CRT; + fbinfo_crt->pseudo_palette = &par->pseudo_palette; + + par = fbinfo_pnl->par; + par->info = info; + par->head = HEAD_PANEL; + fbinfo_pnl->pseudo_palette = &par->pseudo_palette; + + /* store the two fbs into our info */ + info->fb[HEAD_CRT] = fbinfo_crt; + info->fb[HEAD_PANEL] = fbinfo_pnl; + } + + return info; +} + +/* sm501_init_cursor + * + * initialise hw cursor parameters +*/ + +int sm501_init_cursor(struct fb_info *fbi, unsigned int reg_base) +{ + struct sm501fb_par *par = fbi->par; + struct sm501fb_info *info = par->info; + int ret; + + par->cursor_regs = info->regs + reg_base; + + ret = sm501_alloc_mem(info, &par->cursor, SM501_MEMF_CURSOR, 1024); + if (ret < 0) + return ret; + + /* initialise the colour registers */ + + writel(par->cursor.sm_addr, par->cursor_regs + SM501_OFF_HWC_ADDR); + + writel(0x00, par->cursor_regs + SM501_OFF_HWC_LOC); + writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_1_2); + writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_3); + sm501fb_sync_regs(info); + + return 0; +} + +/* sm501fb_info_start + * + * fills the par structure claiming resources and remapping etc. +*/ + +static int sm501fb_start(struct sm501fb_info *info, + struct platform_device *pdev) +{ + struct resource *res; + struct device *dev; + int ret; + + info->dev = dev = &pdev->dev; + platform_set_drvdata(pdev, info); + + info->irq = ret = platform_get_irq(pdev, 0); + if (ret < 0) { + /* we currently do not use the IRQ */ + dev_warn(dev, "no irq for device\n"); + } + + /* allocate, reserve and remap resources for registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(dev, "no resource definition for registers\n"); + ret = -ENOENT; + goto err_release; + } + + info->regs_res = request_mem_region(res->start, + res->end - res->start, + pdev->name); + + if (info->regs_res == NULL) { + dev_err(dev, "cannot claim registers\n"); + ret = -ENXIO; + goto err_release; + } + + info->regs = ioremap(res->start, (res->end - res->start)+1); + if (info->regs == NULL) { + dev_err(dev, "cannot remap registers\n"); + ret = -ENXIO; + goto err_regs_res; + } + + /* allocate, reserve resources for framebuffer */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (res == NULL) { + dev_err(dev, "no memory resource defined\n"); + ret = -ENXIO; + goto err_regs_map; + } + + info->fbmem_res = request_mem_region(res->start, + (res->end - res->start)+1, + pdev->name); + if (info->fbmem_res == NULL) { + dev_err(dev, "cannot claim framebuffer\n"); + ret = -ENXIO; + goto err_regs_map; + } + + info->fbmem = ioremap(res->start, (res->end - res->start)+1); + if (info->fbmem == NULL) { + dev_err(dev, "cannot remap framebuffer\n"); + goto err_mem_res; + } + + info->fbmem_len = (res->end - res->start)+1; + + /* enable display controller */ + sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1); + + /* 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); + + return 0; /* everything is setup */ + + err_mem_res: + release_resource(info->fbmem_res); + kfree(info->fbmem_res); + + err_regs_map: + iounmap(info->regs); + + err_regs_res: + release_resource(info->regs_res); + kfree(info->regs_res); + + err_release: + return ret; +} + +static void sm501fb_stop(struct sm501fb_info *info) +{ + /* disable display controller */ + sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0); + + iounmap(info->fbmem); + release_resource(info->fbmem_res); + kfree(info->fbmem_res); + + iounmap(info->regs); + release_resource(info->regs_res); + kfree(info->regs_res); +} + +static void sm501fb_info_release(struct sm501fb_info *info) +{ + kfree(info); +} + +static int sm501fb_init_fb(struct fb_info *fb, + enum sm501_controller head, + const char *fbname) +{ + struct sm501_platdata_fbsub *pd; + struct sm501fb_par *par = fb->par; + struct sm501fb_info *info = par->info; + unsigned long ctrl; + unsigned int enable; + int ret; + + switch (head) { + case HEAD_CRT: + pd = info->pdata->fb_crt; + ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); + enable = (ctrl & SM501_DC_CRT_CONTROL_ENABLE) ? 1 : 0; + + /* ensure we set the correct source register */ + if (info->pdata->fb_route != SM501_FB_CRT_PANEL) { + ctrl |= SM501_DC_CRT_CONTROL_SEL; + writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); + } + + break; + + case HEAD_PANEL: + pd = info->pdata->fb_pnl; + ctrl = readl(info->regs + SM501_DC_PANEL_CONTROL); + enable = (ctrl & SM501_DC_PANEL_CONTROL_EN) ? 1 : 0; + break; + + default: + pd = NULL; /* stop compiler warnings */ + ctrl = 0; + enable = 0; + BUG(); + } + + dev_info(info->dev, "fb %s %sabled at start\n", + fbname, enable ? "en" : "dis"); + + /* check to see if our routing allows this */ + + if (head == HEAD_CRT && info->pdata->fb_route == SM501_FB_CRT_PANEL) { + ctrl &= ~SM501_DC_CRT_CONTROL_SEL; + writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); + enable = 0; + } + + strlcpy(fb->fix.id, fbname, sizeof(fb->fix.id)); + + memcpy(&par->ops, + (head == HEAD_CRT) ? &sm501fb_ops_crt : &sm501fb_ops_pnl, + sizeof(struct fb_ops)); + + /* update ops dependant on what we've been passed */ + + if ((pd->flags & SM501FB_FLAG_USE_HWCURSOR) == 0) + par->ops.fb_cursor = NULL; + + fb->fbops = &par->ops; + fb->flags = FBINFO_FLAG_DEFAULT | + FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; + + /* fixed data */ + + fb->fix.type = FB_TYPE_PACKED_PIXELS; + fb->fix.type_aux = 0; + fb->fix.xpanstep = 1; + fb->fix.ypanstep = 1; + fb->fix.ywrapstep = 0; + fb->fix.accel = FB_ACCEL_NONE; + + /* screenmode */ + + fb->var.nonstd = 0; + fb->var.activate = FB_ACTIVATE_NOW; + fb->var.accel_flags = 0; + fb->var.vmode = FB_VMODE_NONINTERLACED; + fb->var.bits_per_pixel = 16; + + if (enable && (pd->flags & SM501FB_FLAG_USE_INIT_MODE) && 0) { + /* TODO read the mode from the current display */ + + } else { + ret = fb_find_mode(&fb->var, fb, NULL, NULL, 0, NULL, 8); + + if (ret == 0 || ret == 4) { + dev_err(info->dev, "failed to get initial mode\n"); + return -EINVAL; + } + } + + /* initialise and set the palette */ + fb_alloc_cmap(&fb->cmap, NR_PALETTE, 0); + fb_set_cmap(&fb->cmap, fb); + + ret = (fb->fbops->fb_check_var)(&fb->var, fb); + if (ret) + dev_err(info->dev, "check_var() failed on initial setup?\n"); + + /* ensure we've activated our new configuration */ + (fb->fbops->fb_set_par)(fb); + + return 0; +} + +/* default platform data if none is supplied (ie, PCI device) */ + +static struct sm501_platdata_fbsub sm501fb_pdata_crt = { + .flags = (SM501FB_FLAG_USE_INIT_MODE | + SM501FB_FLAG_USE_HWCURSOR | + SM501FB_FLAG_USE_HWACCEL | + SM501FB_FLAG_DISABLE_AT_EXIT), + +}; + +static struct sm501_platdata_fbsub sm501fb_pdata_pnl = { + .flags = (SM501FB_FLAG_USE_INIT_MODE | + SM501FB_FLAG_USE_HWCURSOR | + SM501FB_FLAG_USE_HWACCEL | + SM501FB_FLAG_DISABLE_AT_EXIT), +}; + +static struct sm501_platdata_fb sm501fb_def_pdata = { + .fb_route = SM501_FB_OWN, + .fb_crt = &sm501fb_pdata_crt, + .fb_pnl = &sm501fb_pdata_pnl, +}; + +static char driver_name_crt[] = "sm501fb-crt"; +static char driver_name_pnl[] = "sm501fb-panel"; + +static int __init sm501fb_probe(struct platform_device *pdev) +{ + struct sm501fb_info *info; + struct device *dev = &pdev->dev; + struct fb_info *fbinfo_crt; + struct fb_info *fbinfo_pnl; + int ret; + + /* allocate our framebuffers */ + + fbinfo_crt = framebuffer_alloc(sizeof(struct sm501fb_par), dev); + if (fbinfo_crt == NULL) { + dev_err(dev, "cannot allocate crt framebuffer\n"); + return -ENOMEM; + } + + fbinfo_pnl = framebuffer_alloc(sizeof(struct sm501fb_par), dev); + if (fbinfo_pnl == NULL) { + dev_err(dev, "cannot allocate panel framebuffer\n"); + ret = -ENOMEM; + goto fbinfo_crt_alloc_fail; + } + + info = sm501fb_info_alloc(fbinfo_crt, fbinfo_pnl); + if (info == NULL) { + dev_err(dev, "cannot allocate par\n"); + ret = -ENOMEM; + goto sm501fb_alloc_fail; + } + + if (dev->parent->platform_data) { + struct sm501_platdata *pd = dev->parent->platform_data; + info->pdata = pd->fb; + } + + if (info->pdata == NULL) { + dev_info(dev, "using default configuration data\n"); + info->pdata = &sm501fb_def_pdata; + } + + /* start the framebuffers */ + + ret = sm501fb_start(info, pdev); + if (ret) { + dev_err(dev, "cannot initialise SM501\n"); + goto sm501fb_start_fail; + } + + /* CRT framebuffer setup */ + + ret = sm501fb_init_fb(fbinfo_crt, HEAD_CRT, driver_name_crt); + if (ret) { + dev_err(dev, "cannot initialise CRT fb\n"); + goto sm501fb_start_fail; + } + + /* Panel framebuffer setup */ + + ret = sm501fb_init_fb(fbinfo_pnl, HEAD_PANEL, driver_name_pnl); + if (ret) { + dev_err(dev, "cannot initialise Panel fb\n"); + goto sm501fb_start_fail; + } + + /* register framebuffers */ + + ret = register_framebuffer(fbinfo_crt); + if (ret < 0) { + dev_err(dev, "failed to register CRT fb (%d)\n", ret); + goto register_crt_fail; + } + + ret = register_framebuffer(fbinfo_pnl); + if (ret < 0) { + dev_err(dev, "failed to register panel fb (%d)\n", ret); + goto register_pnl_fail; + } + + dev_info(dev, "fb%d: %s frame buffer device\n", + fbinfo_crt->node, fbinfo_crt->fix.id); + + dev_info(dev, "fb%d: %s frame buffer device\n", + fbinfo_pnl->node, fbinfo_pnl->fix.id); + + /* create device files */ + + ret = device_create_file(dev, &dev_attr_crt_src); + if (ret) + goto crtsrc_fail; + + ret = device_create_file(dev, &dev_attr_fbregs_pnl); + if (ret) + goto fbregs_pnl_fail; + + ret = device_create_file(dev, &dev_attr_fbregs_crt); + if (ret) + goto fbregs_crt_fail; + + /* we registered, return ok */ + return 0; + + fbregs_crt_fail: + device_remove_file(dev, &dev_attr_fbregs_pnl); + + fbregs_pnl_fail: + device_remove_file(dev, &dev_attr_crt_src); + + crtsrc_fail: + unregister_framebuffer(fbinfo_pnl); + + register_pnl_fail: + unregister_framebuffer(fbinfo_crt); + + register_crt_fail: + sm501fb_stop(info); + + sm501fb_start_fail: + sm501fb_info_release(info); + + sm501fb_alloc_fail: + framebuffer_release(fbinfo_pnl); + + fbinfo_crt_alloc_fail: + framebuffer_release(fbinfo_crt); + + return ret; +} + + +/* + * Cleanup + */ +static int sm501fb_remove(struct platform_device *pdev) +{ + struct sm501fb_info *info = platform_get_drvdata(pdev); + struct fb_info *fbinfo_crt = info->fb[0]; + struct fb_info *fbinfo_pnl = info->fb[1]; + + device_remove_file(&pdev->dev, &dev_attr_fbregs_crt); + device_remove_file(&pdev->dev, &dev_attr_fbregs_pnl); + device_remove_file(&pdev->dev, &dev_attr_crt_src); + + unregister_framebuffer(fbinfo_crt); + unregister_framebuffer(fbinfo_pnl); + + sm501fb_stop(info); + sm501fb_info_release(info); + + framebuffer_release(fbinfo_pnl); + framebuffer_release(fbinfo_crt); + + return 0; +} + +#ifdef CONFIG_PM + +static int sm501fb_suspend_fb(struct sm501fb_info *info, + enum sm501_controller head) +{ + struct fb_info *fbi = info->fb[head]; + struct sm501fb_par *par = fbi->par; + + if (par->screen.size == 0) + return 0; + + /* backup copies in case chip is powered down over suspend */ + + par->store_fb = vmalloc(par->screen.size); + if (par->store_fb == NULL) { + dev_err(info->dev, "no memory to store screen\n"); + return -ENOMEM; + } + + par->store_cursor = vmalloc(par->cursor.size); + if (par->store_cursor == NULL) { + dev_err(info->dev, "no memory to store cursor\n"); + goto err_nocursor; + } + + memcpy_fromio(par->store_fb, par->screen.k_addr, par->screen.size); + memcpy_fromio(par->store_cursor, par->cursor.k_addr, par->cursor.size); + + /* blank the relevant interface to ensure unit power minimised */ + (par->ops.fb_blank)(FB_BLANK_POWERDOWN, fbi); + + return 0; + + err_nocursor: + vfree(par->store_fb); + + return -ENOMEM; + +} + +static void sm501fb_resume_fb(struct sm501fb_info *info, + enum sm501_controller head) +{ + struct fb_info *fbi = info->fb[head]; + struct sm501fb_par *par = fbi->par; + + if (par->screen.size == 0) + return; + + /* re-activate the configuration */ + + (par->ops.fb_set_par)(fbi); + + /* restore the data */ + + memcpy_toio(par->screen.k_addr, par->store_fb, par->screen.size); + memcpy_toio(par->cursor.k_addr, par->store_cursor, par->cursor.size); + + vfree(par->store_fb); + vfree(par->store_cursor); +} + + +/* suspend and resume support */ + +static int sm501fb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct sm501fb_info *info = platform_get_drvdata(pdev); + + sm501fb_suspend_fb(info, HEAD_CRT); + sm501fb_suspend_fb(info, HEAD_PANEL); + + /* turn off the clocks, in case the device is not powered down */ + sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0); + + return 0; +} + +static int sm501fb_resume(struct platform_device *pdev) +{ + struct sm501fb_info *info = platform_get_drvdata(pdev); + + sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 1); + + sm501fb_resume_fb(info, HEAD_CRT); + sm501fb_resume_fb(info, HEAD_PANEL); + + return 0; +} + +#else +#define sm501fb_suspend NULL +#define sm501fb_resume NULL +#endif + +static struct platform_driver sm501fb_driver = { + .probe = sm501fb_probe, + .remove = sm501fb_remove, + .suspend = sm501fb_suspend, + .resume = sm501fb_resume, + .driver = { + .name = "sm501-fb", + .owner = THIS_MODULE, + }, +}; + +int __devinit sm501fb_init(void) +{ + return platform_driver_register(&sm501fb_driver); +} + +static void __exit sm501fb_cleanup(void) +{ + platform_driver_unregister(&sm501fb_driver); +} + +module_init(sm501fb_init); +module_exit(sm501fb_cleanup); + +MODULE_AUTHOR("Ben Dooks, Vincent Sanders"); +MODULE_DESCRIPTION("SM501 Framebuffer driver"); +MODULE_LICENSE("GPL v2"); |
From: Manuel L. <ma...@ro...> - 2007-01-22 08:05:15
|
Hi Ben, Ben Dooks <ben-fbdev <at> fluff.org> writes: > This is a resend of the SM501 patch, with all > the previous comments addressed, and some more > support for items such as blanking and display > panning. A number of code paths are now handled > by common blocks where possible to make the code > easier to change. Is there an updates patchset for the MFD part? (I'm using the one you posted on Dec 13, 2006 to LKML) I get a few compile errors in this driver, all referencing missing constants: sm501fb.c: In function `sm501fb_set_par_common': sm501fb.c:411: error: `SM501_ADDR_FLIP' undeclared (first use in this function) sm501fb.c:411: error: (Each undeclared identifier is reported only once sm501fb.c:411: error: for each function it appears in.) sm501fb.c: In function `sm501fb_set_par_geometry': sm501fb.c:462: error: `SM501_OFF_DC_H_TOT' undeclared (first use in this function) sm501fb.c:469: error: `SM501_OFF_DC_H_SYNC' undeclared (first use in this function) sm501fb.c:476: error: `SM501_OFF_DC_V_TOT' undeclared (first use in this function) sm501fb.c:482: error: `SM501_OFF_DC_V_SYNC' undeclared (first use in this function) sm501fb.c: In function `sm501fb_pan_crt': sm501fb.c:523: error: `SM501_DC_CRT_CONTROL_PIXEL_MASK' undeclared (first use in this function) sm501fb.c:529: error: `SM501_ADDR_FLIP' undeclared (first use in this function) (and so on...) Thanks! -- Manuel Lauss |
From: Ben D. <be...@fl...> - 2007-01-23 10:18:16
|
On Mon, Jan 22, 2007 at 07:55:20AM +0000, Manuel Lauss wrote: > Hi Ben, > > Ben Dooks <ben-fbdev <at> fluff.org> writes: > > > This is a resend of the SM501 patch, with all > > the previous comments addressed, and some more > > support for items such as blanking and display > > panning. A number of code paths are now handled > > by common blocks where possible to make the code > > easier to change. > > Is there an updates patchset for the MFD part? > (I'm using the one you posted on Dec 13, 2006 to LKML) > I get a few compile errors in this driver, all > referencing missing constants: > > sm501fb.c: In function `sm501fb_set_par_common': > sm501fb.c:411: error: `SM501_ADDR_FLIP' undeclared (first use in this function) > sm501fb.c:411: error: (Each undeclared identifier is reported only once > sm501fb.c:411: error: for each function it appears in.) > sm501fb.c: In function `sm501fb_set_par_geometry': > sm501fb.c:462: error: `SM501_OFF_DC_H_TOT' undeclared (first use in this > function) > sm501fb.c:469: error: `SM501_OFF_DC_H_SYNC' undeclared (first use in this > function) > sm501fb.c:476: error: `SM501_OFF_DC_V_TOT' undeclared (first use in this > function) > sm501fb.c:482: error: `SM501_OFF_DC_V_SYNC' undeclared (first use in this > function) > sm501fb.c: In function `sm501fb_pan_crt': > sm501fb.c:523: error: `SM501_DC_CRT_CONTROL_PIXEL_MASK' undeclared (first use in > this function) > sm501fb.c:529: error: `SM501_ADDR_FLIP' undeclared (first use in this function) > > (and so on...) This is the new MFD patch, which should be re-posted soon to the linux-kernel list for looking at 2.6.20. --- linux-2.6.19/drivers/mfd/Kconfig 2006-11-29 21:57:37.000000000 +0000 +++ linux-2.6.19-simtec1p28/drivers/mfd/Kconfig 2007-01-22 20:19:30.000000000 +0000 @@ -2,6 +2,20 @@ # Multifunction miscellaneous devices # +menu "Multifunction device drivers" + +config MFD_SM501 + tristate "Support for Silicon Motion SM501" + ---help--- + This is the core driver for the Silicon motion SM501 multimedia + companion chip. This device is a multifunction device which may + provide numerous interfaces including USB host controller USB gadget, + Asyncronous Serial ports, Audio functions and a dual display video + interface. The device may be connected by PCI or local bus with + varying functions enabled. + +endmenu + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 --- linux-2.6.19/drivers/mfd/Makefile 2006-11-29 21:57:37.000000000 +0000 +++ linux-2.6.19-simtec1p28/drivers/mfd/Makefile 2007-01-22 20:19:30.000000000 +0000 @@ -2,6 +2,8 @@ # Makefile for multifunction miscellaneous devices # +obj-$(CONFIG_MFD_SM501) += sm501.o + obj-$(CONFIG_MCP) += mcp-core.o obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o --- linux-2.6.19/drivers/mfd/sm501.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19-simtec1p28/drivers/mfd/sm501.c 2007-01-22 20:19:30.000000000 +0000 @@ -0,0 +1,1101 @@ +/* linux/drivers/mfd/sm501.c + * + * Copyright (C) 2006 Simtec Electronics + * Ben Dooks <be...@si...> + * Vincent Sanders <vi...@si...> + * + * 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. + * + * SM501 MFD driver +*/ + +#define DEBUG + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/pci.h> +#include <linux/serial_8250.h> + +#include <linux/sm501.h> +#include <linux/sm501-regs.h> + +//#include <asm/sizes.h> +#include <asm/io.h> + +#define SZ_256K (256*1024) +#define SZ_1M (1*1024*1024) +#define SZ_2M (2*1024*1024) +#define SZ_4M (4*1024*1024) +#define SZ_8M (8*1024*1024) +#define SZ_16M (16*1024*1024) +#define SZ_32M (32*1024*1024) +#define SZ_64M (64*1024*1024) + +/* we need to work out why we cannot reserve these PCI regions */ +#define do_request_regions (0) + +struct sm501_device { + struct list_head list; + struct platform_device pdev; +}; + +struct sm501_devdata { + spinlock_t reg_lock; + struct semaphore clock_lock; + struct list_head devices; + + struct device *dev; + struct resource *io_res; + struct resource *mem_res; + struct sm501_platdata *platdata; + + int unit_power[20]; + unsigned int pdev_id; + unsigned int irq; + void __iomem *regs; +}; + +#ifdef CONFIG_DEBUG +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, +}; +#endif + +#define decode_div(val, lshft, selbit, mask, dtab) \ + ((((val) & (selbit)) ? pll2 : 288 * MHZ) / dtab[(((val) >> (lshft)) & (mask))]) + +#define MHZ (1000 * 1000) + +#define fmt_freq(x) ((x) / MHZ), ((x) % MHZ), (x) + +#ifdef CONFIG_DEBUG + +/* 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(pm0, 24, 1<<29, 31, px_div)), + fmt_freq(decode_div(pm0, 16, 1<<20, 15, misc_div)), + fmt_freq(decode_div(pm0, 8, 1<<12, 15, misc_div)), + fmt_freq(decode_div(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(pm1, 24, 1<<29, 31, px_div)), + fmt_freq(decode_div(pm1, 16, 1<<20, 15, misc_div)), + fmt_freq(decode_div(pm1, 8, 1<<12, 15, misc_div)), + fmt_freq(decode_div(pm1, 0, 1<<4, 15, misc_div))); +} +#else +static void sm501_dump_clk(struct sm501_devdata *sm) +{ +} +#endif + +/* sm501_misc_control + * + * alters the misceleneous control parameters +*/ + +int sm501_misc_control(struct device *dev, + unsigned long set, unsigned long clear) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev); + unsigned long misc; + unsigned long save; + unsigned long to; + + spin_lock_irqsave(&sm->reg_lock, save); + + misc = readl(sm->regs + SM501_MISC_CONTROL); + to = (misc & ~clear) | set; + + if (to != misc) { + writel(to, sm->regs + SM501_MISC_CONTROL); + (void)readl(sm->regs); + + dev_dbg(sm->dev, "MISC_CONTROL %08lx\n", misc); + } + + spin_unlock_irqrestore(&sm->reg_lock, save); + return to; +} + +EXPORT_SYMBOL_GPL(sm501_misc_control); + +/* sm501_modify_reg + * + * Modify a register in the SM501 which may be shared with other + * drivers. +*/ + +unsigned long sm501_modify_reg(struct device *dev, + unsigned long reg, + unsigned long set, + unsigned long clear) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev); + unsigned long data; + unsigned long save; + + spin_lock_irqsave(&sm->reg_lock, save); + + data = readl(sm->regs + reg); + data |= set; + data &= ~clear; + + writel(data, sm->regs + reg); + (void)readl(sm->regs); + + spin_unlock_irqrestore(&sm->reg_lock, save); + + return data; +} + +EXPORT_SYMBOL_GPL(sm501_modify_reg); + +unsigned long sm501_gpio_get(struct device *dev, + unsigned long gpio) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev); + 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; +} + +EXPORT_SYMBOL_GPL(sm501_gpio_get); + +void sm501_gpio_set(struct device *dev, + unsigned long gpio, + unsigned int to, + unsigned int dir) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev); + + unsigned long bit = 1 << (gpio & 31); + unsigned long base; + unsigned long save; + unsigned long val; + + base = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW; + base += SM501_GPIO; + + spin_lock_irqsave(&sm->reg_lock, save); + + 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); + (void)readl(sm->regs); + + spin_unlock_irqrestore(&sm->reg_lock, save); + +} + +EXPORT_SYMBOL_GPL(sm501_gpio_set); + + +/* sm501_unit_power + * + * alters the power active gate to set specific units on or off + */ + +int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev); + unsigned long mode; + unsigned long gate; + unsigned long clock; + + down(&sm->clock_lock); + + 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); + (void)readl(sm->regs); + + dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", + gate, clock, mode); + + msleep(16); + + already: + up(&sm->clock_lock); + return gate; +} + +EXPORT_SYMBOL_GPL(sm501_unit_power); + + +/* 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 device *dev, + int clksrc, + unsigned long req_freq) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev); + 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 */ + } + + down(&sm->clock_lock); + + 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: + up(&sm->clock_lock); + return -1; + } + + writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); + (void)readl(sm->regs); + + dev_info(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", + gate, clock, mode); + + msleep(16); + up(&sm->clock_lock); + + sm501_dump_clk(sm); + + return sm501_freq; +} + +EXPORT_SYMBOL_GPL(sm501_set_clock); + +/* 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; +} + +EXPORT_SYMBOL_GPL(sm501_find_clock); + +/* sm501_null_release + * + * A release function for the platform devices we create to keep the + * driver core happy, and stop any crashed when the devices are removed +*/ + +static void sm501_null_release(struct device *dev) +{ +} + +/* sm501_create_subdev + * + * Create a skeleton platform device with resources for passing to a + * sub-driver +*/ + +static struct platform_device * +sm501_create_subdev(struct sm501_devdata *sm, + char *name, unsigned int res_count) +{ + struct sm501_device *smdev; + + smdev = kzalloc(sizeof(struct sm501_device) + + sizeof(struct resource) * res_count, GFP_KERNEL); + if (!smdev) + return NULL; + + smdev->pdev.dev.release = sm501_null_release; + + smdev->pdev.name = name; + smdev->pdev.id = sm->pdev_id; + smdev->pdev.resource = (struct resource *)(smdev+1); + smdev->pdev.num_resources = res_count; + + smdev->pdev.dev.parent = sm->dev; + + return &smdev->pdev; +} + +static struct sm501_device *to_sm_device(struct platform_device *pdev) +{ + return container_of(pdev, struct sm501_device, pdev); +} + +/* sm501_register_device + * + * Register a platform device created with sm501_create_subdev() +*/ + +static int sm501_register_device(struct sm501_devdata *sm, + struct platform_device *pdev) +{ + struct sm501_device *smdev = to_sm_device(pdev); + int ptr; + int ret; + + for (ptr = 0; ptr < pdev->num_resources; ptr++) { + printk("%s[%d] flags %08lx: %08zx..%08zx\n", + pdev->name, ptr, + pdev->resource[ptr].flags, + pdev->resource[ptr].start, + pdev->resource[ptr].end); + } + + ret = platform_device_register(pdev); + + if (ret >= 0) { + dev_dbg(sm->dev, "registered %s\n", pdev->name); + list_add_tail(&smdev->list, &sm->devices); + } else + dev_err(sm->dev, "error registering %s (%d)\n", + pdev->name, ret); + + return ret; +} + +/* sm501_create_subio + * + * Fill in an IO resource for a sub device +*/ + +static void sm501_create_subio(struct sm501_devdata *sm, + struct resource *res, + unsigned long offs, + unsigned long size) +{ + res->flags = IORESOURCE_MEM; + res->parent = sm->io_res; + res->start = sm->io_res->start + offs; + res->end = res->start + size - 1; +} + +/* sm501_create_mem + * + * Fill in an MEM resource for a sub device +*/ + +static void sm501_create_mem(struct sm501_devdata *sm, + struct resource *res, + unsigned long *offs, + unsigned long size) +{ + *offs -= size; /* adjust memory size */ + + res->flags = IORESOURCE_MEM; + res->parent = sm->mem_res; + res->start = sm->mem_res->start + *offs; + res->end = res->start + size - 1; +} + +/* sm501_create_irq + * + * Fill in an IRQ resource for a sub device +*/ + +static void sm501_create_irq(struct sm501_devdata *sm, + struct resource *res) +{ + res->flags = IORESOURCE_IRQ; + res->parent = NULL; + res->start = res->end = sm->irq; +} + +static int sm501_register_usbhost(struct sm501_devdata *sm, + unsigned long *mem_avail) +{ + struct platform_device *pdev; + + pdev = sm501_create_subdev(sm, "sm501-usb", 3); + if (!pdev) + return -ENOMEM; + + sm501_create_subio(sm, &pdev->resource[0], 0x40000, 0x20000); + sm501_create_mem(sm, &pdev->resource[1], mem_avail, SZ_256K); + sm501_create_irq(sm, &pdev->resource[2]); + + return sm501_register_device(sm, pdev); +} + +static int sm501_register_display(struct sm501_devdata *sm, + unsigned long *mem_avail) +{ + struct platform_device *pdev; + + pdev = sm501_create_subdev(sm, "sm501-fb", 4); + if (!pdev) + return -ENOMEM; + + sm501_create_subio(sm, &pdev->resource[0], 0x80000, 0x10000); + sm501_create_subio(sm, &pdev->resource[1], 0x100000, 0x50000); + sm501_create_mem(sm, &pdev->resource[2], mem_avail, *mem_avail); + sm501_create_irq(sm, &pdev->resource[3]); + + return sm501_register_device(sm, pdev); +} + +/* sm501_dbg_regs + * + * Debug attribute to attach to parent device to show core registers +*/ + +static ssize_t sm501_dbg_regs(struct device *dev, + struct device_attribute *attr, char *buff) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev) ; + unsigned int reg; + char *ptr = buff; + int ret; + + for (reg = 0x00; reg < 0x70; reg += 4) { + ret = sprintf(ptr, "%08x = %08x\n", + reg, readl(sm->regs + reg)); + ptr += ret; + } + + return ptr - buff; +} + + +static DEVICE_ATTR(dbg_regs, 0666, sm501_dbg_regs, NULL); + +/* sm501_init_reg + * + * Helper function for the init code to setup a register +*/ + +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->set; + tmp &= ~r->mask; + 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->dev, + 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); +} + +static unsigned int sm501_mem_local[] = { + [0] = SZ_4M, + [1] = SZ_8M, + [2] = SZ_16M, + [3] = SZ_32M, + [4] = SZ_64M, + [5] = SZ_2M, +}; + +/* 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; + int ret; + + sema_init(&sm->clock_lock, 1); + spin_lock_init(&sm->reg_lock); + + INIT_LIST_HEAD(&sm->devices); + + dramctrl = readl(sm->regs + SM501_DRAM_CONTROL); + + mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7]; + + dev_info(sm->dev, "SM501 At %p,%p: Version %08x, %ld Mb, IRQ %d\n", + NULL, sm->regs, readl(sm->regs + SM501_DEVICEID), + mem_avail >> 20, sm->irq); + + dev_info(sm->dev, "CurrentGate %08x\n", readl(sm->regs+0x38)); + dev_info(sm->dev, "CurrentClock %08x\n", readl(sm->regs+0x3c)); + dev_info(sm->dev, "PowerModeControl %08x\n", readl(sm->regs+0x54)); + + ret = device_create_file(sm->dev, &dev_attr_dbg_regs); + if (ret) + dev_err(sm->dev, "failed to create debug regs file\n"); + + 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); + + if (pdata->init->devices & SM501_USE_USB_HOST) + sm501_register_usbhost(sm, &mem_avail); + } + } + + /* always create a framebuffer */ + sm501_register_display(sm, &mem_avail); + + return 0; +} + +static int sm501_plat_probe(struct platform_device *dev) +{ + struct sm501_devdata *sm; + int err; + + sm = kzalloc(sizeof(struct sm501_devdata), GFP_KERNEL); + if (sm == NULL) { + dev_err(&dev->dev, "no memory for device data\n"); + err = -ENOMEM; + goto err1; + } + + sm->dev = &dev->dev; + sm->pdev_id = dev->id; + sm->irq = platform_get_irq(dev, 0); + sm->io_res = platform_get_resource(dev, IORESOURCE_MEM, 1); + sm->mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0); + sm->platdata = dev->dev.platform_data; + + if (sm->irq < 0) { + dev_err(&dev->dev, "failed to get irq resource\n"); + err = sm->irq; + goto err_res; + } + + if (sm->io_res == NULL || sm->mem_res == NULL) { + dev_err(&dev->dev, "failed to get IO resource\n"); + err = -ENOENT; + goto err_res; + } + + platform_set_drvdata(dev, sm); + + sm->regs = ioremap(sm->io_res->start, + (sm->io_res->end - sm->io_res->start) - 1); + + if (sm->regs == NULL) { + dev_err(&dev->dev, "cannot remap registers\n"); + err = -EIO; + goto err_res; + } + + return sm501_init_dev(sm); + + err_res: + kfree(sm); + err1: + return err; + +} + +/* 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, + .mclk = 100 * MHZ, + .m1xclk = 160 * MHZ, +}; + +static struct sm501_platdata sm501_pci_platdata = { + .init = &sm501_pci_initdata, +}; + +static int sm501_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct sm501_devdata *sm; + int err; + + sm = kzalloc(sizeof(struct sm501_devdata), GFP_KERNEL); + if (sm == NULL) { + dev_err(&dev->dev, "no memory for device data\n"); + err = -ENOMEM; + goto err1; + } + + /* set a default set of platform data */ + dev->dev.platform_data = sm->platdata = &sm501_pci_platdata; + + /* set a hopefully unique id for our child platform devices */ + sm->pdev_id = 32 + dev->devfn; + + pci_set_drvdata(dev, sm); + + err = pci_enable_device(dev); + if (err) { + dev_err(&dev->dev, "cannot enable device\n"); + goto err2; + } + + sm->dev = &dev->dev; + sm->irq = dev->irq; + + /* check our resources */ + + if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM)) { + dev_err(&dev->dev, "region #0 is not memory?\n"); + err = -EINVAL; + goto err3; + } + + if (!(pci_resource_flags(dev, 1) & IORESOURCE_MEM)) { + dev_err(&dev->dev, "region #1 is not memory?\n"); + err = -EINVAL; + goto err3; + } + + /* reserve our io regions */ + + if (do_request_regions) { + err = pci_request_regions(dev, "sm501"); + if (err) { + dev_err(&dev->dev, "cannot get regions\n"); + goto err3; + } + } + + sm->io_res = &dev->resource[1]; + sm->mem_res = &dev->resource[0]; + + sm->regs = ioremap(pci_resource_start(dev, 1), + pci_resource_len(dev, 1)); + + if (sm->regs == NULL) { + dev_err(&dev->dev, "cannot remap registers\n"); + err = -EIO; + goto err4; + } + + sm501_init_dev(sm); + return 0; + + err4: + if (do_request_regions) + pci_release_regions(dev); + err3: + pci_disable_device(dev); + err2: + pci_set_drvdata(dev, NULL); + kfree(sm); + err1: + return err; +} + +static void sm501_remove_sub(struct sm501_devdata *sm, + struct sm501_device *smdev) +{ + list_del(&smdev->list); + platform_device_unregister(&smdev->pdev); +} + +static void sm501_dev_remove(struct sm501_devdata *sm) +{ + struct sm501_device *smdev, *tmp; + + list_for_each_entry_safe(smdev, tmp, &sm->devices, list) + sm501_remove_sub(sm, smdev); + + device_remove_file(sm->dev, &dev_attr_dbg_regs); +} + +static void sm501_pci_remove(struct pci_dev *dev) +{ + struct sm501_devdata *sm = pci_get_drvdata(dev); + + sm501_dev_remove(sm); + iounmap(sm->regs); + + if (do_request_regions) + pci_release_regions(dev); + + pci_set_drvdata(dev, NULL); + pci_disable_device(dev); +} + +static int sm501_plat_remove(struct platform_device *dev) +{ + struct sm501_devdata *sm = platform_get_drvdata(dev); + + sm501_dev_remove(sm); + iounmap(sm->regs); + + return 0; +} + +static struct pci_device_id sm501_pci_tbl[] = { + { 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, sm501_pci_tbl); + +static struct pci_driver sm501_pci_drv = { + .name = "sm501", + .id_table = sm501_pci_tbl, + .probe = sm501_pci_probe, + .remove = sm501_pci_remove, +}; + +static struct platform_driver sm501_plat_drv = { + .driver = { + .name = "sm501", + .owner = THIS_MODULE, + }, + .probe = sm501_plat_probe, + .remove = sm501_plat_remove, +}; + +static int __init sm501_base_init(void) +{ + platform_driver_register(&sm501_plat_drv); + return pci_module_init(&sm501_pci_drv); +} + +static void __exit sm501_base_exit(void) +{ + platform_driver_unregister(&sm501_plat_drv); + pci_unregister_driver(&sm501_pci_drv); +} + +module_init(sm501_base_init); +module_exit(sm501_base_exit); + +MODULE_DESCRIPTION("SM501 Core Driver"); +MODULE_AUTHOR("Ben Dooks <be...@si...>, Vincent Sanders"); +MODULE_LICENSE("GPL v2"); --- linux-2.6.19/include/linux/sm501-regs.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19-simtec1p28/include/linux/sm501-regs.h 2007-01-22 20:19:30.000000000 +0000 @@ -0,0 +1,357 @@ +/* sm501-regs.h + * + * Copyright 2006 Simtec Electronics + * + * 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. + * + * Silicon Motion SM501 register definitions +*/ + +/* System Configuration area */ +/* System config base */ +#define SM501_SYS_CONFIG (0x000000) + +/* config 1 */ +#define SM501_SYSTEM_CONTROL (0x000000) +#define SM501_MISC_CONTROL (0x000004) + +#define SM501_MISC_BUS_SH (0x0) +#define SM501_MISC_BUS_PCI (0x1) +#define SM501_MISC_BUS_XSCALE (0x2) +#define SM501_MISC_BUS_NEC (0x6) +#define SM501_MISC_BUS_MASK (0x7) + +#define SM501_MISC_VR_62MB (1<<3) +#define SM501_MISC_CDR_RESET (1<<7) +#define SM501_MISC_USB_LB (1<<8) +#define SM501_MISC_USB_SLAVE (1<<9) +#define SM501_MISC_BL_1 (1<<10) +#define SM501_MISC_MC (1<<11) +#define SM501_MISC_DAC_POWER (1<<12) +#define SM501_MISC_IRQ_INVERT (1<<16) +#define SM501_MISC_SH (1<<17) + +#define SM501_MISC_HOLD_EMPTY (0<<18) +#define SM501_MISC_HOLD_8 (1<<18) +#define SM501_MISC_HOLD_16 (2<<18) +#define SM501_MISC_HOLD_24 (3<<18) +#define SM501_MISC_HOLD_32 (4<<18) +#define SM501_MISC_HOLD_MASK (7<<18) + +#define SM501_MISC_FREQ_12 (1<<24) +#define SM501_MISC_PNL_24BIT (1<<25) +#define SM501_MISC_8051_LE (1<<26) + + + +#define SM501_GPIO31_0_CONTROL (0x000008) +#define SM501_GPIO63_32_CONTROL (0x00000C) +#define SM501_DRAM_CONTROL (0x000010) + +/* command list */ +#define SM501_ARBTRTN_CONTROL (0x000014) + +/* command list */ +#define SM501_COMMAND_LIST_STATUS (0x000024) + +/* interrupt debug */ +#define SM501_RAW_IRQ_STATUS (0x000028) +#define SM501_RAW_IRQ_CLEAR (0x000028) +#define SM501_IRQ_STATUS (0x00002C) +#define SM501_IRQ_MASK (0x000030) +#define SM501_DEBUG_CONTROL (0x000034) + +/* power management */ +#define SM501_CURRENT_GATE (0x000038) +#define SM501_CURRENT_CLOCK (0x00003C) +#define SM501_POWER_MODE_0_GATE (0x000040) +#define SM501_POWER_MODE_0_CLOCK (0x000044) +#define SM501_POWER_MODE_1_GATE (0x000048) +#define SM501_POWER_MODE_1_CLOCK (0x00004C) +#define SM501_SLEEP_MODE_GATE (0x000050) +#define SM501_POWER_MODE_CONTROL (0x000054) + +/* power gates for units within the 501 */ +#define SM501_GATE_HOST (0) +#define SM501_GATE_MEMORY (1) +#define SM501_GATE_DISPLAY (2) +#define SM501_GATE_2D_ENGINE (3) +#define SM501_GATE_CSC (4) +#define SM501_GATE_ZVPORT (5) +#define SM501_GATE_GPIO (6) +#define SM501_GATE_UART0 (7) +#define SM501_GATE_UART1 (8) +#define SM501_GATE_SSP (10) +#define SM501_GATE_USB_HOST (11) +#define SM501_GATE_USB_GADGET (12) +#define SM501_GATE_UCONTROLLER (17) +#define SM501_GATE_AC97 (18) + +/* panel clock */ +#define SM501_CLOCK_P2XCLK (24) +/* crt clock */ +#define SM501_CLOCK_V2XCLK (16) +/* main clock */ +#define SM501_CLOCK_MCLK (8) +/* SDRAM controller clock */ +#define SM501_CLOCK_M1XCLK (0) + +/* config 2 */ +#define SM501_PCI_MASTER_BASE (0x000058) +#define SM501_ENDIAN_CONTROL (0x00005C) +#define SM501_DEVICEID (0x000060) +/* 0x050100A0 */ + +#define SM501_PLLCLOCK_COUNT (0x000064) +#define SM501_MISC_TIMING (0x000068) +#define SM501_CURRENT_SDRAM_CLOCK (0x00006C) + +/* GPIO base */ +#define SM501_GPIO (0x010000) +#define SM501_GPIO_DATA_LOW (0x00) +#define SM501_GPIO_DATA_HIGH (0x04) +#define SM501_GPIO_DDR_LOW (0x08) +#define SM501_GPIO_DDR_HIGH (0x0C) +#define SM501_GPIO_IRQ_SETUP (0x10) +#define SM501_GPIO_IRQ_STATUS (0x14) +#define SM501_GPIO_IRQ_RESET (0x14) + +/* I2C controller base */ +#define SM501_I2C (0x010040) +#define SM501_I2C_BYTE_COUNT (0x00) +#define SM501_I2C_CONTROL (0x01) +#define SM501_I2C_STATUS (0x02) +#define SM501_I2C_RESET (0x02) +#define SM501_I2C_SLAVE_ADDRESS (0x03) +#define SM501_I2C_DATA (0x04) + +/* SSP base */ +#define SM501_SSP (0x020000) + +/* Uart 0 base */ +#define SM501_UART0 (0x030000) + +/* Uart 1 base */ +#define SM501_UART1 (0x030020) + +/* USB host port base */ +#define SM501_USB_HOST (0x040000) + +/* USB slave/gadget base */ +#define SM501_USB_GADGET (0x060000) + +/* USB slave/gadget data port base */ +#define SM501_USB_GADGET_DATA (0x070000) + +/* Display contoller/video engine base */ +#define SM501_DC (0x080000) + +/* common defines for the SM501 address registers */ +#define SM501_ADDR_FLIP (1<<31) +#define SM501_ADDR_EXT (1<<27) +#define SM501_ADDR_CS1 (1<<26) +#define SM501_ADDR_MASK (0x3f << 26) + +#define SM501_FIFO_MASK (0x3 << 16) +#define SM501_FIFO_1 (0x0 << 16) +#define SM501_FIFO_3 (0x1 << 16) +#define SM501_FIFO_7 (0x2 << 16) +#define SM501_FIFO_11 (0x3 << 16) + +/* common registers for panel and the crt */ +#define SM501_OFF_DC_H_TOT (0x000) +#define SM501_OFF_DC_V_TOT (0x008) +#define SM501_OFF_DC_H_SYNC (0x004) +#define SM501_OFF_DC_V_SYNC (0x00C) + +#define SM501_DC_PANEL_CONTROL (0x000) + +#define SM501_DC_PANEL_CONTROL_FPEN (1<<27) +#define SM501_DC_PANEL_CONTROL_BIAS (1<<26) +#define SM501_DC_PANEL_CONTROL_DATA (1<<25) +#define SM501_DC_PANEL_CONTROL_VDD (1<<24) +#define SM501_DC_PANEL_CONTROL_DP (1<<23) + +#define SM501_DC_PANEL_CONTROL_TFT_888 (0<<21) +#define SM501_DC_PANEL_CONTROL_TFT_333 (1<<21) +#define SM501_DC_PANEL_CONTROL_TFT_444 (2<<21) + +#define SM501_DC_PANEL_CONTROL_DE (1<<20) + +#define SM501_DC_PANEL_CONTROL_LCD_TFT (0<<18) +#define SM501_DC_PANEL_CONTROL_LCD_STN8 (1<<18) +#define SM501_DC_PANEL_CONTROL_LCD_STN12 (2<<18) + +#define SM501_DC_PANEL_CONTROL_CP (1<<14) +#define SM501_DC_PANEL_CONTROL_VSP (1<<13) +#define SM501_DC_PANEL_CONTROL_HSP (1<<12) +#define SM501_DC_PANEL_CONTROL_CK (1<<9) +#define SM501_DC_PANEL_CONTROL_TE (1<<8) +#define SM501_DC_PANEL_CONTROL_VPD (1<<7) +#define SM501_DC_PANEL_CONTROL_VP (1<<6) +#define SM501_DC_PANEL_CONTROL_HPD (1<<5) +#define SM501_DC_PANEL_CONTROL_HP (1<<4) +#define SM501_DC_PANEL_CONTROL_GAMMA (1<<3) +#define SM501_DC_PANEL_CONTROL_EN (1<<2) + +#define SM501_DC_PANEL_CONTROL_8BPP (0<<0) +#define SM501_DC_PANEL_CONTROL_16BPP (1<<0) +#define SM501_DC_PANEL_CONTROL_32BPP (2<<0) + + +#define SM501_DC_PANEL_PANNING_CONTROL (0x004) +#define SM501_DC_PANEL_COLOR_KEY (0x008) +#define SM501_DC_PANEL_FB_ADDR (0x00C) +#define SM501_DC_PANEL_FB_OFFSET (0x010) +#define SM501_DC_PANEL_FB_WIDTH (0x014) +#define SM501_DC_PANEL_FB_HEIGHT (0x018) +#define SM501_DC_PANEL_TL_LOC (0x01C) +#define SM501_DC_PANEL_BR_LOC (0x020) +#define SM501_DC_PANEL_H_TOT (0x024) +#define SM501_DC_PANEL_H_SYNC (0x028) +#define SM501_DC_PANEL_V_TOT (0x02C) +#define SM501_DC_PANEL_V_SYNC (0x030) +#define SM501_DC_PANEL_CUR_LINE (0x034) + +#define SM501_DC_VIDEO_CONTROL (0x040) +#define SM501_DC_VIDEO_FB0_ADDR (0x044) +#define SM501_DC_VIDEO_FB_WIDTH (0x048) +#define SM501_DC_VIDEO_FB0_LAST_ADDR (0x04C) +#define SM501_DC_VIDEO_TL_LOC (0x050) +#define SM501_DC_VIDEO_BR_LOC (0x054) +#define SM501_DC_VIDEO_SCALE (0x058) +#define SM501_DC_VIDEO_INIT_SCALE (0x05C) +#define SM501_DC_VIDEO_YUV_CONSTANTS (0x060) +#define SM501_DC_VIDEO_FB1_ADDR (0x064) +#define SM501_DC_VIDEO_FB1_LAST_ADDR (0x068) + +#define SM501_DC_VIDEO_ALPHA_CONTROL (0x080) +#define SM501_DC_VIDEO_ALPHA_FB_ADDR (0x084) +#define SM501_DC_VIDEO_ALPHA_FB_OFFSET (0x088) +#define SM501_DC_VIDEO_ALPHA_FB_LAST_ADDR (0x08C) +#define SM501_DC_VIDEO_ALPHA_TL_LOC (0x090) +#define SM501_DC_VIDEO_ALPHA_BR_LOC (0x094) +#define SM501_DC_VIDEO_ALPHA_SCALE (0x098) +#define SM501_DC_VIDEO_ALPHA_INIT_SCALE (0x09C) +#define SM501_DC_VIDEO_ALPHA_CHROMA_KEY (0x0A0) +#define SM501_DC_VIDEO_ALPHA_COLOR_LOOKUP (0x0A4) + +#define SM501_DC_PANEL_HWC_BASE (0x0F0) +#define SM501_DC_PANEL_HWC_ADDR (0x0F0) +#define SM501_DC_PANEL_HWC_LOC (0x0F4) +#define SM501_DC_PANEL_HWC_COLOR_1_2 (0x0F8) +#define SM501_DC_PANEL_HWC_COLOR_3 (0x0FC) + +#define SM501_HWC_EN (1<<31) + +#define SM501_OFF_HWC_ADDR (0x00) +#define SM501_OFF_HWC_LOC (0x04) +#define SM501_OFF_HWC_COLOR_1_2 (0x08) +#define SM501_OFF_HWC_COLOR_3 (0x0C) + +#define SM501_DC_ALPHA_CONTROL (0x100) +#define SM501_DC_ALPHA_FB_ADDR (0x104) +#define SM501_DC_ALPHA_FB_OFFSET (0x108) +#define SM501_DC_ALPHA_TL_LOC (0x10C) +#define SM501_DC_ALPHA_BR_LOC (0x110) +#define SM501_DC_ALPHA_CHROMA_KEY (0x114) +#define SM501_DC_ALPHA_COLOR_LOOKUP (0x118) + +#define SM501_DC_CRT_CONTROL (0x200) + +#define SM501_DC_CRT_CONTROL_TVP (1<<15) +#define SM501_DC_CRT_CONTROL_CP (1<<14) +#define SM501_DC_CRT_CONTROL_VSP (1<<13) +#define SM501_DC_CRT_CONTROL_HSP (1<<12) +#define SM501_DC_CRT_CONTROL_VS (1<<11) +#define SM501_DC_CRT_CONTROL_BLANK (1<<10) +#define SM501_DC_CRT_CONTROL_SEL (1<<9) +#define SM501_DC_CRT_CONTROL_TE (1<<8) +#define SM501_DC_CRT_CONTROL_PIXEL_MASK (0xF << 4) +#define SM501_DC_CRT_CONTROL_GAMMA (1<<3) +#define SM501_DC_CRT_CONTROL_ENABLE (1<<2) + +#define SM501_DC_CRT_CONTROL_8BPP (0<<0) +#define SM501_DC_CRT_CONTROL_16BPP (1<<0) +#define SM501_DC_CRT_CONTROL_32BPP (2<<0) + +#define SM501_DC_CRT_FB_ADDR (0x204) +#define SM501_DC_CRT_FB_OFFSET (0x208) +#define SM501_DC_CRT_H_TOT (0x20C) +#define SM501_DC_CRT_H_SYNC (0x210) +#define SM501_DC_CRT_V_TOT (0x214) +#define SM501_DC_CRT_V_SYNC (0x218) +#define SM501_DC_CRT_SIGNATURE_ANALYZER (0x21C) +#define SM501_DC_CRT_CUR_LINE (0x220) +#define SM501_DC_CRT_MONITOR_DETECT (0x224) + +#define SM501_DC_CRT_HWC_BASE (0x230) +#define SM501_DC_CRT_HWC_ADDR (0x230) +#define SM501_DC_CRT_HWC_LOC (0x234) +#define SM501_DC_CRT_HWC_COLOR_1_2 (0x238) +#define SM501_DC_CRT_HWC_COLOR_3 (0x23C) + +#define SM501_DC_PANEL_PALETTE (0x400) + +#define SM501_DC_VIDEO_PALETTE (0x800) + +#define SM501_DC_CRT_PALETTE (0xC00) + +/* Zoom Video port base */ +#define SM501_ZVPORT (0x090000) + +/* AC97/I2S base */ +#define SM501_AC97 (0x0A0000) + +/* 8051 micro controller base */ +#define SM501_UCONTROLLER (0x0B0000) + +/* 8051 micro controller SRAM base */ +#define SM501_UCONTROLLER_SRAM (0x0C0000) + +/* DMA base */ +#define SM501_DMA (0x0D0000) + +/* 2d engine base */ +#define SM501_2D_ENGINE (0x100000) +#define SM501_2D_SOURCE (0x00) +#define SM501_2D_DESTINATION (0x04) +#define SM501_2D_DIMENSION (0x08) +#define SM501_2D_CONTROL (0x0C) +#define SM501_2D_PITCH (0x10) +#define SM501_2D_FOREGROUND (0x14) +#define SM501_2D_BACKGROUND (0x18) +#define SM501_2D_STRETCH (0x1C) +#define SM501_2D_COLOR_COMPARE (0x20) +#define SM501_2D_COLOR_COMPARE_MASK (0x24) +#define SM501_2D_MASK (0x28) +#define SM501_2D_CLIP_TL (0x2C) +#define SM501_2D_CLIP_BR (0x30) +#define SM501_2D_MONO_PATTERN_LOW (0x34) +#define SM501_2D_MONO_PATTERN_HIGH (0x38) +#define SM501_2D_WINDOW_WIDTH (0x3C) +#define SM501_2D_SOURCE_BASE (0x40) +#define SM501_2D_DESTINATION_BASE (0x44) +#define SM501_2D_ALPHA (0x48) +#define SM501_2D_WRAP (0x4C) +#define SM501_2D_STATUS (0x50) + +#define SM501_CSC_Y_SOURCE_BASE (0xC8) +#define SM501_CSC_CONSTANTS (0xCC) +#define SM501_CSC_Y_SOURCE_X (0xD0) +#define SM501_CSC_Y_SOURCE_Y (0xD4) +#define SM501_CSC_U_SOURCE_BASE (0xD8) +#define SM501_CSC_V_SOURCE_BASE (0xDC) +#define SM501_CSC_SOURCE_DIMENSION (0xE0) +#define SM501_CSC_SOURCE_PITCH (0xE4) +#define SM501_CSC_DESTINATION (0xE8) +#define SM501_CSC_DESTINATION_DIMENSION (0xEC) +#define SM501_CSC_DESTINATION_PITCH (0xF0) +#define SM501_CSC_SCALE_FACTOR (0xF4) +#define SM501_CSC_DESTINATION_BASE (0xF8) +#define SM501_CSC_CONTROL (0xFC) + +/* 2d engine data port base */ +#define SM501_2D_ENGINE_DATA (0x110000) --- linux-2.6.19/include/linux/sm501.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19-simtec1p28/include/linux/sm501.h 2007-01-22 20:19:30.000000000 +0000 @@ -0,0 +1,164 @@ +/* include/linux/sm501.h + * + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks <be...@si...> + * Vincent Sanders <vi...@si...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +extern int sm501_unit_power(struct device *dev, + unsigned int unit, unsigned int to); + +extern unsigned long sm501_set_clock(struct device *dev, + int clksrc, unsigned long freq); + +extern unsigned long sm501_find_clock(int clksrc, unsigned long req_freq); + +/* sm501_misc_control + * + * Modify the SM501's MISC_CONTROL register +*/ + +extern int sm501_misc_control(struct device *dev, + unsigned long set, unsigned long clear); + +/* sm501_modify_reg + * + * Modify a register in the SM501 which may be shared with other + * drivers. +*/ + +extern unsigned long sm501_modify_reg(struct device *dev, + unsigned long reg, + unsigned long set, + unsigned long clear); + +/* sm501_gpio_set + * + * set the state of the given GPIO line +*/ + +extern void sm501_gpio_set(struct device *dev, + unsigned long gpio, + unsigned int to, + unsigned int dir); + +/* sm501_gpio_get + * + * get the state of the given GPIO line +*/ + +extern unsigned long sm501_gpio_get(struct device *dev, + unsigned long gpio); + + +/* Platform data definitions */ + +#define SM501FB_FLAG_USE_INIT_MODE (1<<0) +#define SM501FB_FLAG_DISABLE_AT_EXIT (1<<1) +#define SM501FB_FLAG_USE_HWCURSOR (1<<2) +#define SM501FB_FLAG_USE_HWACCEL (1<<3) + +struct sm501_platdata_fbsub { + struct fb_mode *def_mode; + unsigned long max_mem; + unsigned int flags; +}; + +enum sm501_fb_routing { + SM501_FB_OWN = 0, /* CRT=>CRT, Panel=>Panel */ + SM501_FB_CRT_PANEL = 1, /* Panel=>CRT, Panel=>Panel */ +}; + +/* sm501_platdata_fb + * + * configuration data for the framebuffer driver +*/ + +struct sm501_platdata_fb { + enum sm501_fb_routing fb_route; + struct sm501_platdata_fbsub *fb_crt; + struct sm501_platdata_fbsub *fb_pnl; +}; + +/* gpio i2c */ + +struct sm501_platdata_gpio_i2c { + unsigned int pin_sda; + unsigned int pin_scl; +}; + +/* sm501_initdata + * + * use for initialising values that may not have been setup + * before the driver is loaded. +*/ + +struct sm501_reg_init { + unsigned long set; + unsigned long mask; +}; + +#define SM501_USE_USB_HOST (1<<0) +#define SM501_USE_USB_SLAVE (1<<1) +#define SM501_USE_SSP0 (1<<2) +#define SM501_USE_SSP1 (1<<3) +#define SM501_USE_UART0 (1<<4) +#define SM501_USE_UART1 (1<<5) +#define SM501_USE_FBACCEL (1<<6) +#define SM501_USE_AC97 (1<<7) +#define SM501_USE_I2S (1<<8) + +#define SM501_USE_ALL (0xffffffff) + +struct sm501_initdata { + struct sm501_reg_init gpio_low; + struct sm501_reg_init gpio_high; + struct sm501_reg_init misc_timing; + struct sm501_reg_init misc_control; + + unsigned long devices; + unsigned long mclk; /* non-zero to modify */ + unsigned long m1xclk; /* non-zero to modify */ +}; + +/* sm501_init_gpio + * + * default gpio settings +*/ + +struct sm501_init_gpio { + struct sm501_reg_init gpio_data_low; + struct sm501_reg_init gpio_data_high; + struct sm501_reg_init gpio_ddr_low; + struct sm501_reg_init gpio_ddr_high; +}; + +/* sm501_platdata + * + * This is passed with the platform device to allow the board + * to control the behaviour of the SM501 driver(s) which attach + * to the device. + * +*/ + +struct sm501_platdata { + struct sm501_initdata *init; + struct sm501_init_gpio *init_gpiop; + struct sm501_platdata_fb *fb; + + struct sm501_platdata_gpio_i2c *gpio_i2c; + unsigned int gpio_i2c_nr; +}; |
From: Manuel L. <ma...@ro...> - 2007-01-23 15:13:58
|
> This is the new MFD patch, which should be re-posted > soon to the linux-kernel list for looking at 2.6.20. Thank you, works very well on my SH-4 hardware! -- ml. |
From: Clemens K. <cle...@an...> - 2006-10-06 17:25:58
|
Hello, Bill, > Sorry about starting a new thread, but I was referred to this > list/thread by a colleague just a few minutes ago. (OT: it isn't in the > archives in SF, either!). Okay, fine! I'll be glad to join the party because we are using the SM501 on PCI of an MPC8540 embedded PowerPC. Searching the list archives, about ten people have had (at least) some hands on the SM501 during the last year. I also got several questions about the driver status off list. > I hate to play "me too!", but I also have an SM501 framebuffer driver in > a partial state of development. I'd love to collaborate with others to > get a good version pushed upstream. Great! Where shall we collect the code? I have some - a working fb driver kernel module (I did lots of code cleanup, but still needs a lot) - older Xfree86 driver (partially accelerated!?, never tried but code looks good) - some collected MMCC (Multi Media Companion Chip) drivers/snippets for the other periperals besides the graphics part. - kernel code for an functional complete(?) SM501 implementation for an RTS7751R2D Renesas SH-4 Board running Linux. I am working on the latest 2.6.18 Kernels on PowerPC. But I can also test the SM501 on PCI in an x86 since we have this PCI Demo Board with an SM501G8 AA. On our PPC we use the SM501GF8 AB Chip Revision (F means: RoHS Version). I also mirrored the Silicon Motion's FTP server with lots of Datasheets and reference designs. > One of the challenges I've encountered with this chip is dealing with > all the "other stuff" it has onboard. In my driver, I addressed that by > creating a whole new bus similar to what Nico did for the UCB1400. I > was in the process of getting AC97 working when I figured out that that > part of the chip is so braindead, it isn't worth using--- at least on > ARM platforms that don't have DMA (which was my case). Oh, I will need to get the AC97 working, too :-/ (my hardware is more or less ready to test some software) > In an upcoming project I need to use the USBH, so I'll be revisiting > this issue again soon. I have also cc'd others who have worked with me > on this chip. > > I'd be happy to post code if that makes sense. Or to host a CVS repo or > something. Can you host a CVS repo at your place? I don't like SF much. Best greets, Clemens Koller _______________________________ R&D Imaging Devices Anagramm GmbH Rupert-Mayer-Str. 45/1 81379 Muenchen Germany http://www.anagramm.de Phone: +49-89-741518-50 Fax: +49-89-741518-19 |
From: Bill G. <bg...@bi...> - 2006-10-06 18:04:17
|
Clemens: Clemens Koller wrote: > > Great! Where shall we collect the code? I have a co-lo machine that has space on it. I can set up a user account "sm501" and a CVS repository, but I'm such a lousy sysadmin, I don't know how to give others access to the repository without giving them all the login to the "sm501" account. Can someone coach me through it? I just created the account now. b.g. -- Bill Gatliff bg...@bi... |
From: Alex D. <ale...@gm...> - 2006-10-06 18:14:22
|
On 10/6/06, Clemens Koller <cle...@an...> wrote: > Hello, Bill, > > > Sorry about starting a new thread, but I was referred to this > > list/thread by a colleague just a few minutes ago. (OT: it isn't in the > > archives in SF, either!). > > Okay, fine! I'll be glad to join the party because we are using the SM501 > on PCI of an MPC8540 embedded PowerPC. > > Searching the list archives, about ten people have had (at least) some > hands on the SM501 during the last year. I also got several questions about > the driver status off list. > > > I hate to play "me too!", but I also have an SM501 framebuffer driver in > > a partial state of development. I'd love to collaborate with others to > > get a good version pushed upstream. > > Great! Where shall we collect the code? > I have some > > - a working fb driver kernel module > (I did lots of code cleanup, but still needs a lot) > > - older Xfree86 driver (partially accelerated!?, never tried but code looks good) > As I mentioned earlier, with the exception of dualhead, I've got a working xorg smi501 driver. I'll be adding it to xorg git once I get dualhead working and a few other odds and ends worked out. Alex > - some collected MMCC (Multi Media Companion Chip) drivers/snippets > for the other periperals besides the graphics part. > > - kernel code for an functional complete(?) SM501 implementation for > an RTS7751R2D Renesas SH-4 Board running Linux. > > I am working on the latest 2.6.18 Kernels on PowerPC. > But I can also test the SM501 on PCI in an x86 since we have > this PCI Demo Board with an SM501G8 AA. > On our PPC we use the SM501GF8 AB Chip Revision (F means: RoHS Version). > > I also mirrored the Silicon Motion's FTP server with lots of > Datasheets and reference designs. > > > One of the challenges I've encountered with this chip is dealing with > > all the "other stuff" it has onboard. In my driver, I addressed that by > > creating a whole new bus similar to what Nico did for the UCB1400. I > > was in the process of getting AC97 working when I figured out that that > > part of the chip is so braindead, it isn't worth using--- at least on > > ARM platforms that don't have DMA (which was my case). > > Oh, I will need to get the AC97 working, too :-/ > (my hardware is more or less ready to test some software) > > > In an upcoming project I need to use the USBH, so I'll be revisiting > > this issue again soon. I have also cc'd others who have worked with me > > on this chip. > > > > I'd be happy to post code if that makes sense. Or to host a CVS repo or > > something. > > Can you host a CVS repo at your place? I don't like SF much. > > Best greets, > > Clemens Koller > _______________________________ > R&D Imaging Devices > Anagramm GmbH > Rupert-Mayer-Str. 45/1 > 81379 Muenchen > Germany > > http://www.anagramm.de > Phone: +49-89-741518-50 > Fax: +49-89-741518-19 > > > ------------------------------------------------------------------------- > Take Surveys. Earn Cash. Influence the Future of IT > Join SourceForge.net's Techsay panel and you'll get the chance to share your > opinions on IT & business topics through brief surveys -- and earn cash > http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV > _______________________________________________ > Linux-fbdev-devel mailing list > Lin...@li... > https://lists.sourceforge.net/lists/listinfo/linux-fbdev-devel > |
From: Ville <sy...@sc...> - 2006-10-06 18:55:44
|
On Fri, Oct 06, 2006 at 07:25:54PM +0200, Clemens Koller wrote: > Hello, Bill, >=20 > > Sorry about starting a new thread, but I was referred to this=20 > > list/thread by a colleague just a few minutes ago. (OT: it isn't in = the=20 > > archives in SF, either!). >=20 > Okay, fine! I'll be glad to join the party because we are using the SM5= 01 > on PCI of an MPC8540 embedded PowerPC. I'm using an ARM board. The CPU is an Atmel AT91RM9200, and the SM501 is=20 connected to it via a CPLD. > > I hate to play "me too!", but I also have an SM501 framebuffer driver= in=20 > > a partial state of development. I'd love to collaborate with others = to=20 > > get a good version pushed upstream. >=20 > Great! Where shall we collect the code? > I have some=20 >=20 > - a working fb driver kernel module > (I did lots of code cleanup, but still needs a lot) Me too here :) My changes to the original sm501 fb driver include: - Use dev_dbg instead of custom macro - video=3D command line parsing - Better checking in check_var - Started to implement setcolreg and panning, not finished - Removed duplicated code - Tried to make code more readable :) - Maybe something else (I didn't keep track I also started to write an ALSA driver for the audio part. I wrote the=20 basic driver skeleton, and my next step was going to be the 8051, but=20 then I got distracted by other stuff. My plan was to start off with some=20 very simple code for the 8051 which would just issue an interrupt at=20 some interval so I could just see how the CPU<->8051 communication=20 works. I'll try dig up the code in case someone wants to take a look. Like I mentioned in the other thread I also wrote a DirectFB gfxdriver.=20 It does basic blitting and filling for now. I tried to make it draw=20 lines too but that caused the chip to lock up, and I tried to use alpha=20 bleded blits but those seem to make the chip really sluggish to respond. --=20 Ville Syrj=E4l=E4 sy...@sc... http://www.sci.fi/~syrjala/ |
From: Bill G. <bg...@bi...> - 2006-10-06 19:03:43
|
Clemens: Clemens Koller wrote: > > If you want I can spend an hour on that stuff if you give me an > account on your machine. Done. See your Inbox. I think what makes sense is to have a public_html directory with subdirectories for each contribution that's been mentioned so far. Then we can collaborate on a CVS repository that blends the best bits of all the contributed code into one place. Any volunteers? :) b.g. -- Bill Gatliff bg...@bi... |
From: Paul M. <le...@li...> - 2006-10-13 12:12:05
|
On Fri, Oct 06, 2006 at 07:25:54PM +0200, Clemens Koller wrote: > Great! Where shall we collect the code? > I have some > > - a working fb driver kernel module > (I did lots of code cleanup, but still needs a lot) > There's at least half a dozen of these floating around, with some disagreement on the actual implementation. We had one in the old SH tree that specifically registered a different framebuffer device for each plane, which we had some code in DirectFB and mplayer for making use of for overlays and so on. There's also another framebuffer driver for this in the Linux/MIPS tree that's reasonably clean and might make a better starting point. The main issue is supporting both PCI and platform device handling from the same driver, since there seem to be just as many in both configurations floating around. > - kernel code for an functional complete(?) SM501 implementation for > an RTS7751R2D Renesas SH-4 Board running Linux. > It's largely complete, yes, but most of the code is fairly messy, or against outdated APIs. It might make a good reference point, but little else. The OHCI and i2c drivers are probably the "cleanest" out of these, though I haven't gotten around to tidying them up and merging them in to mainline as of yet. It might be beneficial to get these in to your sm5xx tree though, before we end up having even more duplication (it seems there already is with i2c..). The OHCI thing was a bit of a mess since the DMA requests need to be in the 8051 address space via the accessible SRAM there. You can see our lame allocator for that in arch/sh/cchips/voyagergx/consistent.c. > Oh, I will need to get the AC97 working, too :-/ > (my hardware is more or less ready to test some software) > We also have an OSS driver for SM501, but it contains a lot of 8051 microcode that needs to be shoved in to userspace, and it needs to be reworked for ALSA. It should be fairly trivial to rework this for the mfd approach and work from there, which I'll look at doing if no one beats me to it and I finish some more pressing matters first. |
From: Andrey V. <av...@va...> - 2006-10-13 13:12:16
|
Hi Paul, Paul Mundt wrote: > On Fri, Oct 06, 2006 at 07:25:54PM +0200, Clemens Koller wrote: >> Great! Where shall we collect the code? >> I have some >> >> - a working fb driver kernel module >> (I did lots of code cleanup, but still needs a lot) >> > There's at least half a dozen of these floating around, with some > disagreement on the actual implementation. We had one in the old SH tree > that specifically registered a different framebuffer device for each > plane, which we had some code in DirectFB and mplayer for making use of > for overlays and so on. IMHO, its very awkward implementation for real world application, especially since, as ex, alpha/video planes is coupled with panel plane but not with CRT plane. We currently try use sm501 as two fb (crt/panel) devices. > > There's also another framebuffer driver for this in the Linux/MIPS tree > that's reasonably clean and might make a better starting point. The main > issue is supporting both PCI and platform device handling from the same > driver, since there seem to be just as many in both configurations > floating around. > PCI and MMIO modes of SM501 is mutually exclusive, so it should be selected at configure time (as I already does, check my implementation in svn). >> - kernel code for an functional complete(?) SM501 implementation for >> an RTS7751R2D Renesas SH-4 Board running Linux. >> > It's largely complete, yes, but most of the code is fairly messy, or > against outdated APIs. It might make a good reference point, but little > else. The OHCI and i2c drivers are probably the "cleanest" out of these, > though I haven't gotten around to tidying them up and merging them in to > mainline as of yet. It might be beneficial to get these in to your sm5xx > tree though, before we end up having even more duplication (it seems > there already is with i2c..). > > The OHCI thing was a bit of a mess since the DMA requests need to be in > the 8051 address space via the accessible SRAM there. You can see our > lame allocator for that in arch/sh/cchips/voyagergx/consistent.c. > >> Oh, I will need to get the AC97 working, too :-/ >> (my hardware is more or less ready to test some software) >> > We also have an OSS driver for SM501, but it contains a lot of 8051 > microcode that needs to be shoved in to userspace, and it needs to be > reworked for ALSA. > > It should be fairly trivial to rework this for the mfd approach and work > from there, which I'll look at doing if no one beats me to it and I > finish some more pressing matters first. Nice, but please, do it vs berlios svn. P.S. I repeat my invitation, sm5xx svn/maillists is up and ready. Andrey |
From: Paul M. <le...@li...> - 2006-10-13 13:36:48
|
On Fri, Oct 13, 2006 at 05:12:04PM +0400, Andrey Volkov wrote: > Paul Mundt wrote: > > There's at least half a dozen of these floating around, with some > > disagreement on the actual implementation. We had one in the old SH tree > > that specifically registered a different framebuffer device for each > > plane, which we had some code in DirectFB and mplayer for making use of > > for overlays and so on. > IMHO, its very awkward implementation for real world application, > especially since, as ex, alpha/video planes is coupled with panel plane > but not with CRT plane. We currently try use sm501 as two fb (crt/panel) > devices. > Awkward as it may be, there are plenty of users going with this approach, and DirectFB has been using it for quite some time. If you don't care for this approach, do you have a better idea for exposing individual planes to userspace and fine-grained overlay control? Multiple device nodes are a lot more pleasant to look at than an ioctl abortion. > PCI and MMIO modes of SM501 is mutually exclusive, so it should be > selected at configure time (as I already does, check my implementation > in svn). > The driver still requires visibility of these two things. Looking at the fb driver, it's clear you have no transparent means for gauging the aperture size. With PCI this comes down to sizing the membar, and in the platform case it can be statically defined at registration time too. This will never work on a system where you have a platform bus SM501 with an SM501 on a PCI add-in board, you simply can not gaurantee that they are exclusive. |
From: Bill G. <bg...@bi...> - 2006-10-15 17:34:57
|
Paul Mundt wrote: > > We also have an OSS driver for SM501, but it contains a lot of 8051 > microcode that needs to be shoved in to userspace, and it needs to be > reworked for ALSA. > Hi Paul! Long time no chat! :) One thing that concerns me about all of the ALSA implementations I've seen so far is that the source code for the 8051 side is never included. GPL questions aside, for legacy reasons alone we've got to make sure that stuff gets in too. But then again, there's the question of where the source code is at all, and what toolsystem you use to produce the binary image... b.g. -- Bill Gatliff bg...@bi... |
From: Paul M. <le...@li...> - 2006-10-16 03:02:19
|
Hi Bill, fancy seeing you in this thread ;-) On Sun, Oct 15, 2006 at 12:34:28PM -0500, William A. Gatliff wrote: > One thing that concerns me about all of the ALSA implementations I've > seen so far is that the source code for the 8051 side is never > included. GPL questions aside, for legacy reasons alone we've got to > make sure that stuff gets in too. > We want the 8051 blob in userspace if anything, there's little reason to tie this in to the module specifically, and it's handy to expose the interface for people that are specifically interested in loading in alternate code on the 8051. As far as including source for the 8051 code itself, that would be nice to have, but I doubt it's something the majority of users are going to care about. I've certainly never seen any. > But then again, there's the question of where the source code is at all, > and what toolsystem you use to produce the binary image... > I suspect it's just a simple binary blob and little else, there's certainly no infrastructure in the loading side for dealing with anything more elaborate, and it is just a stupid 8051 after all. Taking and disassembling the thing will likely get you as close as to what the real source looks like as anything else, but again, most people aren't going to have a use for this. Kbuild also doesn't have a simple facility for mapping in multiple cross compilers depending on target preference, which is one of the things we hit when trying to figure out how to interface the ARM7 firmware build for the AICA on the Dreamcast (prior to the request_firmware() interface being added). This is now handled by pushing everything to userspace, which seems the obvious choice for the 8051, too. On that note, do you have a pointer to some of the various ALSA implementations that are floating around? I've only seen the OSS driver we had in CVS, and that provides a rather dysmal starting point.. |
From: Bill G. <bg...@bi...> - 2006-10-16 19:27:47
|
Paul: Paul Mundt wrote: >Hi Bill, fancy seeing you in this thread ;-) > > Heh, you need to talk to the guy at the door. He'll let anyone in, I'm proof! :) >We want the 8051 blob in userspace if anything, there's little reason to >tie this in to the module specifically, and it's handy to expose the >interface for people that are specifically interested in loading in >alternate code on the 8051. > >As far as including source for the 8051 code itself, that would be nice >to have, but I doubt it's something the majority of users are going to >care about. I've certainly never seen any. > > I totally agree on all points. But I hate blobs. If the source code is around, I'd love to have it recorded somewhere even though we all know that Kbuild and most users won't know what to do with it. Just In Case. Do we even know where this blob came from originally? >On that note, do you have a pointer to some of the various ALSA >implementations that are floating around? I've only seen the OSS driver >we had in CVS, and that provides a rather dysmal starting point.. > > Whaley sent me one just last week (hi Robert!). Given that, I don't suppose he'd mind my forwarding the patch on. What makes it tricky to do so is that the patch is against his highly-evolved sm501 driver stack, so there will be some mending required. Yet another reason why we've all got to get all this stuff together. The SM501 is a decent chip, and there's too few of us to go around re-inventing the wheel. b.g. -- Bill Gatliff bg...@bi... |
From: Robert W. <rw...@ap...> - 2006-10-16 19:40:08
Attachments:
sm501_8051_alsa.diff
|
Bill Gatliff wrote: > Paul: > > Paul Mundt wrote: > >> Hi Bill, fancy seeing you in this thread ;-) >> >> > > Heh, you need to talk to the guy at the door. He'll let anyone in, I'm > proof! :) > >> We want the 8051 blob in userspace if anything, there's little reason to >> tie this in to the module specifically, and it's handy to expose the >> interface for people that are specifically interested in loading in >> alternate code on the 8051. >> >> As far as including source for the 8051 code itself, that would be nice >> to have, but I doubt it's something the majority of users are going to >> care about. I've certainly never seen any. >> >> > > I totally agree on all points. But I hate blobs. If the source code is > around, I'd love to have it recorded somewhere even though we all know > that Kbuild and most users won't know what to do with it. Just In Case. > > Do we even know where this blob came from originally? > >> On that note, do you have a pointer to some of the various ALSA >> implementations that are floating around? I've only seen the OSS driver >> we had in CVS, and that provides a rather dysmal starting point.. >> >> > > Whaley sent me one just last week (hi Robert!). Given that, I don't > suppose he'd mind my forwarding the patch on. Not at all. BTW: we use this (moderately buggy) compiler to compile the 8051 code: http://sourceforge.net/project/showfiles.php?group_id=599 Between the bugs in the compiler and the absence of documentation in the SM501 manual about 8051 interrupts, etc. It was pretty difficult making this work. Don't try anything remotely like this with this compiler: u8 a, b; u32 c; c |= a << b; That's why all the registers are u8 instead of u32. > What makes it tricky to > do so is that the patch is against his highly-evolved sm501 driver > stack, so there will be some mending required. > > Yet another reason why we've all got to get all this stuff together. > The SM501 is a decent chip, and there's too few of us to go around > re-inventing the wheel. > > > > b.g. > -- Robert Whaley Applied Data Systems www.applieddata.net 434-244-9504 rw...@ap... |
From: Bill G. <bg...@bi...> - 2006-10-16 19:47:54
|
Robert Whaley wrote: > Bill Gatliff wrote: > >> Paul: >> >> Paul Mundt wrote: >> >>> Hi Bill, fancy seeing you in this thread ;-) >>> >>> >> >> Heh, you need to talk to the guy at the door. He'll let anyone in, >> I'm proof! :) >> >>> We want the 8051 blob in userspace if anything, there's little >>> reason to >>> tie this in to the module specifically, and it's handy to expose the >>> interface for people that are specifically interested in loading in >>> alternate code on the 8051. >>> >>> As far as including source for the 8051 code itself, that would be nice >>> to have, but I doubt it's something the majority of users are going to >>> care about. I've certainly never seen any. >>> >>> >> >> I totally agree on all points. But I hate blobs. If the source code >> is around, I'd love to have it recorded somewhere even though we all >> know that Kbuild and most users won't know what to do with it. Just >> In Case. >> >> Do we even know where this blob came from originally? >> >>> On that note, do you have a pointer to some of the various ALSA >>> implementations that are floating around? I've only seen the OSS driver >>> we had in CVS, and that provides a rather dysmal starting point.. >>> >>> >> >> Whaley sent me one just last week (hi Robert!). Given that, I don't >> suppose he'd mind my forwarding the patch on. > > > Not at all. BTW: we use this (moderately buggy) compiler to compile > the 8051 code: > > http://sourceforge.net/project/showfiles.php?group_id=599 > > Between the bugs in the compiler and the absence of documentation in > the SM501 manual about 8051 interrupts, etc. It was pretty difficult > making this work. > > Don't try anything remotely like this with this compiler: > > u8 a, b; > u32 c; > > c |= a << b; > > WTF?! :) And here I was thinking that SDCC was probably pretty good... >+ >+/* There are 2 copies of shared because the shared SRAM is not safe if >+ * the 8051 is reading/writing to a location while the CPU is >+ * writing/reading the same location (according to the SM501 manual >+ * and verified by tests). So 2 redundant copies are maintained and >+ * for the contents to be considered valid the same data must be >+ * present in both copies. >+ */ > > Classic SM501. "Here's a feature that works.... but it doesn't!" :) b.g. -- Bill Gatliff bg...@bi... |
From: Ben D. <ben...@fl...> - 2006-10-06 16:27:15
|
On Fri, Oct 06, 2006 at 11:11:46AM -0500, Bill Gatliff wrote: > Guys: > > > Sorry about starting a new thread, but I was referred to this > list/thread by a colleague just a few minutes ago. (OT: it isn't in the > archives in SF, either!). > > I hate to play "me too!", but I also have an SM501 framebuffer driver in > a partial state of development. I'd love to collaborate with others to > get a good version pushed upstream. > > One of the challenges I've encountered with this chip is dealing with > all the "other stuff" it has onboard. In my driver, I addressed that by > creating a whole new bus similar to what Nico did for the UCB1400. I > was in the process of getting AC97 working when I figured out that that > part of the chip is so braindead, it isn't worth using--- at least on > ARM platforms that don't have DMA (which was my case). > > In an upcoming project I need to use the USBH, so I'll be revisiting > this issue again soon. I have also cc'd others who have worked with me > on this chip. > > I'd be happy to post code if that makes sense. Or to host a CVS repo or > something. We have a solution based on an central device in drivers/mfd which creates a set of sub-devices that are used by the invidicual drivers -- Ben (be...@fl..., http://www.fluff.org/) 'a smiley only costs 4 bytes' |
From: Bill G. <bg...@bi...> - 2006-10-06 19:19:34
|
Ben Dooks wrote: >On Fri, Oct 06, 2006 at 11:11:46AM -0500, Bill Gatliff wrote: > >We have a solution based on an central device in drivers/mfd >which creates a set of sub-devices that are used by the invidicual >drivers > > Yes, I've seen your code. My SM501 code does something very similar. I don't know if there's any opportunity for reuse or to create a "standard", given that the SM501 is all over the place functionality (USB, AC97, framebuffer, UART, ...)-wise and bus (PCI, MMIO)-wise. But the general idea of your approach is definitely the right way to go. b.g. -- Bill Gatliff bg...@bi... |
From: Andrey V. <av...@va...> - 2006-10-10 06:01:14
|
Ben Dooks wrote: > On Fri, Oct 06, 2006 at 11:11:46AM -0500, Bill Gatliff wrote: >> Guys: >> >> >> Sorry about starting a new thread, but I was referred to this >> list/thread by a colleague just a few minutes ago. (OT: it isn't in the >> archives in SF, either!). >> >> I hate to play "me too!", but I also have an SM501 framebuffer driver in >> a partial state of development. I'd love to collaborate with others to >> get a good version pushed upstream. >> >> One of the challenges I've encountered with this chip is dealing with >> all the "other stuff" it has onboard. In my driver, I addressed that by >> creating a whole new bus similar to what Nico did for the UCB1400. I >> was in the process of getting AC97 working when I figured out that that >> part of the chip is so braindead, it isn't worth using--- at least on >> ARM platforms that don't have DMA (which was my case). >> >> In an upcoming project I need to use the USBH, so I'll be revisiting >> this issue again soon. I have also cc'd others who have worked with me >> on this chip. >> >> I'd be happy to post code if that makes sense. Or to host a CVS repo or >> something. > > We have a solution based on an central device in drivers/mfd > which creates a set of sub-devices that are used by the invidicual > drivers > We too ;), and use on our board: http://www.varma-el.com/en/prod?cls=sbc Anyone have objections about berlios.de SVN for a sm501 staff? Andrey |
From: Bill G. <bg...@bi...> - 2006-10-10 12:06:15
|
Andrey: Andrey Volkov wrote: >We too ;), and use on our board: http://www.varma-el.com/en/prod?cls=sbc > >Anyone have objections about berlios.de SVN for a sm501 staff? > > Works for me! :) Please post the URL when you have it. b.g. -- Bill Gatliff bg...@bi... |
From: Clemens K. <cle...@an...> - 2006-10-11 15:17:12
|
Hello, Andrey! >>We too ;), and use on our board: http://www.varma-el.com/en/prod?cls=sbc >> >>Anyone have objections about berlios.de SVN for a sm501 staff? Good start! I am with berlios.de, too. For all the SM501 interested developers, please see: https://developer.berlios.de/projects/sm5xx/ Best greets, Clemens Koller _______________________________ R&D Imaging Devices Anagramm GmbH Rupert-Mayer-Str. 45/1 81379 Muenchen Germany http://www.anagramm.de Phone: +49-89-741518-50 Fax: +49-89-741518-19 |
From: Clemens K. <cle...@an...> - 2006-10-13 13:12:20
|
Hello, Paul! Thank you for your comments! I'll have to dig through the code to see of what we can use there. Since I don't want to re-invent the wheel, I am curious how we get the driver cross-platform'ish. Hmm... no big ideas yet. > It should be fairly trivial to rework this for the mfd approach and work > from there, which I'll look at doing if no one beats me to it and I > finish some more pressing matters first. The MultiFunctionDevice (mfd) approach looks good to me. I'll be glad to have you onboard! :-) Best greets, Clemens Koller _______________________________ R&D Imaging Devices Anagramm GmbH Rupert-Mayer-Str. 45/1 81379 Muenchen Germany http://www.anagramm.de Phone: +49-89-741518-50 Fax: +49-89-741518-19 |
From: Andrey V. <av...@va...> - 2006-10-13 13:18:20
|
Hi, Clemens Clemens Koller wrote: > Hello, Paul! > > Thank you for your comments! I'll have to dig through the code to > see of what we can use there. > Since I don't want to re-invent the wheel, I am curious how we get the > driver cross-platform'ish. Hmm... no big ideas yet. > >> It should be fairly trivial to rework this for the mfd approach and work >> from there, which I'll look at doing if no one beats me to it and I >> finish some more pressing matters first. > > The MultiFunctionDevice (mfd) approach looks good to me. > I'll be glad to have you onboard! :-) > As I already wrote before, check sm5xx svn ;). And may be continue this this thread in directly sm5xx-devel? Andrey |