Skip to content

Commit

Permalink
powerpc: Add DIU platform code for MPC8610HPCD
Browse files Browse the repository at this point in the history
Add platform code to support Freescale DIU.  The platform code includes
framebuffer memory allocation, pixel format, monitor port, etc.

Signed-off-by: York Sun <yorksun@freescale.com>
Signed-off-by: Timur Tabi <timur@freescale.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
York Sun authored and Linus Torvalds committed Apr 28, 2008
1 parent 9b53a9e commit 6f90a8b
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 9 deletions.
190 changes: 181 additions & 9 deletions arch/powerpc/platforms/86xx/mpc8610_hpcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
*
* Initial author: Xianghua Xiao <x.xiao@freescale.com>
* Recode: Jason Jin <jason.jin@freescale.com>
* York Sun <yorksun@freescale.com>
*
* Rewrite the interrupt routing. remove the 8259PIC support,
* All the integrated device in ULI use sideband interrupt.
*
* Copyright 2007 Freescale Semiconductor Inc.
* Copyright 2008 Freescale Semiconductor Inc.
*
* 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
Expand Down Expand Up @@ -38,6 +39,8 @@
#include <sysdev/fsl_pci.h>
#include <sysdev/fsl_soc.h>

static unsigned char *pixis_bdcfg0, *pixis_arch;

static struct of_device_id __initdata mpc8610_ids[] = {
{ .compatible = "fsl,mpc8610-immr", },
{}
Expand All @@ -52,8 +55,7 @@ static int __init mpc8610_declare_of_platform_devices(void)
}
machine_device_initcall(mpc86xx_hpcd, mpc8610_declare_of_platform_devices);

static void __init
mpc86xx_hpcd_init_irq(void)
static void __init mpc86xx_hpcd_init_irq(void)
{
struct mpic *mpic1;
struct device_node *np;
Expand Down Expand Up @@ -161,12 +163,159 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x5229, quirk_uli5229);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, 0x5288, final_uli5288);
#endif /* CONFIG_PCI */

static void __init
mpc86xx_hpcd_setup_arch(void)
#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)

static u32 get_busfreq(void)
{
#ifdef CONFIG_PCI
struct device_node *np;
struct device_node *node;

u32 fs_busfreq = 0;
node = of_find_node_by_type(NULL, "cpu");
if (node) {
unsigned int size;
const unsigned int *prop =
of_get_property(node, "bus-frequency", &size);
if (prop)
fs_busfreq = *prop;
of_node_put(node);
};
return fs_busfreq;
}

unsigned int mpc8610hpcd_get_pixel_format(unsigned int bits_per_pixel,
int monitor_port)
{
static const unsigned long pixelformat[][3] = {
{0x88882317, 0x88083218, 0x65052119},
{0x88883316, 0x88082219, 0x65053118},
};
unsigned int pix_fmt, arch_monitor;

arch_monitor = ((*pixis_arch == 0x01) && (monitor_port == 0))? 0 : 1;
/* DVI port for board version 0x01 */

if (bits_per_pixel == 32)
pix_fmt = pixelformat[arch_monitor][0];
else if (bits_per_pixel == 24)
pix_fmt = pixelformat[arch_monitor][1];
else if (bits_per_pixel == 16)
pix_fmt = pixelformat[arch_monitor][2];
else
pix_fmt = pixelformat[1][0];

return pix_fmt;
}

void mpc8610hpcd_set_gamma_table(int monitor_port, char *gamma_table_base)
{
int i;
if (monitor_port == 2) { /* dual link LVDS */
for (i = 0; i < 256*3; i++)
gamma_table_base[i] = (gamma_table_base[i] << 2) |
((gamma_table_base[i] >> 6) & 0x03);
}
}

void mpc8610hpcd_set_monitor_port(int monitor_port)
{
static const u8 bdcfg[] = {0xBD, 0xB5, 0xA5};
if (monitor_port < 3)
*pixis_bdcfg0 = bdcfg[monitor_port];
}

void mpc8610hpcd_set_pixel_clock(unsigned int pixclock)
{
u32 __iomem *clkdvdr;
u32 temp;
/* variables for pixel clock calcs */
ulong bestval, bestfreq, speed_ccb, minpixclock, maxpixclock;
ulong pixval;
long err;
int i;

clkdvdr = ioremap(get_immrbase() + 0xe0800, sizeof(u32));
if (!clkdvdr) {
printk(KERN_ERR "Err: can't map clock divider register!\n");
return;
}

/* Pixel Clock configuration */
pr_debug("DIU: Bus Frequency = %d\n", get_busfreq());
speed_ccb = get_busfreq();

/* Calculate the pixel clock with the smallest error */
/* calculate the following in steps to avoid overflow */
pr_debug("DIU pixclock in ps - %d\n", pixclock);
temp = 1000000000/pixclock;
temp *= 1000;
pixclock = temp;
pr_debug("DIU pixclock freq - %u\n", pixclock);

temp = pixclock * 5 / 100;
pr_debug("deviation = %d\n", temp);
minpixclock = pixclock - temp;
maxpixclock = pixclock + temp;
pr_debug("DIU minpixclock - %lu\n", minpixclock);
pr_debug("DIU maxpixclock - %lu\n", maxpixclock);
pixval = speed_ccb/pixclock;
pr_debug("DIU pixval = %lu\n", pixval);

err = 100000000;
bestval = pixval;
pr_debug("DIU bestval = %lu\n", bestval);

bestfreq = 0;
for (i = -1; i <= 1; i++) {
temp = speed_ccb / ((pixval+i) + 1);
pr_debug("DIU test pixval i= %d, pixval=%lu, temp freq. = %u\n",
i, pixval, temp);
if ((temp < minpixclock) || (temp > maxpixclock))
pr_debug("DIU exceeds monitor range (%lu to %lu)\n",
minpixclock, maxpixclock);
else if (abs(temp - pixclock) < err) {
pr_debug("Entered the else if block %d\n", i);
err = abs(temp - pixclock);
bestval = pixval+i;
bestfreq = temp;
}
}

pr_debug("DIU chose = %lx\n", bestval);
pr_debug("DIU error = %ld\n NomPixClk ", err);
pr_debug("DIU: Best Freq = %lx\n", bestfreq);
/* Modify PXCLK in GUTS CLKDVDR */
pr_debug("DIU: Current value of CLKDVDR = 0x%08x\n", (*clkdvdr));
temp = (*clkdvdr) & 0x2000FFFF;
*clkdvdr = temp; /* turn off clock */
*clkdvdr = temp | 0x80000000 | (((bestval) & 0x1F) << 16);
pr_debug("DIU: Modified value of CLKDVDR = 0x%08x\n", (*clkdvdr));
iounmap(clkdvdr);
}

ssize_t mpc8610hpcd_show_monitor_port(int monitor_port, char *buf)
{
return snprintf(buf, PAGE_SIZE,
"%c0 - DVI\n"
"%c1 - Single link LVDS\n"
"%c2 - Dual link LVDS\n",
monitor_port == 0 ? '*' : ' ',
monitor_port == 1 ? '*' : ' ',
monitor_port == 2 ? '*' : ' ');
}

int mpc8610hpcd_set_sysfs_monitor_port(int val)
{
return val < 3 ? val : 0;
}

#endif

static void __init mpc86xx_hpcd_setup_arch(void)
{
struct resource r;
struct device_node *np;
unsigned char *pixis;

if (ppc_md.progress)
ppc_md.progress("mpc86xx_hpcd_setup_arch()", 0);

Expand All @@ -183,6 +332,30 @@ mpc86xx_hpcd_setup_arch(void)
}
}
#endif
#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
preallocate_diu_videomemory();
diu_ops.get_pixel_format = mpc8610hpcd_get_pixel_format;
diu_ops.set_gamma_table = mpc8610hpcd_set_gamma_table;
diu_ops.set_monitor_port = mpc8610hpcd_set_monitor_port;
diu_ops.set_pixel_clock = mpc8610hpcd_set_pixel_clock;
diu_ops.show_monitor_port = mpc8610hpcd_show_monitor_port;
diu_ops.set_sysfs_monitor_port = mpc8610hpcd_set_sysfs_monitor_port;
#endif

np = of_find_compatible_node(NULL, NULL, "fsl,fpga-pixis");
if (np) {
of_address_to_resource(np, 0, &r);
of_node_put(np);
pixis = ioremap(r.start, 32);
if (!pixis) {
printk(KERN_ERR "Err: can't map FPGA cfg register!\n");
return;
}
pixis_bdcfg0 = pixis + 8;
pixis_arch = pixis + 1;
} else
printk(KERN_ERR "Err: "
"can't find device node 'fsl,fpga-pixis'\n");

printk("MPC86xx HPCD board from Freescale Semiconductor\n");
}
Expand All @@ -200,8 +373,7 @@ static int __init mpc86xx_hpcd_probe(void)
return 0;
}

static long __init
mpc86xx_time_init(void)
static long __init mpc86xx_time_init(void)
{
unsigned int temp;

Expand Down
41 changes: 41 additions & 0 deletions arch/powerpc/sysdev/fsl_soc.c
Original file line number Diff line number Diff line change
Expand Up @@ -892,3 +892,44 @@ void fsl_rstcr_restart(char *cmd)
while (1) ;
}
#endif

#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
struct platform_diu_data_ops diu_ops = {
.diu_size = 1280 * 1024 * 4, /* default one 1280x1024 buffer */
};
EXPORT_SYMBOL(diu_ops);

int __init preallocate_diu_videomemory(void)
{
pr_debug("diu_size=%lu\n", diu_ops.diu_size);

diu_ops.diu_mem = __alloc_bootmem(diu_ops.diu_size, 8, 0);
if (!diu_ops.diu_mem) {
printk(KERN_ERR "fsl-diu: cannot allocate %lu bytes\n",
diu_ops.diu_size);
return -ENOMEM;
}

pr_debug("diu_mem=%p\n", diu_ops.diu_mem);

rh_init(&diu_ops.diu_rh_info, 4096, ARRAY_SIZE(diu_ops.diu_rh_block),
diu_ops.diu_rh_block);
return rh_attach_region(&diu_ops.diu_rh_info,
(unsigned long) diu_ops.diu_mem,
diu_ops.diu_size);
}

static int __init early_parse_diufb(char *p)
{
if (!p)
return 1;

diu_ops.diu_size = _ALIGN_UP(memparse(p, &p), 8);

pr_debug("diu_size=%lu\n", diu_ops.diu_size);

return 0;
}
early_param("diufb", early_parse_diufb);

#endif
23 changes: 23 additions & 0 deletions arch/powerpc/sysdev/fsl_soc.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,28 @@ extern int fsl_spi_init(struct spi_board_info *board_infos,
void (*deactivate_cs)(u8 cs, u8 polarity));

extern void fsl_rstcr_restart(char *cmd);

#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
#include <linux/bootmem.h>
#include <asm/rheap.h>
struct platform_diu_data_ops {
rh_block_t diu_rh_block[16];
rh_info_t diu_rh_info;
unsigned long diu_size;
void *diu_mem;

unsigned int (*get_pixel_format) (unsigned int bits_per_pixel,
int monitor_port);
void (*set_gamma_table) (int monitor_port, char *gamma_table_base);
void (*set_monitor_port) (int monitor_port);
void (*set_pixel_clock) (unsigned int pixclock);
ssize_t (*show_monitor_port) (int monitor_port, char *buf);
int (*set_sysfs_monitor_port) (int val);
};

extern struct platform_diu_data_ops diu_ops;
int __init preallocate_diu_videomemory(void);
#endif

#endif
#endif

0 comments on commit 6f90a8b

Please sign in to comment.