diff options
author | Paul Mundt <lethal@linux-sh.org> | 2010-05-24 08:52:55 +0900 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2010-05-24 08:52:55 +0900 |
commit | 1f782fee18b39b9ad438ebbd82c2915a16c879ee (patch) | |
tree | f292930065e6c860714c134790ab8882680ac739 /drivers/video | |
parent | 8eda2f21ed9c936a54fd7bc16cbfa5ee656635c2 (diff) | |
parent | f4b87dee923342505e1ddba8d34ce9de33e75050 (diff) | |
download | kernel_samsung_espresso10-1f782fee18b39b9ad438ebbd82c2915a16c879ee.zip kernel_samsung_espresso10-1f782fee18b39b9ad438ebbd82c2915a16c879ee.tar.gz kernel_samsung_espresso10-1f782fee18b39b9ad438ebbd82c2915a16c879ee.tar.bz2 |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/video')
49 files changed, 2977 insertions, 731 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 773c4a6..3dc1038 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1511,6 +1511,7 @@ config FB_VIA select FB_CFB_IMAGEBLIT select I2C_ALGOBIT select I2C + select GPIOLIB help This is the frame buffer device driver for Graphics chips of VIA UniChrome (Pro) Family (CLE266,PM800/CN400,P4M800CE/P4M800Pro/ @@ -1520,6 +1521,21 @@ config FB_VIA To compile this driver as a module, choose M here: the module will be called viafb. + +if FB_VIA + +config FB_VIA_DIRECT_PROCFS + bool "direct hardware access via procfs (DEPRECATED)(DANGEROUS)" + depends on FB_VIA + default n + help + Allow direct hardware access to some output registers via procfs. + This is dangerous but may provide the only chance to get the + correct output device configuration. + Its use is strongly discouraged. + +endif + config FB_NEOMAGIC tristate "NeoMagic display support" depends on FB && PCI @@ -2194,7 +2210,6 @@ config FB_MSM select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT - default y config FB_MX3 tristate "MX3 Framebuffer support" diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 6c37e8e..3c1e13e 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -2099,7 +2099,7 @@ static ssize_t radeon_show_one_edid(char *buf, loff_t off, size_t count, const u } -static ssize_t radeon_show_edid1(struct kobject *kobj, +static ssize_t radeon_show_edid1(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -2112,7 +2112,7 @@ static ssize_t radeon_show_edid1(struct kobject *kobj, } -static ssize_t radeon_show_edid2(struct kobject *kobj, +static ssize_t radeon_show_edid2(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/drivers/video/cobalt_lcdfb.c b/drivers/video/cobalt_lcdfb.c index 5eb61b5..42fe155 100644 --- a/drivers/video/cobalt_lcdfb.c +++ b/drivers/video/cobalt_lcdfb.c @@ -123,7 +123,7 @@ static void lcd_clear(struct fb_info *info) lcd_write_control(info, LCD_RESET); } -static struct fb_fix_screeninfo cobalt_lcdfb_fix __initdata = { +static struct fb_fix_screeninfo cobalt_lcdfb_fix __devinitdata = { .id = "cobalt-lcd", .type = FB_TYPE_TEXT, .type_aux = FB_AUX_TEXT_MDA, diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c index ecf4055..4a56f46 100644 --- a/drivers/video/efifb.c +++ b/drivers/video/efifb.c @@ -168,7 +168,7 @@ static void efifb_destroy(struct fb_info *info) { if (info->screen_base) iounmap(info->screen_base); - release_mem_region(info->aperture_base, info->aperture_size); + release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size); framebuffer_release(info); } @@ -292,8 +292,13 @@ static int __devinit efifb_probe(struct platform_device *dev) info->pseudo_palette = info->par; info->par = NULL; - info->aperture_base = efifb_fix.smem_start; - info->aperture_size = size_remap; + info->apertures = alloc_apertures(1); + if (!info->apertures) { + err = -ENOMEM; + goto err_release_fb; + } + info->apertures->ranges[0].base = efifb_fix.smem_start; + info->apertures->ranges[0].size = size_remap; info->screen_base = ioremap(efifb_fix.smem_start, efifb_fix.smem_len); if (!info->screen_base) { diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index a15b44e..731fce6 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1468,16 +1468,70 @@ static int fb_check_foreignness(struct fb_info *fi) return 0; } -static bool fb_do_apertures_overlap(struct fb_info *gen, struct fb_info *hw) +static bool apertures_overlap(struct aperture *gen, struct aperture *hw) { /* is the generic aperture base the same as the HW one */ - if (gen->aperture_base == hw->aperture_base) + if (gen->base == hw->base) return true; /* is the generic aperture base inside the hw base->hw base+size */ - if (gen->aperture_base > hw->aperture_base && gen->aperture_base <= hw->aperture_base + hw->aperture_size) + if (gen->base > hw->base && gen->base <= hw->base + hw->size) return true; return false; } + +static bool fb_do_apertures_overlap(struct apertures_struct *gena, + struct apertures_struct *hwa) +{ + int i, j; + if (!hwa || !gena) + return false; + + for (i = 0; i < hwa->count; ++i) { + struct aperture *h = &hwa->ranges[i]; + for (j = 0; j < gena->count; ++j) { + struct aperture *g = &gena->ranges[j]; + printk(KERN_DEBUG "checking generic (%llx %llx) vs hw (%llx %llx)\n", + (unsigned long long)g->base, + (unsigned long long)g->size, + (unsigned long long)h->base, + (unsigned long long)h->size); + if (apertures_overlap(g, h)) + return true; + } + } + + return false; +} + +#define VGA_FB_PHYS 0xA0000 +void remove_conflicting_framebuffers(struct apertures_struct *a, + const char *name, bool primary) +{ + int i; + + /* check all firmware fbs and kick off if the base addr overlaps */ + for (i = 0 ; i < FB_MAX; i++) { + struct apertures_struct *gen_aper; + if (!registered_fb[i]) + continue; + + if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE)) + continue; + + gen_aper = registered_fb[i]->apertures; + if (fb_do_apertures_overlap(gen_aper, a) || + (primary && gen_aper && gen_aper->count && + gen_aper->ranges[0].base == VGA_FB_PHYS)) { + + printk(KERN_ERR "fb: conflicting fb hw usage " + "%s vs %s - removing generic driver\n", + name, registered_fb[i]->fix.id); + unregister_framebuffer(registered_fb[i]); + } + } +} +EXPORT_SYMBOL(remove_conflicting_framebuffers); + /** * register_framebuffer - registers a frame buffer device * @fb_info: frame buffer info structure @@ -1501,21 +1555,8 @@ register_framebuffer(struct fb_info *fb_info) if (fb_check_foreignness(fb_info)) return -ENOSYS; - /* check all firmware fbs and kick off if the base addr overlaps */ - for (i = 0 ; i < FB_MAX; i++) { - if (!registered_fb[i]) - continue; - - if (registered_fb[i]->flags & FBINFO_MISC_FIRMWARE) { - if (fb_do_apertures_overlap(registered_fb[i], fb_info)) { - printk(KERN_ERR "fb: conflicting fb hw usage " - "%s vs %s - removing generic driver\n", - fb_info->fix.id, - registered_fb[i]->fix.id); - unregister_framebuffer(registered_fb[i]); - } - } - } + remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id, + fb_is_primary_device(fb_info)); num_registered_fb++; for (i = 0 ; i < FB_MAX; i++) diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index 81aa312..0a08f13 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -80,6 +80,7 @@ EXPORT_SYMBOL(framebuffer_alloc); */ void framebuffer_release(struct fb_info *info) { + kfree(info->apertures); kfree(info); } EXPORT_SYMBOL(framebuffer_release); diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index 772ba3f..7cfc170 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -387,7 +387,8 @@ static void sdc_disable_channel(struct mx3fb_info *mx3_fbi) spin_unlock_irqrestore(&mx3fb->lock, flags); - mx3_fbi->txd->chan->device->device_terminate_all(mx3_fbi->txd->chan); + mx3_fbi->txd->chan->device->device_control(mx3_fbi->txd->chan, + DMA_TERMINATE_ALL, 0); mx3_fbi->txd = NULL; mx3_fbi->cookie = -EINVAL; } diff --git a/drivers/video/offb.c b/drivers/video/offb.c index 61f8b8f..46dda7d 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -285,7 +285,7 @@ static void offb_destroy(struct fb_info *info) { if (info->screen_base) iounmap(info->screen_base); - release_mem_region(info->aperture_base, info->aperture_size); + release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size); framebuffer_release(info); } @@ -491,8 +491,11 @@ static void __init offb_init_fb(const char *name, const char *full_name, var->vmode = FB_VMODE_NONINTERLACED; /* set offb aperture size for generic probing */ - info->aperture_base = address; - info->aperture_size = fix->smem_len; + info->apertures = alloc_apertures(1); + if (!info->apertures) + goto out_aper; + info->apertures->ranges[0].base = address; + info->apertures->ranges[0].size = fix->smem_len; info->fbops = &offb_ops; info->screen_base = ioremap(address, fix->smem_len); @@ -501,17 +504,20 @@ static void __init offb_init_fb(const char *name, const char *full_name, fb_alloc_cmap(&info->cmap, 256, 0); - if (register_framebuffer(info) < 0) { - iounmap(par->cmap_adr); - par->cmap_adr = NULL; - iounmap(info->screen_base); - framebuffer_release(info); - release_mem_region(res_start, res_size); - return; - } + if (register_framebuffer(info) < 0) + goto out_err; printk(KERN_INFO "fb%d: Open Firmware frame buffer device on %s\n", info->node, full_name); + return; + +out_err: + iounmap(info->screen_base); +out_aper: + iounmap(par->cmap_adr); + par->cmap_adr = NULL; + framebuffer_release(info); + release_mem_region(res_start, res_size); } diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index dfb57ee..881c9f7 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig @@ -10,6 +10,7 @@ config PANEL_GENERIC config PANEL_SHARP_LS037V7DW01 tristate "Sharp LS037V7DW01 LCD Panel" depends on OMAP2_DSS + select BACKLIGHT_CLASS_DEVICE help LCD Panel used in TI's SDP3430 and EVM boards @@ -33,8 +34,14 @@ config PANEL_TOPPOLY_TDO35S config PANEL_TPO_TD043MTEA1 tristate "TPO TD043MTEA1 LCD Panel" - depends on OMAP2_DSS && I2C + depends on OMAP2_DSS && SPI help LCD Panel used in OMAP3 Pandora +config PANEL_ACX565AKM + tristate "ACX565AKM Panel" + depends on OMAP2_DSS_SDI + select BACKLIGHT_CLASS_DEVICE + help + This is the LCD panel used on Nokia N900 endmenu diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile index e2bb321..aa38609 100644 --- a/drivers/video/omap2/displays/Makefile +++ b/drivers/video/omap2/displays/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_PANEL_SHARP_LQ043T1DG01) += panel-sharp-lq043t1dg01.o obj-$(CONFIG_PANEL_TAAL) += panel-taal.o obj-$(CONFIG_PANEL_TOPPOLY_TDO35S) += panel-toppoly-tdo35s.o obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o +obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c new file mode 100644 index 0000000..1f8eb70 --- /dev/null +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -0,0 +1,819 @@ +/* + * Support for ACX565AKM LCD Panel used on Nokia N900 + * + * Copyright (C) 2010 Nokia Corporation + * + * Original Driver Author: Imre Deak <imre.deak@nokia.com> + * Based on panel-generic.c by Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Adapted to new DSS2 framework: Roger Quadros <roger.quadros@nokia.com> + * + * 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. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/backlight.h> +#include <linux/fb.h> + +#include <plat/display.h> + +#define MIPID_CMD_READ_DISP_ID 0x04 +#define MIPID_CMD_READ_RED 0x06 +#define MIPID_CMD_READ_GREEN 0x07 +#define MIPID_CMD_READ_BLUE 0x08 +#define MIPID_CMD_READ_DISP_STATUS 0x09 +#define MIPID_CMD_RDDSDR 0x0F +#define MIPID_CMD_SLEEP_IN 0x10 +#define MIPID_CMD_SLEEP_OUT 0x11 +#define MIPID_CMD_DISP_OFF 0x28 +#define MIPID_CMD_DISP_ON 0x29 +#define MIPID_CMD_WRITE_DISP_BRIGHTNESS 0x51 +#define MIPID_CMD_READ_DISP_BRIGHTNESS 0x52 +#define MIPID_CMD_WRITE_CTRL_DISP 0x53 + +#define CTRL_DISP_BRIGHTNESS_CTRL_ON (1 << 5) +#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON (1 << 4) +#define CTRL_DISP_BACKLIGHT_ON (1 << 2) +#define CTRL_DISP_AUTO_BRIGHTNESS_ON (1 << 1) + +#define MIPID_CMD_READ_CTRL_DISP 0x54 +#define MIPID_CMD_WRITE_CABC 0x55 +#define MIPID_CMD_READ_CABC 0x56 + +#define MIPID_VER_LPH8923 3 +#define MIPID_VER_LS041Y3 4 +#define MIPID_VER_L4F00311 8 +#define MIPID_VER_ACX565AKM 9 + +struct acx565akm_device { + char *name; + int enabled; + int model; + int revision; + u8 display_id[3]; + unsigned has_bc:1; + unsigned has_cabc:1; + unsigned cabc_mode; + unsigned long hw_guard_end; /* next value of jiffies + when we can issue the + next sleep in/out command */ + unsigned long hw_guard_wait; /* max guard time in jiffies */ + + struct spi_device *spi; + struct mutex mutex; + + struct omap_dss_device *dssdev; + struct backlight_device *bl_dev; +}; + +static struct acx565akm_device acx_dev; +static int acx565akm_bl_update_status(struct backlight_device *dev); + +/*--------------------MIPID interface-----------------------------*/ + +static void acx565akm_transfer(struct acx565akm_device *md, int cmd, + const u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + struct spi_message m; + struct spi_transfer *x, xfer[5]; + int r; + + BUG_ON(md->spi == NULL); + + spi_message_init(&m); + + memset(xfer, 0, sizeof(xfer)); + x = &xfer[0]; + + cmd &= 0xff; + x->tx_buf = &cmd; + x->bits_per_word = 9; + x->len = 2; + + if (rlen > 1 && wlen == 0) { + /* + * Between the command and the response data there is a + * dummy clock cycle. Add an extra bit after the command + * word to account for this. + */ + x->bits_per_word = 10; + cmd <<= 1; + } + spi_message_add_tail(x, &m); + + if (wlen) { + x++; + x->tx_buf = wbuf; + x->len = wlen; + x->bits_per_word = 9; + spi_message_add_tail(x, &m); + } + + if (rlen) { + x++; + x->rx_buf = rbuf; + x->len = rlen; + spi_message_add_tail(x, &m); + } + + r = spi_sync(md->spi, &m); + if (r < 0) + dev_dbg(&md->spi->dev, "spi_sync %d\n", r); +} + +static inline void acx565akm_cmd(struct acx565akm_device *md, int cmd) +{ + acx565akm_transfer(md, cmd, NULL, 0, NULL, 0); +} + +static inline void acx565akm_write(struct acx565akm_device *md, + int reg, const u8 *buf, int len) +{ + acx565akm_transfer(md, reg, buf, len, NULL, 0); +} + +static inline void acx565akm_read(struct acx565akm_device *md, + int reg, u8 *buf, int len) +{ + acx565akm_transfer(md, reg, NULL, 0, buf, len); +} + +static void hw_guard_start(struct acx565akm_device *md, int guard_msec) +{ + md->hw_guard_wait = msecs_to_jiffies(guard_msec); + md->hw_guard_end = jiffies + md->hw_guard_wait; +} + +static void hw_guard_wait(struct acx565akm_device *md) +{ + unsigned long wait = md->hw_guard_end - jiffies; + + if ((long)wait > 0 && wait <= md->hw_guard_wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(wait); + } +} + +/*----------------------MIPID wrappers----------------------------*/ + +static void set_sleep_mode(struct acx565akm_device *md, int on) +{ + int cmd; + + if (on) + cmd = MIPID_CMD_SLEEP_IN; + else + cmd = MIPID_CMD_SLEEP_OUT; + /* + * We have to keep 120msec between sleep in/out commands. + * (8.2.15, 8.2.16). + */ + hw_guard_wait(md); + acx565akm_cmd(md, cmd); + hw_guard_start(md, 120); +} + +static void set_display_state(struct acx565akm_device *md, int enabled) +{ + int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF; + + acx565akm_cmd(md, cmd); +} + +static int panel_enabled(struct acx565akm_device *md) +{ + u32 disp_status; + int enabled; + + acx565akm_read(md, MIPID_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4); + disp_status = __be32_to_cpu(disp_status); + enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); + dev_dbg(&md->spi->dev, + "LCD panel %senabled by bootloader (status 0x%04x)\n", + enabled ? "" : "not ", disp_status); + return enabled; +} + +static int panel_detect(struct acx565akm_device *md) +{ + acx565akm_read(md, MIPID_CMD_READ_DISP_ID, md->display_id, 3); + dev_dbg(&md->spi->dev, "MIPI display ID: %02x%02x%02x\n", + md->display_id[0], md->display_id[1], md->display_id[2]); + + switch (md->display_id[0]) { + case 0x10: + md->model = MIPID_VER_ACX565AKM; + md->name = "acx565akm"; + md->has_bc = 1; + md->has_cabc = 1; + break; + case 0x29: + md->model = MIPID_VER_L4F00311; + md->name = "l4f00311"; + break; + case 0x45: + md->model = MIPID_VER_LPH8923; + md->name = "lph8923"; + break; + case 0x83: + md->model = MIPID_VER_LS041Y3; + md->name = "ls041y3"; + break; + default: + md->name = "unknown"; + dev_err(&md->spi->dev, "invalid display ID\n"); + return -ENODEV; + } + + md->revision = md->display_id[1]; + + dev_info(&md->spi->dev, "omapfb: %s rev %02x LCD detected\n", + md->name, md->revision); + + return 0; +} + +/*----------------------Backlight Control-------------------------*/ + +static void enable_backlight_ctrl(struct acx565akm_device *md, int enable) +{ + u16 ctrl; + + acx565akm_read(md, MIPID_CMD_READ_CTRL_DISP, (u8 *)&ctrl, 1); + if (enable) { + ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON | + CTRL_DISP_BACKLIGHT_ON; + } else { + ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON | + CTRL_DISP_BACKLIGHT_ON); + } + + ctrl |= 1 << 8; + acx565akm_write(md, MIPID_CMD_WRITE_CTRL_DISP, (u8 *)&ctrl, 2); +} + +static void set_cabc_mode(struct acx565akm_device *md, unsigned mode) +{ + u16 cabc_ctrl; + + md->cabc_mode = mode; + if (!md->enabled) + return; + cabc_ctrl = 0; + acx565akm_read(md, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1); + cabc_ctrl &= ~3; + cabc_ctrl |= (1 << 8) | (mode & 3); + acx565akm_write(md, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2); +} + +static unsigned get_cabc_mode(struct acx565akm_device *md) +{ + return md->cabc_mode; +} + +static unsigned get_hw_cabc_mode(struct acx565akm_device *md) +{ + u8 cabc_ctrl; + + acx565akm_read(md, MIPID_CMD_READ_CABC, &cabc_ctrl, 1); + return cabc_ctrl & 3; +} + +static void acx565akm_set_brightness(struct acx565akm_device *md, int level) +{ + int bv; + + bv = level | (1 << 8); + acx565akm_write(md, MIPID_CMD_WRITE_DISP_BRIGHTNESS, (u8 *)&bv, 2); + + if (level) + enable_backlight_ctrl(md, 1); + else + enable_backlight_ctrl(md, 0); +} + +static int acx565akm_get_actual_brightness(struct acx565akm_device *md) +{ + u8 bv; + + acx565akm_read(md, MIPID_CMD_READ_DISP_BRIGHTNESS, &bv, 1); + + return bv; +} + + +static int acx565akm_bl_update_status(struct backlight_device *dev) +{ + struct acx565akm_device *md = dev_get_drvdata(&dev->dev); + int r; + int level; + + dev_dbg(&md->spi->dev, "%s\n", __func__); + + mutex_lock(&md->mutex); + + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) + level = dev->props.brightness; + else + level = 0; + + r = 0; + if (md->has_bc) + acx565akm_set_brightness(md, level); + else if (md->dssdev->set_backlight) + r = md->dssdev->set_backlight(md->dssdev, level); + else + r = -ENODEV; + + mutex_unlock(&md->mutex); + + return r; +} + +static int acx565akm_bl_get_intensity(struct backlight_device *dev) +{ + struct acx565akm_device *md = dev_get_drvdata(&dev->dev); + + dev_dbg(&dev->dev, "%s\n", __func__); + + if (!md->has_bc && md->dssdev->set_backlight == NULL) + return -ENODEV; + + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) { + if (md->has_bc) + return acx565akm_get_actual_brightness(md); + else + return dev->props.brightness; + } + + return 0; +} + +static const struct backlight_ops acx565akm_bl_ops = { + .get_brightness = acx565akm_bl_get_intensity, + .update_status = acx565akm_bl_update_status, +}; + +/*--------------------Auto Brightness control via Sysfs---------------------*/ + +static const char *cabc_modes[] = { + "off", /* always used when CABC is not supported */ + "ui", + "still-image", + "moving-image", +}; + +static ssize_t show_cabc_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acx565akm_device *md = dev_get_drvdata(dev); + const char *mode_str; + int mode; + int len; + + if (!md->has_cabc) + mode = 0; + else + mode = get_cabc_mode(md); + mode_str = "unknown"; + if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes)) + mode_str = cabc_modes[mode]; + len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str); + + return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1; +} + +static ssize_t store_cabc_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acx565akm_device *md = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) { + const char *mode_str = cabc_modes[i]; + int cmp_len = strlen(mode_str); + + if (count > 0 && buf[count - 1] == '\n') + count--; + if (count != cmp_len) + continue; + + if (strncmp(buf, mode_str, cmp_len) == 0) + break; + } + + if (i == ARRAY_SIZE(cabc_modes)) + return -EINVAL; + + if (!md->has_cabc && i != 0) + return -EINVAL; + + mutex_lock(&md->mutex); + set_cabc_mode(md, i); + mutex_unlock(&md->mutex); + + return count; +} + +static ssize_t show_cabc_available_modes(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acx565akm_device *md = dev_get_drvdata(dev); + int len; + int i; + + if (!md->has_cabc) + return snprintf(buf, PAGE_SIZE, "%s\n", cabc_modes[0]); + + for (i = 0, len = 0; + len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++) + len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s", + i ? " " : "", cabc_modes[i], + i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : ""); + + return len < PAGE_SIZE ? len : PAGE_SIZE - 1; +} + +static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR, + show_cabc_mode, store_cabc_mode); +static DEVICE_ATTR(cabc_available_modes, S_IRUGO, + show_cabc_available_modes, NULL); + +static struct attribute *bldev_attrs[] = { + &dev_attr_cabc_mode.attr, + &dev_attr_cabc_available_modes.attr, + NULL, +}; + +static struct attribute_group bldev_attr_group = { + .attrs = bldev_attrs, +}; + + +/*---------------------------ACX Panel----------------------------*/ + +static int acx_get_recommended_bpp(struct omap_dss_device *dssdev) +{ + return 16; +} + +static struct omap_video_timings acx_panel_timings = { + .x_res = 800, + .y_res = 480, + .pixel_clock = 24000, + .hfp = 28, + .hsw = 4, + .hbp = 24, + .vfp = 3, + .vsw = 3, + .vbp = 4, +}; + +static int acx_panel_probe(struct omap_dss_device *dssdev) +{ + int r; + struct acx565akm_device *md = &acx_dev; + struct backlight_device *bldev; + int max_brightness, brightness; + struct backlight_properties props; + + dev_dbg(&dssdev->dev, "%s\n", __func__); + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS; + /* FIXME AC bias ? */ + dssdev->panel.timings = acx_panel_timings; + + if (dssdev->platform_enable) + dssdev->platform_enable(dssdev); + /* + * After reset we have to wait 5 msec before the first + * command can be sent. + */ + msleep(5); + + md->enabled = panel_enabled(md); + + r = panel_detect(md); + if (r) { + dev_err(&dssdev->dev, "%s panel detect error\n", __func__); + if (!md->enabled && dssdev->platform_disable) + dssdev->platform_disable(dssdev); + return r; + } + + mutex_lock(&acx_dev.mutex); + acx_dev.dssdev = dssdev; + mutex_unlock(&acx_dev.mutex); + + if (!md->enabled) { + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + } + + /*------- Backlight control --------*/ + + props.fb_blank = FB_BLANK_UNBLANK; + props.power = FB_BLANK_UNBLANK; + + bldev = backlight_device_register("acx565akm", &md->spi->dev, + md, &acx565akm_bl_ops, &props); + md->bl_dev = bldev; + if (md->has_cabc) { + r = sysfs_create_group(&bldev->dev.kobj, &bldev_attr_group); + if (r) { + dev_err(&bldev->dev, + "%s failed to create sysfs files\n", __func__); + backlight_device_unregister(bldev); + return r; + } + md->cabc_mode = get_hw_cabc_mode(md); + } + + if (md->has_bc) + max_brightness = 255; + else + max_brightness = dssdev->max_backlight_level; + + if (md->has_bc) + brightness = acx565akm_get_actual_brightness(md); + else if (dssdev->get_backlight) + brightness = dssdev->get_backlight(dssdev); + else + brightness = 0; + + bldev->props.max_brightness = max_brightness; + bldev->props.brightness = brightness; + + acx565akm_bl_update_status(bldev); + return 0; +} + +static void acx_panel_remove(struct omap_dss_device *dssdev) +{ + struct acx565akm_device *md = &acx_dev; + + dev_dbg(&dssdev->dev, "%s\n", __func__); + sysfs_remove_group(&md->bl_dev->dev.kobj, &bldev_attr_group); + backlight_device_unregister(md->bl_dev); + mutex_lock(&acx_dev.mutex); + acx_dev.dssdev = NULL; + mutex_unlock(&acx_dev.mutex); +} + +static int acx_panel_power_on(struct omap_dss_device *dssdev) +{ + struct acx565akm_device *md = &acx_dev; + int r; + + dev_dbg(&dssdev->dev, "%s\n", __func__); + + mutex_lock(&md->mutex); + + r = omapdss_sdi_display_enable(dssdev); + if (r) { + pr_err("%s sdi enable failed\n", __func__); + return r; + } + + /*FIXME tweak me */ + msleep(50); + + if (dssdev->platform_enable) { + r = dssdev->platform_enable(dssdev); + if (r) + goto fail; + } + + if (md->enabled) { + dev_dbg(&md->spi->dev, "panel already enabled\n"); + mutex_unlock(&md->mutex); + return 0; + } + + /* + * We have to meet all the following delay requirements: + * 1. tRW: reset pulse width 10usec (7.12.1) + * 2. tRT: reset cancel time 5msec (7.12.1) + * 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst + * case (7.6.2) + * 4. 120msec before the sleep out command (7.12.1) + */ + msleep(120); + + set_sleep_mode(md, 0); + md->enabled = 1; + + /* 5msec between sleep out and the next command. (8.2.16) */ + msleep(5); + set_display_state(md, 1); + set_cabc_mode(md, md->cabc_mode); + + mutex_unlock(&md->mutex); + + return acx565akm_bl_update_status(md->bl_dev); +fail: + omapdss_sdi_display_disable(dssdev); + return r; +} + +static void acx_panel_power_off(struct omap_dss_device *dssdev) +{ + struct acx565akm_device *md = &acx_dev; + + dev_dbg(&dssdev->dev, "%s\n", __func__); + + mutex_lock(&md->mutex); + + if (!md->enabled) { + mutex_unlock(&md->mutex); + return; + } + set_display_state(md, 0); + set_sleep_mode(md, 1); + md->enabled = 0; + /* + * We have to provide PCLK,HS,VS signals for 2 frames (worst case + * ~50msec) after sending the sleep in command and asserting the + * reset signal. We probably could assert the reset w/o the delay + * but we still delay to avoid possible artifacts. (7.6.1) + */ + msleep(50); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + /* FIXME need to tweak this delay */ + msleep(100); + + omapdss_sdi_display_disable(dssdev); + + mutex_unlock(&md->mutex); +} + +static int acx_panel_enable(struct omap_dss_device *dssdev) +{ + int r; + + dev_dbg(&dssdev->dev, "%s\n", __func__); + r = acx_panel_power_on(dssdev); + + if (r) + return r; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + return 0; +} + +static void acx_panel_disable(struct omap_dss_device *dssdev) +{ + dev_dbg(&dssdev->dev, "%s\n", __func__); + acx_panel_power_off(dssdev); + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static int acx_panel_suspend(struct omap_dss_device *dssdev) +{ + dev_dbg(&dssdev->dev, "%s\n", __func__); + acx_panel_power_off(dssdev); + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + return 0; +} + +static int acx_panel_resume(struct omap_dss_device *dssdev) +{ + int r; + + dev_dbg(&dssdev->dev, "%s\n", __func__); + r = acx_panel_power_on(dssdev); + if (r) + return r; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + return 0; +} + +static void acx_panel_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + int r; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + omapdss_sdi_display_disable(dssdev); + + dssdev->panel.timings = *timings; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + r = omapdss_sdi_display_enable(dssdev); + if (r) + dev_err(&dssdev->dev, "%s enable failed\n", __func__); + } +} + +static void acx_panel_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static int acx_panel_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + return 0; +} + + +static struct omap_dss_driver acx_panel_driver = { + .probe = acx_panel_probe, + .remove = acx_panel_remove, + + .enable = acx_panel_enable, + .disable = acx_panel_disable, + .suspend = acx_panel_suspend, + .resume = acx_panel_resume, + + .set_timings = acx_panel_set_timings, + .get_timings = acx_panel_get_timings, + .check_timings = acx_panel_check_timings, + + .get_recommended_bpp = acx_get_recommended_bpp, + + .driver = { + .name = "panel-acx565akm", + .owner = THIS_MODULE, + }, +}; + +/*--------------------SPI probe-------------------------*/ + +static int acx565akm_spi_probe(struct spi_device *spi) +{ + struct acx565akm_device *md = &acx_dev; + + dev_dbg(&spi->dev, "%s\n", __func__); + + spi->mode = SPI_MODE_3; + md->spi = spi; + mutex_init(&md->mutex); + dev_set_drvdata(&spi->dev, md); + + omap_dss_register_driver(&acx_panel_driver); + + return 0; +} + +static int acx565akm_spi_remove(struct spi_device *spi) +{ + struct acx565akm_device *md = dev_get_drvdata(&spi->dev); + + dev_dbg(&md->spi->dev, "%s\n", __func__); + omap_dss_unregister_driver(&acx_panel_driver); + + return 0; +} + +static struct spi_driver acx565akm_spi_driver = { + .driver = { + .name = "acx565akm", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = acx565akm_spi_probe, + .remove = __devexit_p(acx565akm_spi_remove), +}; + +static int __init acx565akm_init(void) +{ + return spi_register_driver(&acx565akm_spi_driver); +} + +static void __exit acx565akm_exit(void) +{ + spi_unregister_driver(&acx565akm_spi_driver); +} + +module_init(acx565akm_init); +module_exit(acx565akm_exit); + +MODULE_AUTHOR("Nokia Corporation"); +MODULE_DESCRIPTION("acx565akm LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c index 8d51a5e..7d9eb2b 100644 --- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -20,10 +20,17 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/backlight.h> +#include <linux/fb.h> #include <linux/err.h> +#include <linux/slab.h> #include <plat/display.h> +struct sharp_data { + struct backlight_device *bl; +}; + static struct omap_video_timings sharp_ls_timings = { .x_res = 480, .y_res = 640, @@ -39,18 +46,89 @@ static struct omap_video_timings sharp_ls_timings = { .vbp = 1, }; +static int sharp_ls_bl_update_status(struct backlight_device *bl) +{ + struct omap_dss_device *dssdev = dev_get_drvdata(&bl->dev); + int level; + + if (!dssdev->set_backlight) + return -EINVAL; + + if (bl->props.fb_blank == FB_BLANK_UNBLANK && + bl->props.power == FB_BLANK_UNBLANK) + level = bl->props.brightness; + else + level = 0; + + return dssdev->set_backlight(dssdev, level); +} + +static int sharp_ls_bl_get_brightness(struct backlight_device *bl) +{ + if (bl->props.fb_blank == FB_BLANK_UNBLANK && + bl->props.power == FB_BLANK_UNBLANK) + return bl->props.brightness; + + return 0; +} + +static const struct backlight_ops sharp_ls_bl_ops = { + .get_brightness = sharp_ls_bl_get_brightness, + .update_status = sharp_ls_bl_update_status, +}; + + + static int sharp_ls_panel_probe(struct omap_dss_device *dssdev) { + struct backlight_properties props; + struct backlight_device *bl; + struct sharp_data *sd; + int r; + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; dssdev->panel.acb = 0x28; dssdev->panel.timings = sharp_ls_timings; + sd = kzalloc(sizeof(*sd), GFP_KERNEL); + if (!sd) + return -ENOMEM; + + dev_set_drvdata(&dssdev->dev, sd); + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = dssdev->max_backlight_level; + + bl = backlight_device_register("sharp-ls", &dssdev->dev, dssdev, + &sharp_ls_bl_ops, &props); + if (IS_ERR(bl)) { + r = PTR_ERR(bl); + kfree(sd); + return r; + } + sd->bl = bl; + + bl->props.fb_blank = FB_BLANK_UNBLANK; + bl->props.power = FB_BLANK_UNBLANK; + bl->props.brightness = dssdev->max_backlight_level; + r = sharp_ls_bl_update_status(bl); + if (r < 0) + dev_err(&dssdev->dev, "failed to set lcd brightness\n"); + return 0; } static void sharp_ls_panel_remove(struct omap_dss_device *dssdev) { + struct sharp_data *sd = dev_get_drvdata(&dssdev->dev); + struct backlight_device *bl = sd->bl; + + bl->props.power = FB_BLANK_POWERDOWN; + sharp_ls_bl_update_status(bl); + backlight_device_unregister(bl); + + kfree(sd); } static int sharp_ls_power_on(struct omap_dss_device *dssdev) diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index 4f3988a..aaf5d30 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -31,6 +31,7 @@ #include <linux/completion.h> #include <linux/workqueue.h> #include <linux/slab.h> +#include <linux/mutex.h> #include <plat/display.h> @@ -67,6 +68,8 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable); struct taal_data { + struct mutex lock; + struct backlight_device *bldev; unsigned long hw_guard_end; /* next value of jiffies when we can @@ -510,6 +513,8 @@ static int taal_probe(struct omap_dss_device *dssdev) } td->dssdev = dssdev; + mutex_init(&td->lock); + td->esd_wq = create_singlethread_workqueue("taal_esd"); if (td->esd_wq == NULL) { dev_err(&dssdev->dev, "can't create ESD workqueue\n"); @@ -697,10 +702,9 @@ static int taal_power_on(struct omap_dss_device *dssdev) return 0; err: - dsi_bus_unlock(); - omapdss_dsi_display_disable(dssdev); err0: + dsi_bus_unlock(); if (dssdev->platform_disable) dssdev->platform_disable(dssdev); @@ -733,54 +737,96 @@ static void taal_power_off(struct omap_dss_device *dssdev) static int taal_enable(struct omap_dss_device *dssdev) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); int r; + dev_dbg(&dssdev->dev, "enable\n"); - if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) - return -EINVAL; + mutex_lock(&td->lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + r = -EINVAL; + goto err; + } r = taal_power_on(dssdev); if (r) - return r; + goto err; dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + mutex_unlock(&td->lock); + + return 0; +err: + dev_dbg(&dssdev->dev, "enable failed\n"); + mutex_unlock(&td->lock); return r; } static void taal_disable(struct omap_dss_device *dssdev) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + dev_dbg(&dssdev->dev, "disable\n"); + mutex_lock(&td->lock); + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) taal_power_off(dssdev); dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + mutex_unlock(&td->lock); } static int taal_suspend(struct omap_dss_device *dssdev) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int r; + dev_dbg(&dssdev->dev, "suspend\n"); - if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) - return -EINVAL; + mutex_lock(&td->lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + r = -EINVAL; + goto err; + } taal_power_off(dssdev); dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + mutex_unlock(&td->lock); + return 0; +err: + mutex_unlock(&td->lock); + return r; } static int taal_resume(struct omap_dss_device *dssdev) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); int r; + dev_dbg(&dssdev->dev, "resume\n"); - if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) - return -EINVAL; + mutex_lock(&td->lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { + r = -EINVAL; + goto err; + } r = taal_power_on(dssdev); dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + mutex_unlock(&td->lock); + + return r; +err: + mutex_unlock(&td->lock); return r; } @@ -799,6 +845,7 @@ static int taal_update(struct omap_dss_device *dssdev, dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h); + mutex_lock(&td->lock); dsi_bus_lock(); if (!td->enabled) { @@ -820,18 +867,24 @@ static int taal_update(struct omap_dss_device *dssdev, goto err; /* note: no bus_unlock here. unlock is in framedone_cb */ + mutex_unlock(&td->lock); return 0; err: dsi_bus_unlock(); + mutex_unlock(&td->lock); return r; } static int taal_sync(struct omap_dss_device *dssdev) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + dev_dbg(&dssdev->dev, "sync\n"); + mutex_lock(&td->lock); dsi_bus_lock(); dsi_bus_unlock(); + mutex_unlock(&td->lock); dev_dbg(&dssdev->dev, "sync done\n"); @@ -861,13 +914,16 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable) static int taal_enable_te(struct omap_dss_device *dssdev, bool enable) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); int r; + mutex_lock(&td->lock); dsi_bus_lock(); r = _taal_enable_te(dssdev, enable); dsi_bus_unlock(); + mutex_unlock(&td->lock); return r; } @@ -875,7 +931,13 @@ static int taal_enable_te(struct omap_dss_device *dssdev, bool enable) static int taal_get_te(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); - return td->te_enabled; + int r; + + mutex_lock(&td->lock); + r = td->te_enabled; + mutex_unlock(&td->lock); + + return r; } static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) @@ -885,6 +947,7 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) dev_dbg(&dssdev->dev, "rotate %d\n", rotate); + mutex_lock(&td->lock); dsi_bus_lock(); if (td->enabled) { @@ -896,16 +959,24 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) td->rotate = rotate; dsi_bus_unlock(); + mutex_unlock(&td->lock); return 0; err: dsi_bus_unlock(); + mutex_unlock(&td->lock); return r; } static u8 taal_get_rotate(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); - return td->rotate; + int r; + + mutex_lock(&td->lock); + r = td->rotate; + mutex_unlock(&td->lock); + + return r; } static int taal_mirror(struct omap_dss_device *dssdev, bool enable) @@ -915,6 +986,7 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable) dev_dbg(&dssdev->dev, "mirror %d\n", enable); + mutex_lock(&td->lock); dsi_bus_lock(); if (td->enabled) { r = taal_set_addr_mode(td->rotate, enable); @@ -925,23 +997,33 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable) td->mirror = enable; dsi_bus_unlock(); + mutex_unlock(&td->lock); return 0; err: dsi_bus_unlock(); + mutex_unlock(&td->lock); return r; } static bool taal_get_mirror(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); - return td->mirror; + int r; + + mutex_lock(&td->lock); + r = td->mirror; + mutex_unlock(&td->lock); + + return r; } static int taal_run_test(struct omap_dss_device *dssdev, int test_num) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); u8 id1, id2, id3; int r; + mutex_lock(&td->lock); dsi_bus_lock(); r = taal_dcs_read_1(DCS_GET_ID1, &id1); @@ -955,9 +1037,11 @@ static int taal_run_test(struct omap_dss_device *dssdev, int test_num) goto err; dsi_bus_unlock(); + mutex_unlock(&td->lock); return 0; err: dsi_bus_unlock(); + mutex_unlock(&td->lock); return r; } @@ -971,12 +1055,16 @@ static int taal_memory_read(struct omap_dss_device *dssdev, unsigned buf_used = 0; struct taal_data *td = dev_get_drvdata(&dssdev->dev); - if (!td->enabled) - return -ENODEV; - if (size < w * h * 3) return -ENOMEM; + mutex_lock(&td->lock); + + if (!td->enabled) { + r = -ENODEV; + goto err1; + } + size = min(w * h * 3, dssdev->panel.timings.x_res * dssdev->panel.timings.y_res * 3); @@ -995,7 +1083,7 @@ static int taal_memory_read(struct omap_dss_device *dssdev, r = dsi_vc_set_max_rx_packet_size(TCH, plen); if (r) - goto err0; + goto err2; while (buf_used < size) { u8 dcs_cmd = first ? 0x2e : 0x3e; @@ -1006,7 +1094,7 @@ static int taal_memory_read(struct omap_dss_device *dssdev, if (r < 0) { dev_err(&dssdev->dev, "read error\n"); - goto err; + goto err3; } buf_used += r; @@ -1020,16 +1108,18 @@ static int taal_memory_read(struct omap_dss_device *dssdev, dev_err(&dssdev->dev, "signal pending, " "aborting memory read\n"); r = -ERESTARTSYS; - goto err; + goto err3; } } r = buf_used; -err: +err3: dsi_vc_set_max_rx_packet_size(TCH, 1); -err0: +err2: dsi_bus_unlock(); +err1: + mutex_unlock(&td->lock); return r; } @@ -1041,8 +1131,12 @@ static void taal_esd_work(struct work_struct *work) u8 state1, state2; int r; - if (!td->enabled) + mutex_lock(&td->lock); + + if (!td->enabled) { + mutex_unlock(&td->lock); return; + } dsi_bus_lock(); @@ -1084,16 +1178,19 @@ static void taal_esd_work(struct work_struct *work) queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); + mutex_unlock(&td->lock); return; err: dev_err(&dssdev->dev, "performing LCD reset\n"); - taal_disable(dssdev); - taal_enable(dssdev); + taal_power_off(dssdev); + taal_power_on(dssdev); dsi_bus_unlock(); queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); + + mutex_unlock(&td->lock); } static int taal_set_update_mode(struct omap_dss_device *dssdev, diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig index 87afb81..43b6440 100644 --- a/drivers/video/omap2/dss/Kconfig +++ b/drivers/video/omap2/dss/Kconfig @@ -36,6 +36,12 @@ config OMAP2_DSS_COLLECT_IRQ_STATS <debugfs>/omapdss/dispc_irq for DISPC interrupts, and <debugfs>/omapdss/dsi_irq for DSI interrupts. +config OMAP2_DSS_DPI + bool "DPI support" + default y + help + DPI Interface. This is the Parallel Display Interface. + config OMAP2_DSS_RFBI bool "RFBI support" default n diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile index 980c72c..d71b5d9 100644 --- a/drivers/video/omap2/dss/Makefile +++ b/drivers/video/omap2/dss/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_OMAP2_DSS) += omapdss.o -omapdss-y := core.o dss.o dispc.o dpi.o display.o manager.o overlay.o +omapdss-y := core.o dss.o dispc.o display.o manager.o overlay.o +omapdss-$(CONFIG_OMAP2_DSS_DPI) += dpi.o omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index 7ebe50b..b3a498f 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -482,6 +482,14 @@ static void dss_uninitialize_debugfs(void) if (dss_debugfs_dir) debugfs_remove_recursive(dss_debugfs_dir); } +#else /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */ +static inline int dss_initialize_debugfs(void) +{ + return 0; +} +static inline void dss_uninitialize_debugfs(void) +{ +} #endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */ /* PLATFORM DEVICE */ @@ -499,7 +507,7 @@ static int omap_dss_probe(struct platform_device *pdev) r = dss_get_clocks(); if (r) - goto fail0; + goto err_clocks; dss_clk_enable_all_no_ctx(); @@ -515,64 +523,64 @@ static int omap_dss_probe(struct platform_device *pdev) r = dss_init(skip_init); if (r) { DSSERR("Failed to initialize DSS\n"); - goto fail0; + goto err_dss; } -#ifdef CONFIG_OMAP2_DSS_RFBI r = rfbi_init(); if (r) { DSSERR("Failed to initialize rfbi\n"); - goto fail0; + goto err_rfbi; } -#endif r = dpi_init(pdev); if (r) { DSSERR("Failed to initialize dpi\n"); - goto fail0; + goto err_dpi; } r = dispc_init(); if (r) { DSSERR("Failed to initialize dispc\n"); - goto fail0; + goto err_dispc; } -#ifdef CONFIG_OMAP2_DSS_VENC + r = venc_init(pdev); if (r) { DSSERR("Failed to initialize venc\n"); - goto fail0; + goto err_venc; } -#endif + if (cpu_is_omap34xx()) { -#ifdef CONFIG_OMAP2_DSS_SDI r = sdi_init(skip_init); if (r) { DSSERR("Failed to initialize SDI\n"); - goto fail0; + goto err_sdi; } -#endif -#ifdef CONFIG_OMAP2_DSS_DSI + r = dsi_init(pdev); if (r) { DSSERR("Failed to initialize DSI\n"); - goto fail0; + goto err_dsi; } -#endif } -#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) r = dss_initialize_debugfs(); if (r) - goto fail0; -#endif + goto err_debugfs; for (i = 0; i < pdata->num_devices; ++i) { struct omap_dss_device *dssdev = pdata->devices[i]; r = omap_dss_register_device(dssdev); - if (r) - DSSERR("device reg failed %d\n", i); + if (r) { + DSSERR("device %d %s register failed %d\n", i, + dssdev->name ?: "unnamed", r); + + while (--i >= 0) + omap_dss_unregister_device(pdata->devices[i]); + + goto err_register; + } if (def_disp_name && strcmp(def_disp_name, dssdev->name) == 0) pdata->default_device = dssdev; @@ -582,8 +590,29 @@ static int omap_dss_probe(struct platform_device *pdev) return 0; - /* XXX fail correctly */ -fail0: +err_register: + dss_uninitialize_debugfs(); +err_debugfs: + if (cpu_is_omap34xx()) + dsi_exit(); +err_dsi: + if (cpu_is_omap34xx()) + sdi_exit(); +err_sdi: + venc_exit(); +err_venc: + dispc_exit(); +err_dispc: + dpi_exit(); +err_dpi: + rfbi_exit(); +err_rfbi: + dss_exit(); +err_dss: + dss_clk_disable_all_no_ctx(); + dss_put_clocks(); +err_clocks: + return r; } @@ -593,25 +622,15 @@ static int omap_dss_remove(struct platform_device *pdev) int i; int c; -#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) dss_uninitialize_debugfs(); -#endif -#ifdef CONFIG_OMAP2_DSS_VENC venc_exit(); -#endif dispc_exit(); dpi_exit(); -#ifdef CONFIG_OMAP2_DSS_RFBI rfbi_exit(); -#endif if (cpu_is_omap34xx()) { -#ifdef CONFIG_OMAP2_DSS_DSI dsi_exit(); -#endif -#ifdef CONFIG_OMAP2_DSS_SDI sdi_exit(); -#endif } dss_exit(); diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index 6a74ea1..ef8c852 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -392,7 +392,9 @@ void dss_init_device(struct platform_device *pdev, int r; switch (dssdev->type) { +#ifdef CONFIG_OMAP2_DSS_DPI case OMAP_DISPLAY_TYPE_DPI: +#endif #ifdef CONFIG_OMAP2_DSS_RFBI case OMAP_DISPLAY_TYPE_DBI: #endif @@ -413,9 +415,11 @@ void dss_init_device(struct platform_device *pdev, } switch (dssdev->type) { +#ifdef CONFIG_OMAP2_DSS_DPI case OMAP_DISPLAY_TYPE_DPI: r = dpi_init_display(dssdev); break; +#endif #ifdef CONFIG_OMAP2_DSS_RFBI case OMAP_DISPLAY_TYPE_DBI: r = rfbi_init_display(dssdev); @@ -541,7 +545,10 @@ int dss_resume_all_devices(void) static int dss_disable_device(struct device *dev, void *data) { struct omap_dss_device *dssdev = to_dss_device(dev); - dssdev->driver->disable(dssdev); + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) + dssdev->driver->disable(dssdev); + return 0; } diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index 5434418..24b1825 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -223,7 +223,13 @@ void dss_dump_clocks(struct seq_file *s) seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate); - seq_printf(s, "dss1_alwon_fclk = %lu / %lu * 2 = %lu\n", + if (cpu_is_omap3630()) + seq_printf(s, "dss1_alwon_fclk = %lu / %lu = %lu\n", + dpll4_ck_rate, + dpll4_ck_rate / dpll4_m4_ck_rate, + dss_clk_get_rate(DSS_CLK_FCK1)); + else + seq_printf(s, "dss1_alwon_fclk = %lu / %lu * 2 = %lu\n", dpll4_ck_rate, dpll4_ck_rate / dpll4_m4_ck_rate, dss_clk_get_rate(DSS_CLK_FCK1)); @@ -293,7 +299,8 @@ int dss_calc_clock_rates(struct dss_clock_info *cinfo) { unsigned long prate; - if (cinfo->fck_div > 16 || cinfo->fck_div == 0) + if (cinfo->fck_div > (cpu_is_omap3630() ? 32 : 16) || + cinfo->fck_div == 0) return -EINVAL; prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); @@ -329,7 +336,10 @@ int dss_get_clock_div(struct dss_clock_info *cinfo) if (cpu_is_omap34xx()) { unsigned long prate; prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); - cinfo->fck_div = prate / (cinfo->fck / 2); + if (cpu_is_omap3630()) + cinfo->fck_div = prate / (cinfo->fck); + else + cinfo->fck_div = prate / (cinfo->fck / 2); } else { cinfo->fck_div = 0; } @@ -402,10 +412,14 @@ retry: goto found; } else if (cpu_is_omap34xx()) { - for (fck_div = 16; fck_div > 0; --fck_div) { + for (fck_div = (cpu_is_omap3630() ? 32 : 16); + fck_div > 0; --fck_div) { struct dispc_clock_info cur_dispc; - fck = prate / fck_div * 2; + if (cpu_is_omap3630()) + fck = prate / fck_div; + else + fck = prate / fck_div * 2; if (fck > DISPC_MAX_FCK) continue; diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 24326a5..786f433 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -242,11 +242,22 @@ int dss_calc_clock_div(bool is_tft, unsigned long req_pck, struct dispc_clock_info *dispc_cinfo); /* SDI */ +#ifdef CONFIG_OMAP2_DSS_SDI int sdi_init(bool skip_init); void sdi_exit(void); int sdi_init_display(struct omap_dss_device *display); +#else +static inline int sdi_init(bool skip_init) +{ + return 0; +} +static inline void sdi_exit(void) +{ +} +#endif /* DSI */ +#ifdef CONFIG_OMAP2_DSS_DSI int dsi_init(struct platform_device *pdev); void dsi_exit(void); @@ -270,11 +281,30 @@ void dsi_pll_uninit(void); void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, u32 fifo_size, enum omap_burst_size *burst_size, u32 *fifo_low, u32 *fifo_high); +#else +static inline int dsi_init(struct platform_device *pdev) +{ + return 0; +} +static inline void dsi_exit(void) +{ +} +#endif /* DPI */ +#ifdef CONFIG_OMAP2_DSS_DPI int dpi_init(struct platform_device *pdev); void dpi_exit(void); int dpi_init_display(struct omap_dss_device *dssdev); +#else +static inline int dpi_init(struct platform_device *pdev) +{ + return 0; +} +static inline void dpi_exit(void) +{ +} +#endif /* DISPC */ int dispc_init(void); @@ -362,12 +392,23 @@ int dispc_get_clock_div(struct dispc_clock_info *cinfo); /* VENC */ +#ifdef CONFIG_OMAP2_DSS_VENC int venc_init(struct platform_device *pdev); void venc_exit(void); void venc_dump_regs(struct seq_file *s); int venc_init_display(struct omap_dss_device *display); +#else +static inline int venc_init(struct platform_device *pdev) +{ + return 0; +} +static inline void venc_exit(void) +{ +} +#endif /* RFBI */ +#ifdef CONFIG_OMAP2_DSS_RFBI int rfbi_init(void); void rfbi_exit(void); void rfbi_dump_regs(struct seq_file *s); @@ -379,6 +420,15 @@ void rfbi_transfer_area(u16 width, u16 height, void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t); unsigned long rfbi_get_max_tx_rate(void); int rfbi_init_display(struct omap_dss_device *display); +#else +static inline int rfbi_init(void) +{ + return 0; +} +static inline void rfbi_exit(void) +{ +} +#endif #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index 0820986..9e1fbe5 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -843,6 +843,7 @@ static void configure_manager(enum omap_channel channel) c = &dss_cache.manager_cache[channel]; + dispc_set_default_color(channel, c->default_color); dispc_set_trans_key(channel, c->trans_key_type, c->trans_key); dispc_enable_trans_key(channel, c->trans_enabled); dispc_enable_alpha_blending(channel, c->alpha_enabled); @@ -940,6 +941,22 @@ static int configure_dispc(void) return r; } +/* Make the coordinates even. There are some strange problems with OMAP and + * partial DSI update when the update widths are odd. */ +static void make_even(u16 *x, u16 *w) +{ + u16 x1, x2; + + x1 = *x; + x2 = *x + *w; + + x1 &= ~1; + x2 = ALIGN(x2, 2); + + *x = x1; + *w = x2 - x1; +} + /* Configure dispc for partial update. Return possibly modified update * area */ void dss_setup_partial_planes(struct omap_dss_device *dssdev, @@ -968,6 +985,8 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev, return; } + make_even(&x, &w); + spin_lock_irqsave(&dss_cache.lock, flags); /* We need to show the whole overlay if it is scaled. So look for @@ -1029,6 +1048,8 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev, w = x2 - x1; h = y2 - y1; + make_even(&x, &w); + DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n", i, x, y, w, h); } diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 12eb404..ee07a3c 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -23,13 +23,16 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> +#include <linux/regulator/consumer.h> #include <plat/display.h> +#include <plat/cpu.h> #include "dss.h" static struct { bool skip_init; bool update_enabled; + struct regulator *vdds_sdi_reg; } sdi; static void sdi_basic_init(void) @@ -57,6 +60,10 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) goto err0; } + r = regulator_enable(sdi.vdds_sdi_reg); + if (r) + goto err1; + /* In case of skip_init sdi_init has already enabled the clocks */ if (!sdi.skip_init) dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); @@ -115,19 +122,12 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) dssdev->manager->enable(dssdev->manager); - if (dssdev->driver->enable) { - r = dssdev->driver->enable(dssdev); - if (r) - goto err3; - } - sdi.skip_init = 0; return 0; -err3: - dssdev->manager->disable(dssdev->manager); err2: dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + regulator_disable(sdi.vdds_sdi_reg); err1: omap_dss_stop_device(dssdev); err0: @@ -137,15 +137,14 @@ EXPORT_SYMBOL(omapdss_sdi_display_enable); void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) { - if (dssdev->driver->disable) - dssdev->driver->disable(dssdev); - dssdev->manager->disable(dssdev->manager); dss_sdi_disable(); dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + regulator_disable(sdi.vdds_sdi_reg); + omap_dss_stop_device(dssdev); } EXPORT_SYMBOL(omapdss_sdi_display_disable); @@ -162,6 +161,11 @@ int sdi_init(bool skip_init) /* we store this for first display enable, then clear it */ sdi.skip_init = skip_init; + sdi.vdds_sdi_reg = dss_get_vdds_sdi(); + if (IS_ERR(sdi.vdds_sdi_reg)) { + DSSERR("can't get VDDS_SDI regulator\n"); + return PTR_ERR(sdi.vdds_sdi_reg); + } /* * Enable clocks already here, otherwise there would be a toggle * of them until sdi_display_enable is called. diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index f0ba573..eff3505 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -479,12 +479,6 @@ static int venc_panel_enable(struct omap_dss_device *dssdev) goto err1; } - if (dssdev->platform_enable) { - r = dssdev->platform_enable(dssdev); - if (r) - goto err2; - } - venc_power_on(dssdev); venc.wss_data = 0; @@ -494,13 +488,9 @@ static int venc_panel_enable(struct omap_dss_device *dssdev) /* wait couple of vsyncs until enabling the LCD */ msleep(50); - mutex_unlock(&venc.venc_lock); - - return r; -err2: - venc_power_off(dssdev); err1: mutex_unlock(&venc.venc_lock); + return r; } @@ -524,9 +514,6 @@ static void venc_panel_disable(struct omap_dss_device *dssdev) /* wait at least 5 vsyncs after disabling the LCD */ msleep(100); - if (dssdev->platform_disable) - dssdev->platform_disable(dssdev); - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; end: mutex_unlock(&venc.venc_lock); diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index 1ffa760..9c73618 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -183,13 +183,14 @@ int omapfb_update_window(struct fb_info *fbi, struct omapfb2_device *fbdev = ofbi->fbdev; int r; + if (!lock_fb_info(fbi)) + return -ENODEV; omapfb_lock(fbdev); - lock_fb_info(fbi); r = omapfb_update_window_nolock(fbi, x, y, w, h); - unlock_fb_info(fbi); omapfb_unlock(fbdev); + unlock_fb_info(fbi); return r; } diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c index 62bb88f..5179219 100644 --- a/drivers/video/omap2/omapfb/omapfb-sysfs.c +++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c @@ -57,7 +57,8 @@ static ssize_t store_rotate_type(struct device *dev, if (rot_type != OMAP_DSS_ROT_DMA && rot_type != OMAP_DSS_ROT_VRFB) return -EINVAL; - lock_fb_info(fbi); + if (!lock_fb_info(fbi)) + return -ENODEV; r = 0; if (rot_type == ofbi->rotation_type) @@ -105,7 +106,8 @@ static ssize_t store_mirror(struct device *dev, if (mirror != 0 && mirror != 1) return -EINVAL; - lock_fb_info(fbi); + if (!lock_fb_info(fbi)) + return -ENODEV; ofbi->mirror = mirror; @@ -137,8 +139,9 @@ static ssize_t show_overlays(struct device *dev, ssize_t l = 0; int t; + if (!lock_fb_info(fbi)) + return -ENODEV; omapfb_lock(fbdev); - lock_fb_info(fbi); for (t = 0; t < ofbi->num_overlays; t++) { struct omap_overlay *ovl = ofbi->overlays[t]; @@ -154,8 +157,8 @@ static ssize_t show_overlays(struct device *dev, l += snprintf(buf + l, PAGE_SIZE - l, "\n"); - unlock_fb_info(fbi); omapfb_unlock(fbdev); + unlock_fb_info(fbi); return l; } @@ -195,8 +198,9 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr, if (buf[len - 1] == '\n') len = len - 1; + if (!lock_fb_info(fbi)) + return -ENODEV; omapfb_lock(fbdev); - lock_fb_info(fbi); if (len > 0) { char *p = (char *)buf; @@ -303,8 +307,8 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr, r = count; out: - unlock_fb_info(fbi); omapfb_unlock(fbdev); + unlock_fb_info(fbi); return r; } @@ -317,7 +321,8 @@ static ssize_t show_overlays_rotate(struct device *dev, ssize_t l = 0; int t; - lock_fb_info(fbi); + if (!lock_fb_info(fbi)) + return -ENODEV; for (t = 0; t < ofbi->num_overlays; t++) { l += snprintf(buf + l, PAGE_SIZE - l, "%s%d", @@ -345,7 +350,8 @@ static ssize_t store_overlays_rotate(struct device *dev, if (buf[len - 1] == '\n') len = len - 1; - lock_fb_info(fbi); + if (!lock_fb_info(fbi)) + return -ENODEV; if (len > 0) { char *p = (char *)buf; @@ -416,7 +422,8 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr, size = PAGE_ALIGN(simple_strtoul(buf, NULL, 0)); - lock_fb_info(fbi); + if (!lock_fb_info(fbi)) + return -ENODEV; for (i = 0; i < ofbi->num_overlays; i++) { if (ofbi->overlays[i]->info.enabled) { diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c index 0cadf7a..090aa1a 100644 --- a/drivers/video/vesafb.c +++ b/drivers/video/vesafb.c @@ -177,7 +177,7 @@ static void vesafb_destroy(struct fb_info *info) { if (info->screen_base) iounmap(info->screen_base); - release_mem_region(info->aperture_base, info->aperture_size); + release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size); framebuffer_release(info); } @@ -295,8 +295,13 @@ static int __init vesafb_probe(struct platform_device *dev) info->par = NULL; /* set vesafb aperture size for generic probing */ - info->aperture_base = screen_info.lfb_base; - info->aperture_size = size_total; + info->apertures = alloc_apertures(1); + if (!info->apertures) { + err = -ENOMEM; + goto err; + } + info->apertures->ranges[0].base = screen_info.lfb_base; + info->apertures->ranges[0].size = size_total; info->screen_base = ioremap(vesafb_fix.smem_start, vesafb_fix.smem_len); if (!info->screen_base) { diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c index bf638a4..149c47a 100644 --- a/drivers/video/vga16fb.c +++ b/drivers/video/vga16fb.c @@ -1263,10 +1263,19 @@ static void vga16fb_imageblit(struct fb_info *info, const struct fb_image *image vga_imageblit_color(info, image); } +static void vga16fb_destroy(struct fb_info *info) +{ + iounmap(info->screen_base); + fb_dealloc_cmap(&info->cmap); + /* XXX unshare VGA regions */ + framebuffer_release(info); +} + static struct fb_ops vga16fb_ops = { .owner = THIS_MODULE, .fb_open = vga16fb_open, .fb_release = vga16fb_release, + .fb_destroy = vga16fb_destroy, .fb_check_var = vga16fb_check_var, .fb_set_par = vga16fb_set_par, .fb_setcolreg = vga16fb_setcolreg, @@ -1306,6 +1315,11 @@ static int __devinit vga16fb_probe(struct platform_device *dev) ret = -ENOMEM; goto err_fb_alloc; } + info->apertures = alloc_apertures(1); + if (!info->apertures) { + ret = -ENOMEM; + goto err_ioremap; + } /* XXX share VGA_FB_PHYS and I/O region with vgacon and others */ info->screen_base = (void __iomem *)VGA_MAP_MEM(VGA_FB_PHYS, 0); @@ -1335,7 +1349,7 @@ static int __devinit vga16fb_probe(struct platform_device *dev) info->fix = vga16fb_fix; /* supports rectangles with widths of multiples of 8 */ info->pixmap.blit_x = 1 << 7 | 1 << 15 | 1 << 23 | 1 << 31; - info->flags = FBINFO_FLAG_DEFAULT | + info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE | FBINFO_HWACCEL_YPAN; i = (info->var.bits_per_pixel == 8) ? 256 : 16; @@ -1354,6 +1368,9 @@ static int __devinit vga16fb_probe(struct platform_device *dev) vga16fb_update_fix(info); + info->apertures->ranges[0].base = VGA_FB_PHYS; + info->apertures->ranges[0].size = VGA_FB_PHYS_LEN; + if (register_framebuffer(info) < 0) { printk(KERN_ERR "vga16fb: unable to register framebuffer\n"); ret = -EINVAL; @@ -1380,13 +1397,8 @@ static int vga16fb_remove(struct platform_device *dev) { struct fb_info *info = platform_get_drvdata(dev); - if (info) { + if (info) unregister_framebuffer(info); - iounmap(info->screen_base); - fb_dealloc_cmap(&info->cmap); - /* XXX unshare VGA regions */ - framebuffer_release(info); - } return 0; } diff --git a/drivers/video/via/Makefile b/drivers/video/via/Makefile index eeed238..d496adb 100644 --- a/drivers/video/via/Makefile +++ b/drivers/video/via/Makefile @@ -4,4 +4,6 @@ obj-$(CONFIG_FB_VIA) += viafb.o -viafb-y :=viafbdev.o hw.o via_i2c.o dvi.o lcd.o ioctl.o accel.o via_utility.o vt1636.o global.o tblDPASetting.o viamode.o tbl1636.o +viafb-y :=viafbdev.o hw.o via_i2c.o dvi.o lcd.o ioctl.o accel.o \ + via_utility.o vt1636.o global.o tblDPASetting.o viamode.o tbl1636.o \ + via-core.o via-gpio.o via_modesetting.o diff --git a/drivers/video/via/accel.c b/drivers/video/via/accel.c index d5077df..e44893e 100644 --- a/drivers/video/via/accel.c +++ b/drivers/video/via/accel.c @@ -18,14 +18,45 @@ * Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <linux/via-core.h> #include "global.h" +/* + * Figure out an appropriate bytes-per-pixel setting. + */ +static int viafb_set_bpp(void __iomem *engine, u8 bpp) +{ + u32 gemode; + + /* Preserve the reserved bits */ + /* Lowest 2 bits to zero gives us no rotation */ + gemode = readl(engine + VIA_REG_GEMODE) & 0xfffffcfc; + switch (bpp) { + case 8: + gemode |= VIA_GEM_8bpp; + break; + case 16: + gemode |= VIA_GEM_16bpp; + break; + case 32: + gemode |= VIA_GEM_32bpp; + break; + default: + printk(KERN_WARNING "viafb_set_bpp: Unsupported bpp %d\n", bpp); + return -EINVAL; + } + writel(gemode, engine + VIA_REG_GEMODE); + return 0; +} + + static int hw_bitblt_1(void __iomem *engine, u8 op, u32 width, u32 height, u8 dst_bpp, u32 dst_addr, u32 dst_pitch, u32 dst_x, u32 dst_y, u32 *src_mem, u32 src_addr, u32 src_pitch, u32 src_x, u32 src_y, u32 fg_color, u32 bg_color, u8 fill_rop) { u32 ge_cmd = 0, tmp, i; + int ret; if (!op || op > 3) { printk(KERN_WARNING "hw_bitblt_1: Invalid operation: %d\n", op); @@ -59,22 +90,9 @@ static int hw_bitblt_1(void __iomem *engine, u8 op, u32 width, u32 height, } } - switch (dst_bpp) { - case 8: - tmp = 0x00000000; - break; - case 16: - tmp = 0x00000100; - break; - case 32: - tmp = 0x00000300; - break; - default: - printk(KERN_WARNING "hw_bitblt_1: Unsupported bpp %d\n", - dst_bpp); - return -EINVAL; - } - writel(tmp, engine + 0x04); + ret = viafb_set_bpp(engine, dst_bpp); + if (ret) + return ret; if (op != VIA_BITBLT_FILL) { if (src_x & (op == VIA_BITBLT_MONO ? 0xFFFF8000 : 0xFFFFF000) @@ -171,6 +189,7 @@ static int hw_bitblt_2(void __iomem *engine, u8 op, u32 width, u32 height, u32 fg_color, u32 bg_color, u8 fill_rop) { u32 ge_cmd = 0, tmp, i; + int ret; if (!op || op > 3) { printk(KERN_WARNING "hw_bitblt_2: Invalid operation: %d\n", op); @@ -204,22 +223,9 @@ static int hw_bitblt_2(void __iomem *engine, u8 op, u32 width, u32 height, } } - switch (dst_bpp) { - case 8: - tmp = 0x00000000; - break; - case 16: - tmp = 0x00000100; - break; - case 32: - tmp = 0x00000300; - break; - default: - printk(KERN_WARNING "hw_bitblt_2: Unsupported bpp %d\n", - dst_bpp); - return -EINVAL; - } - writel(tmp, engine + 0x04); + ret = viafb_set_bpp(engine, dst_bpp); + if (ret) + return ret; if (op == VIA_BITBLT_FILL) tmp = 0; @@ -312,17 +318,29 @@ int viafb_init_engine(struct fb_info *info) { struct viafb_par *viapar = info->par; void __iomem *engine; + int highest_reg, i; u32 vq_start_addr, vq_end_addr, vq_start_low, vq_end_low, vq_high, vq_len, chip_name = viapar->shared->chip_info.gfx_chip_name; - engine = ioremap_nocache(info->fix.mmio_start, info->fix.mmio_len); - viapar->shared->engine_mmio = engine; + engine = viapar->shared->vdev->engine_mmio; if (!engine) { printk(KERN_WARNING "viafb_init_accel: ioremap failed, " "hardware acceleration disabled\n"); return -ENOMEM; } + /* Initialize registers to reset the 2D engine */ + switch (viapar->shared->chip_info.twod_engine) { + case VIA_2D_ENG_M1: + highest_reg = 0x5c; + break; + default: + highest_reg = 0x40; + break; + } + for (i = 0; i <= highest_reg; i += 4) + writel(0x0, engine + i); + switch (chip_name) { case UNICHROME_CLE266: case UNICHROME_K400: @@ -352,13 +370,28 @@ int viafb_init_engine(struct fb_info *info) viapar->shared->vq_vram_addr = viapar->fbmem_free; viapar->fbmem_used += VQ_SIZE; - /* Init 2D engine reg to reset 2D engine */ - writel(0x0, engine + VIA_REG_KEYCONTROL); +#if defined(CONFIG_FB_VIA_CAMERA) || defined(CONFIG_FB_VIA_CAMERA_MODULE) + /* + * Set aside a chunk of framebuffer memory for the camera + * driver. Someday this driver probably needs a proper allocator + * for fbmem; for now, we just have to do this before the + * framebuffer initializes itself. + * + * As for the size: the engine can handle three frames, + * 16 bits deep, up to VGA resolution. + */ + viapar->shared->vdev->camera_fbmem_size = 3*VGA_HEIGHT*VGA_WIDTH*2; + viapar->fbmem_free -= viapar->shared->vdev->camera_fbmem_size; + viapar->fbmem_used += viapar->shared->vdev->camera_fbmem_size; + viapar->shared->vdev->camera_fbmem_offset = viapar->fbmem_free; +#endif /* Init AGP and VQ regs */ switch (chip_name) { case UNICHROME_K8M890: case UNICHROME_P4M900: + case UNICHROME_VX800: + case UNICHROME_VX855: writel(0x00100000, engine + VIA_REG_CR_TRANSET); writel(0x680A0000, engine + VIA_REG_CR_TRANSPACE); writel(0x02000000, engine + VIA_REG_CR_TRANSPACE); @@ -393,6 +426,8 @@ int viafb_init_engine(struct fb_info *info) switch (chip_name) { case UNICHROME_K8M890: case UNICHROME_P4M900: + case UNICHROME_VX800: + case UNICHROME_VX855: vq_start_low |= 0x20000000; vq_end_low |= 0x20000000; vq_high |= 0x20000000; @@ -446,7 +481,7 @@ void viafb_show_hw_cursor(struct fb_info *info, int Status) struct viafb_par *viapar = info->par; u32 temp, iga_path = viapar->iga_path; - temp = readl(viapar->shared->engine_mmio + VIA_REG_CURSOR_MODE); + temp = readl(viapar->shared->vdev->engine_mmio + VIA_REG_CURSOR_MODE); switch (Status) { case HW_Cursor_ON: temp |= 0x1; @@ -463,23 +498,33 @@ void viafb_show_hw_cursor(struct fb_info *info, int Status) default: temp &= 0x7FFFFFFF; } - writel(temp, viapar->shared->engine_mmio + VIA_REG_CURSOR_MODE); + writel(temp, viapar->shared->vdev->engine_mmio + VIA_REG_CURSOR_MODE); } void viafb_wait_engine_idle(struct fb_info *info) { struct viafb_par *viapar = info->par; int loop = 0; - - while (!(readl(viapar->shared->engine_mmio + VIA_REG_STATUS) & - VIA_VR_QUEUE_BUSY) && (loop < MAXLOOP)) { - loop++; - cpu_relax(); + u32 mask; + void __iomem *engine = viapar->shared->vdev->engine_mmio; + + switch (viapar->shared->chip_info.twod_engine) { + case VIA_2D_ENG_H5: + case VIA_2D_ENG_M1: + mask = VIA_CMD_RGTR_BUSY_M1 | VIA_2D_ENG_BUSY_M1 | + VIA_3D_ENG_BUSY_M1; + break; + default: + while (!(readl(engine + VIA_REG_STATUS) & + VIA_VR_QUEUE_BUSY) && (loop < MAXLOOP)) { + loop++; + cpu_relax(); + } + mask = VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY | VIA_3D_ENG_BUSY; + break; } - while ((readl(viapar->shared->engine_mmio + VIA_REG_STATUS) & - (VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY | VIA_3D_ENG_BUSY)) && - (loop < MAXLOOP)) { + while ((readl(engine + VIA_REG_STATUS) & mask) && (loop < MAXLOOP)) { loop++; cpu_relax(); } diff --git a/drivers/video/via/accel.h b/drivers/video/via/accel.h index 615c84a..2c122d2 100644 --- a/drivers/video/via/accel.h +++ b/drivers/video/via/accel.h @@ -67,6 +67,34 @@ /* from 0x100 to 0x1ff */ #define VIA_REG_COLORPAT 0x100 +/* defines for VIA 2D registers for vt3353/3409 (M1 engine)*/ +#define VIA_REG_GECMD_M1 0x000 +#define VIA_REG_GEMODE_M1 0x004 +#define VIA_REG_GESTATUS_M1 0x004 /* as same as VIA_REG_GEMODE */ +#define VIA_REG_PITCH_M1 0x008 /* pitch of src and dst */ +#define VIA_REG_DIMENSION_M1 0x00C /* width and height */ +#define VIA_REG_DSTPOS_M1 0x010 +#define VIA_REG_LINE_XY_M1 0x010 +#define VIA_REG_DSTBASE_M1 0x014 +#define VIA_REG_SRCPOS_M1 0x018 +#define VIA_REG_LINE_K1K2_M1 0x018 +#define VIA_REG_SRCBASE_M1 0x01C +#define VIA_REG_PATADDR_M1 0x020 +#define VIA_REG_MONOPAT0_M1 0x024 +#define VIA_REG_MONOPAT1_M1 0x028 +#define VIA_REG_OFFSET_M1 0x02C +#define VIA_REG_LINE_ERROR_M1 0x02C +#define VIA_REG_CLIPTL_M1 0x040 /* top and left of clipping */ +#define VIA_REG_CLIPBR_M1 0x044 /* bottom and right of clipping */ +#define VIA_REG_KEYCONTROL_M1 0x048 /* color key control */ +#define VIA_REG_FGCOLOR_M1 0x04C +#define VIA_REG_DSTCOLORKEY_M1 0x04C /* as same as VIA_REG_FG */ +#define VIA_REG_BGCOLOR_M1 0x050 +#define VIA_REG_SRCCOLORKEY_M1 0x050 /* as same as VIA_REG_BG */ +#define VIA_REG_MONOPATFGC_M1 0x058 /* Add BG color of Pattern. */ +#define VIA_REG_MONOPATBGC_M1 0x05C /* Add FG color of Pattern. */ +#define VIA_REG_COLORPAT_M1 0x100 /* from 0x100 to 0x1ff */ + /* VIA_REG_PITCH(0x38): Pitch Setting */ #define VIA_PITCH_ENABLE 0x80000000 @@ -157,6 +185,18 @@ /* Virtual Queue is busy */ #define VIA_VR_QUEUE_BUSY 0x00020000 +/* VIA_REG_STATUS(0x400): Engine Status for H5 */ +#define VIA_CMD_RGTR_BUSY_H5 0x00000010 /* Command Regulator is busy */ +#define VIA_2D_ENG_BUSY_H5 0x00000002 /* 2D Engine is busy */ +#define VIA_3D_ENG_BUSY_H5 0x00001FE1 /* 3D Engine is busy */ +#define VIA_VR_QUEUE_BUSY_H5 0x00000004 /* Virtual Queue is busy */ + +/* VIA_REG_STATUS(0x400): Engine Status for VT3353/3409 */ +#define VIA_CMD_RGTR_BUSY_M1 0x00000010 /* Command Regulator is busy */ +#define VIA_2D_ENG_BUSY_M1 0x00000002 /* 2D Engine is busy */ +#define VIA_3D_ENG_BUSY_M1 0x00001FE1 /* 3D Engine is busy */ +#define VIA_VR_QUEUE_BUSY_M1 0x00000004 /* Virtual Queue is busy */ + #define MAXLOOP 0xFFFFFF #define VIA_BITBLT_COLOR 1 diff --git a/drivers/video/via/chip.h b/drivers/video/via/chip.h index 8c06bd3..d9b6e06 100644 --- a/drivers/video/via/chip.h +++ b/drivers/video/via/chip.h @@ -121,9 +121,17 @@ struct lvds_chip_information { int i2c_port; }; +/* The type of 2D engine */ +enum via_2d_engine { + VIA_2D_ENG_H2, + VIA_2D_ENG_H5, + VIA_2D_ENG_M1, +}; + struct chip_information { int gfx_chip_name; int gfx_chip_revision; + enum via_2d_engine twod_engine; struct tmds_chip_information tmds_chip_info; struct lvds_chip_information lvds_chip_info; struct lvds_chip_information lvds_chip_info2; diff --git a/drivers/video/via/dvi.c b/drivers/video/via/dvi.c index abe59b8..39b040b 100644 --- a/drivers/video/via/dvi.c +++ b/drivers/video/via/dvi.c @@ -18,6 +18,8 @@ * Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <linux/via-core.h> +#include <linux/via_i2c.h> #include "global.h" static void tmds_register_write(int index, u8 data); @@ -96,7 +98,7 @@ int viafb_tmds_trasmitter_identify(void) viaparinfo->chip_info->tmds_chip_info.tmds_chip_name = VT1632_TMDS; viaparinfo->chip_info-> tmds_chip_info.tmds_chip_slave_addr = VT1632_TMDS_I2C_ADDR; - viaparinfo->chip_info->tmds_chip_info.i2c_port = I2CPORTINDEX; + viaparinfo->chip_info->tmds_chip_info.i2c_port = VIA_PORT_31; if (check_tmds_chip(VT1632_DEVICE_ID_REG, VT1632_DEVICE_ID) != FAIL) { /* * Currently only support 12bits,dual edge,add 24bits mode later @@ -110,7 +112,7 @@ int viafb_tmds_trasmitter_identify(void) viaparinfo->chip_info->tmds_chip_info.i2c_port); return OK; } else { - viaparinfo->chip_info->tmds_chip_info.i2c_port = GPIOPORTINDEX; + viaparinfo->chip_info->tmds_chip_info.i2c_port = VIA_PORT_2C; if (check_tmds_chip(VT1632_DEVICE_ID_REG, VT1632_DEVICE_ID) != FAIL) { tmds_register_write(0x08, 0x3b); @@ -160,32 +162,26 @@ int viafb_tmds_trasmitter_identify(void) static void tmds_register_write(int index, u8 data) { - viaparinfo->shared->i2c_stuff.i2c_port = - viaparinfo->chip_info->tmds_chip_info.i2c_port; - - viafb_i2c_writebyte(viaparinfo->chip_info->tmds_chip_info. - tmds_chip_slave_addr, index, - data); + viafb_i2c_writebyte(viaparinfo->chip_info->tmds_chip_info.i2c_port, + viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr, + index, data); } static int tmds_register_read(int index) { u8 data; - viaparinfo->shared->i2c_stuff.i2c_port = - viaparinfo->chip_info->tmds_chip_info.i2c_port; - viafb_i2c_readbyte((u8) viaparinfo->chip_info-> - tmds_chip_info.tmds_chip_slave_addr, - (u8) index, &data); + viafb_i2c_readbyte(viaparinfo->chip_info->tmds_chip_info.i2c_port, + (u8) viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr, + (u8) index, &data); return data; } static int tmds_register_read_bytes(int index, u8 *buff, int buff_len) { - viaparinfo->shared->i2c_stuff.i2c_port = - viaparinfo->chip_info->tmds_chip_info.i2c_port; - viafb_i2c_readbytes((u8) viaparinfo->chip_info->tmds_chip_info. - tmds_chip_slave_addr, (u8) index, buff, buff_len); + viafb_i2c_readbytes(viaparinfo->chip_info->tmds_chip_info.i2c_port, + (u8) viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr, + (u8) index, buff, buff_len); return 0; } @@ -541,9 +537,10 @@ void viafb_dvi_enable(void) else data = 0x37; viafb_i2c_writebyte(viaparinfo->chip_info-> - tmds_chip_info. - tmds_chip_slave_addr, - 0x08, data); + tmds_chip_info.i2c_port, + viaparinfo->chip_info-> + tmds_chip_info.tmds_chip_slave_addr, + 0x08, data); } } } diff --git a/drivers/video/via/global.h b/drivers/video/via/global.h index 8d95d5f..28221a0 100644 --- a/drivers/video/via/global.h +++ b/drivers/video/via/global.h @@ -41,7 +41,6 @@ #include "share.h" #include "dvi.h" #include "viamode.h" -#include "via_i2c.h" #include "hw.h" #include "lcd.h" diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c index f2583b1..b996803 100644 --- a/drivers/video/via/hw.c +++ b/drivers/video/via/hw.c @@ -19,6 +19,7 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <linux/via-core.h> #include "global.h" static struct pll_map pll_value[] = { @@ -62,6 +63,7 @@ static struct pll_map pll_value[] = { CX700_52_977M, VX855_52_977M}, {CLK_56_250M, CLE266_PLL_56_250M, K800_PLL_56_250M, CX700_56_250M, VX855_56_250M}, + {CLK_57_275M, 0, 0, 0, VX855_57_275M}, {CLK_60_466M, CLE266_PLL_60_466M, K800_PLL_60_466M, CX700_60_466M, VX855_60_466M}, {CLK_61_500M, CLE266_PLL_61_500M, K800_PLL_61_500M, @@ -525,8 +527,7 @@ static void dvi_patch_skew_dvp_low(void); static void set_dvi_output_path(int set_iga, int output_interface); static void set_lcd_output_path(int set_iga, int output_interface); static void load_fix_bit_crtc_reg(void); -static void init_gfx_chip_info(struct pci_dev *pdev, - const struct pci_device_id *pdi); +static void init_gfx_chip_info(int chip_type); static void init_tmds_chip_info(void); static void init_lvds_chip_info(void); static void device_screen_off(void); @@ -537,18 +538,6 @@ static void device_on(void); static void enable_second_display_channel(void); static void disable_second_display_channel(void); -void viafb_write_reg(u8 index, u16 io_port, u8 data) -{ - outb(index, io_port); - outb(data, io_port + 1); - /*DEBUG_MSG(KERN_INFO "\nIndex=%2d Value=%2d", index, data); */ -} -u8 viafb_read_reg(int io_port, u8 index) -{ - outb(index, io_port); - return inb(io_port + 1); -} - void viafb_lock_crt(void) { viafb_write_reg_mask(CR11, VIACR, BIT7, BIT7); @@ -560,16 +549,6 @@ void viafb_unlock_crt(void) viafb_write_reg_mask(CR47, VIACR, 0, BIT0); } -void viafb_write_reg_mask(u8 index, int io_port, u8 data, u8 mask) -{ - u8 tmp; - - outb(index, io_port); - tmp = inb(io_port + 1); - outb((data & mask) | (tmp & (~mask)), io_port + 1); - /*DEBUG_MSG(KERN_INFO "\nIndex=%2d Value=%2d", index, tmp); */ -} - void write_dac_reg(u8 index, u8 r, u8 g, u8 b) { outb(index, LUT_INDEX_WRITE); @@ -646,102 +625,6 @@ void viafb_set_iga_path(void) } } -void viafb_set_primary_address(u32 addr) -{ - DEBUG_MSG(KERN_DEBUG "viafb_set_primary_address(0x%08X)\n", addr); - viafb_write_reg(CR0D, VIACR, addr & 0xFF); - viafb_write_reg(CR0C, VIACR, (addr >> 8) & 0xFF); - viafb_write_reg(CR34, VIACR, (addr >> 16) & 0xFF); - viafb_write_reg_mask(CR48, VIACR, (addr >> 24) & 0x1F, 0x1F); -} - -void viafb_set_secondary_address(u32 addr) -{ - DEBUG_MSG(KERN_DEBUG "viafb_set_secondary_address(0x%08X)\n", addr); - /* secondary display supports only quadword aligned memory */ - viafb_write_reg_mask(CR62, VIACR, (addr >> 2) & 0xFE, 0xFE); - viafb_write_reg(CR63, VIACR, (addr >> 10) & 0xFF); - viafb_write_reg(CR64, VIACR, (addr >> 18) & 0xFF); - viafb_write_reg_mask(CRA3, VIACR, (addr >> 26) & 0x07, 0x07); -} - -void viafb_set_primary_pitch(u32 pitch) -{ - DEBUG_MSG(KERN_DEBUG "viafb_set_primary_pitch(0x%08X)\n", pitch); - /* spec does not say that first adapter skips 3 bits but old - * code did it and seems to be reasonable in analogy to 2nd adapter - */ - pitch = pitch >> 3; - viafb_write_reg(0x13, VIACR, pitch & 0xFF); - viafb_write_reg_mask(0x35, VIACR, (pitch >> (8 - 5)) & 0xE0, 0xE0); -} - -void viafb_set_secondary_pitch(u32 pitch) -{ - DEBUG_MSG(KERN_DEBUG "viafb_set_secondary_pitch(0x%08X)\n", pitch); - pitch = pitch >> 3; - viafb_write_reg(0x66, VIACR, pitch & 0xFF); - viafb_write_reg_mask(0x67, VIACR, (pitch >> 8) & 0x03, 0x03); - viafb_write_reg_mask(0x71, VIACR, (pitch >> (10 - 7)) & 0x80, 0x80); -} - -void viafb_set_primary_color_depth(u8 depth) -{ - u8 value; - - DEBUG_MSG(KERN_DEBUG "viafb_set_primary_color_depth(%d)\n", depth); - switch (depth) { - case 8: - value = 0x00; - break; - case 15: - value = 0x04; - break; - case 16: - value = 0x14; - break; - case 24: - value = 0x0C; - break; - case 30: - value = 0x08; - break; - default: - printk(KERN_WARNING "viafb_set_primary_color_depth: " - "Unsupported depth: %d\n", depth); - return; - } - - viafb_write_reg_mask(0x15, VIASR, value, 0x1C); -} - -void viafb_set_secondary_color_depth(u8 depth) -{ - u8 value; - - DEBUG_MSG(KERN_DEBUG "viafb_set_secondary_color_depth(%d)\n", depth); - switch (depth) { - case 8: - value = 0x00; - break; - case 16: - value = 0x40; - break; - case 24: - value = 0xC0; - break; - case 30: - value = 0x80; - break; - default: - printk(KERN_WARNING "viafb_set_secondary_color_depth: " - "Unsupported depth: %d\n", depth); - return; - } - - viafb_write_reg_mask(0x67, VIACR, value, 0xC0); -} - static void set_color_register(u8 index, u8 red, u8 green, u8 blue) { outb(0xFF, 0x3C6); /* bit mask of palette */ @@ -1126,16 +1009,12 @@ void viafb_load_reg(int timing_value, int viafb_load_reg_num, void viafb_write_regx(struct io_reg RegTable[], int ItemNum) { int i; - unsigned char RegTemp; /*DEBUG_MSG(KERN_INFO "Table Size : %x!!\n",ItemNum ); */ - for (i = 0; i < ItemNum; i++) { - outb(RegTable[i].index, RegTable[i].port); - RegTemp = inb(RegTable[i].port + 1); - RegTemp = (RegTemp & (~RegTable[i].mask)) | RegTable[i].value; - outb(RegTemp, RegTable[i].port + 1); - } + for (i = 0; i < ItemNum; i++) + via_write_reg_mask(RegTable[i].port, RegTable[i].index, + RegTable[i].value, RegTable[i].mask); } void viafb_load_fetch_count_reg(int h_addr, int bpp_byte, int set_iga) @@ -1516,8 +1395,6 @@ u32 viafb_get_clk_value(int clk) /* Set VCLK*/ void viafb_set_vclock(u32 CLK, int set_iga) { - unsigned char RegTemp; - /* H.W. Reset : ON */ viafb_write_reg_mask(CR17, VIACR, 0x00, BIT7); @@ -1590,8 +1467,7 @@ void viafb_set_vclock(u32 CLK, int set_iga) } /* Fire! */ - RegTemp = inb(VIARMisc); - outb(RegTemp | (BIT2 + BIT3), VIAWMisc); + via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */ } void viafb_load_crtc_timing(struct display_timing device_timing, @@ -1835,6 +1711,7 @@ void viafb_fill_crtc_timing(struct crt_mode_table *crt_table, int index = 0; int h_addr, v_addr; u32 pll_D_N; + u8 polarity = 0; for (i = 0; i < video_mode->mode_array; i++) { index = i; @@ -1863,20 +1740,11 @@ void viafb_fill_crtc_timing(struct crt_mode_table *crt_table, v_addr = crt_reg.ver_addr; /* update polarity for CRT timing */ - if (crt_table[index].h_sync_polarity == NEGATIVE) { - if (crt_table[index].v_sync_polarity == NEGATIVE) - outb((inb(VIARMisc) & (~(BIT6 + BIT7))) | - (BIT6 + BIT7), VIAWMisc); - else - outb((inb(VIARMisc) & (~(BIT6 + BIT7))) | (BIT6), - VIAWMisc); - } else { - if (crt_table[index].v_sync_polarity == NEGATIVE) - outb((inb(VIARMisc) & (~(BIT6 + BIT7))) | (BIT7), - VIAWMisc); - else - outb((inb(VIARMisc) & (~(BIT6 + BIT7))), VIAWMisc); - } + if (crt_table[index].h_sync_polarity == NEGATIVE) + polarity |= BIT6; + if (crt_table[index].v_sync_polarity == NEGATIVE) + polarity |= BIT7; + via_write_misc_reg_mask(polarity, BIT6 | BIT7); if (set_iga == IGA1) { viafb_unlock_crt(); @@ -1910,10 +1778,9 @@ void viafb_fill_crtc_timing(struct crt_mode_table *crt_table, } -void viafb_init_chip_info(struct pci_dev *pdev, - const struct pci_device_id *pdi) +void viafb_init_chip_info(int chip_type) { - init_gfx_chip_info(pdev, pdi); + init_gfx_chip_info(chip_type); init_tmds_chip_info(); init_lvds_chip_info(); @@ -1980,12 +1847,11 @@ void viafb_update_device_setting(int hres, int vres, } } -static void init_gfx_chip_info(struct pci_dev *pdev, - const struct pci_device_id *pdi) +static void init_gfx_chip_info(int chip_type) { u8 tmp; - viaparinfo->chip_info->gfx_chip_name = pdi->driver_data; + viaparinfo->chip_info->gfx_chip_name = chip_type; /* Check revision of CLE266 Chip */ if (viaparinfo->chip_info->gfx_chip_name == UNICHROME_CLE266) { @@ -2016,6 +1882,21 @@ static void init_gfx_chip_info(struct pci_dev *pdev, CX700_REVISION_700; } } + + /* Determine which 2D engine we have */ + switch (viaparinfo->chip_info->gfx_chip_name) { + case UNICHROME_VX800: + case UNICHROME_VX855: + viaparinfo->chip_info->twod_engine = VIA_2D_ENG_M1; + break; + case UNICHROME_K8M890: + case UNICHROME_P4M900: + viaparinfo->chip_info->twod_engine = VIA_2D_ENG_H5; + break; + default: + viaparinfo->chip_info->twod_engine = VIA_2D_ENG_H2; + break; + } } static void init_tmds_chip_info(void) @@ -2232,13 +2113,11 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, /* Fill VPIT Parameters */ /* Write Misc Register */ - outb(VPIT.Misc, VIAWMisc); + outb(VPIT.Misc, VIA_MISC_REG_WRITE); /* Write Sequencer */ - for (i = 1; i <= StdSR; i++) { - outb(i, VIASR); - outb(VPIT.SR[i - 1], VIASR + 1); - } + for (i = 1; i <= StdSR; i++) + via_write_reg(VIASR, i, VPIT.SR[i - 1]); viafb_write_reg_mask(0x15, VIASR, 0xA2, 0xA2); viafb_set_iga_path(); @@ -2247,10 +2126,8 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, viafb_fill_crtc_timing(crt_timing, vmode_tbl, video_bpp / 8, IGA1); /* Write Graphic Controller */ - for (i = 0; i < StdGR; i++) { - outb(i, VIAGR); - outb(VPIT.GR[i], VIAGR + 1); - } + for (i = 0; i < StdGR; i++) + via_write_reg(VIAGR, i, VPIT.GR[i]); /* Write Attribute Controller */ for (i = 0; i < StdAR; i++) { @@ -2277,11 +2154,11 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, } } - viafb_set_primary_pitch(viafbinfo->fix.line_length); - viafb_set_secondary_pitch(viafb_dual_fb ? viafbinfo1->fix.line_length + via_set_primary_pitch(viafbinfo->fix.line_length); + via_set_secondary_pitch(viafb_dual_fb ? viafbinfo1->fix.line_length : viafbinfo->fix.line_length); - viafb_set_primary_color_depth(viaparinfo->depth); - viafb_set_secondary_color_depth(viafb_dual_fb ? viaparinfo1->depth + via_set_primary_color_depth(viaparinfo->depth); + via_set_secondary_color_depth(viafb_dual_fb ? viaparinfo1->depth : viaparinfo->depth); /* Update Refresh Rate Setting */ @@ -2473,108 +2350,6 @@ static void disable_second_display_channel(void) viafb_write_reg_mask(CR6A, VIACR, BIT6, BIT6); } -int viafb_get_fb_size_from_pci(void) -{ - unsigned long configid, deviceid, FBSize = 0; - int VideoMemSize; - int DeviceFound = false; - - for (configid = 0x80000000; configid < 0x80010800; configid += 0x100) { - outl(configid, (unsigned long)0xCF8); - deviceid = (inl((unsigned long)0xCFC) >> 16) & 0xffff; - - switch (deviceid) { - case CLE266: - case KM400: - outl(configid + 0xE0, (unsigned long)0xCF8); - FBSize = inl((unsigned long)0xCFC); - DeviceFound = true; /* Found device id */ - break; - - case CN400_FUNCTION3: - case CN700_FUNCTION3: - case CX700_FUNCTION3: - case KM800_FUNCTION3: - case KM890_FUNCTION3: - case P4M890_FUNCTION3: - case P4M900_FUNCTION3: - case VX800_FUNCTION3: - case VX855_FUNCTION3: - /*case CN750_FUNCTION3: */ - outl(configid + 0xA0, (unsigned long)0xCF8); - FBSize = inl((unsigned long)0xCFC); - DeviceFound = true; /* Found device id */ - break; - - default: - break; - } - - if (DeviceFound) - break; - } - - DEBUG_MSG(KERN_INFO "Device ID = %lx\n", deviceid); - - FBSize = FBSize & 0x00007000; - DEBUG_MSG(KERN_INFO "FB Size = %x\n", FBSize); - - if (viaparinfo->chip_info->gfx_chip_name < UNICHROME_CX700) { - switch (FBSize) { - case 0x00004000: - VideoMemSize = (16 << 20); /*16M */ - break; - - case 0x00005000: - VideoMemSize = (32 << 20); /*32M */ - break; - - case 0x00006000: - VideoMemSize = (64 << 20); /*64M */ - break; - - default: - VideoMemSize = (32 << 20); /*32M */ - break; - } - } else { - switch (FBSize) { - case 0x00001000: - VideoMemSize = (8 << 20); /*8M */ - break; - - case 0x00002000: - VideoMemSize = (16 << 20); /*16M */ - break; - - case 0x00003000: - VideoMemSize = (32 << 20); /*32M */ - break; - - case 0x00004000: - VideoMemSize = (64 << 20); /*64M */ - break; - - case 0x00005000: - VideoMemSize = (128 << 20); /*128M */ - break; - - case 0x00006000: - VideoMemSize = (256 << 20); /*256M */ - break; - - case 0x00007000: /* Only on VX855/875 */ - VideoMemSize = (512 << 20); /*512M */ - break; - - default: - VideoMemSize = (32 << 20); /*32M */ - break; - } - } - - return VideoMemSize; -} void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\ *p_gfx_dpa_setting) diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h index 12ef32d..a109de3 100644 --- a/drivers/video/via/hw.h +++ b/drivers/video/via/hw.h @@ -24,6 +24,11 @@ #include "viamode.h" #include "global.h" +#include "via_modesetting.h" + +#define viafb_read_reg(p, i) via_read_reg(p, i) +#define viafb_write_reg(i, p, d) via_write_reg(p, i, d) +#define viafb_write_reg_mask(i, p, d, m) via_write_reg_mask(p, i, d, m) /*************************************************** * Definition IGA1 Design Method of CRTC Registers * @@ -823,8 +828,8 @@ struct iga2_crtc_timing { }; /* device ID */ -#define CLE266 0x3123 -#define KM400 0x3205 +#define CLE266_FUNCTION3 0x3123 +#define KM400_FUNCTION3 0x3205 #define CN400_FUNCTION2 0x2259 #define CN400_FUNCTION3 0x3259 /* support VT3314 chipset */ @@ -870,7 +875,6 @@ extern int viafb_LCD_ON; extern int viafb_DVI_ON; extern int viafb_hotplug; -void viafb_write_reg_mask(u8 index, int io_port, u8 data, u8 mask); void viafb_set_output_path(int device, int set_iga, int output_interface); @@ -885,8 +889,6 @@ void viafb_crt_disable(void); void viafb_crt_enable(void); void init_ad9389(void); /* Access I/O Function */ -void viafb_write_reg(u8 index, u16 io_port, u8 data); -u8 viafb_read_reg(int io_port, u8 index); void viafb_lock_crt(void); void viafb_unlock_crt(void); void viafb_load_fetch_count_reg(int h_addr, int bpp_byte, int set_iga); @@ -900,20 +902,14 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, struct VideoModeTable *vmode_tbl1, int video_bpp1); void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, int refresh, struct VideoModeTable *vmode_tbl); -void viafb_init_chip_info(struct pci_dev *pdev, - const struct pci_device_id *pdi); +void viafb_init_chip_info(int chip_type); void viafb_init_dac(int set_iga); int viafb_get_pixclock(int hres, int vres, int vmode_refresh); int viafb_get_refresh(int hres, int vres, u32 float_refresh); void viafb_update_device_setting(int hres, int vres, int bpp, int vmode_refresh, int flag); -int viafb_get_fb_size_from_pci(void); void viafb_set_iga_path(void); -void viafb_set_primary_address(u32 addr); -void viafb_set_secondary_address(u32 addr); -void viafb_set_primary_pitch(u32 pitch); -void viafb_set_secondary_pitch(u32 pitch); void viafb_set_primary_color_register(u8 index, u8 red, u8 green, u8 blue); void viafb_set_secondary_color_register(u8 index, u8 red, u8 green, u8 blue); void viafb_get_fb_info(unsigned int *fb_base, unsigned int *fb_len); diff --git a/drivers/video/via/ioctl.h b/drivers/video/via/ioctl.h index de89980..c430fa2 100644 --- a/drivers/video/via/ioctl.h +++ b/drivers/video/via/ioctl.h @@ -75,7 +75,7 @@ /*SAMM operation flag*/ #define OP_SAMM 0x80 -#define LCD_PANEL_ID_MAXIMUM 22 +#define LCD_PANEL_ID_MAXIMUM 23 #define STATE_ON 0x1 #define STATE_OFF 0x0 diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c index 1b1ccdc..2ab0f15 100644 --- a/drivers/video/via/lcd.c +++ b/drivers/video/via/lcd.c @@ -18,7 +18,8 @@ * Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - +#include <linux/via-core.h> +#include <linux/via_i2c.h> #include "global.h" #include "lcdtbl.h" @@ -172,18 +173,16 @@ static bool lvds_identify_integratedlvds(void) int viafb_lvds_trasmitter_identify(void) { - viaparinfo->shared->i2c_stuff.i2c_port = I2CPORTINDEX; - if (viafb_lvds_identify_vt1636()) { - viaparinfo->chip_info->lvds_chip_info.i2c_port = I2CPORTINDEX; + if (viafb_lvds_identify_vt1636(VIA_PORT_31)) { + viaparinfo->chip_info->lvds_chip_info.i2c_port = VIA_PORT_31; DEBUG_MSG(KERN_INFO - "Found VIA VT1636 LVDS on port i2c 0x31 \n"); + "Found VIA VT1636 LVDS on port i2c 0x31\n"); } else { - viaparinfo->shared->i2c_stuff.i2c_port = GPIOPORTINDEX; - if (viafb_lvds_identify_vt1636()) { + if (viafb_lvds_identify_vt1636(VIA_PORT_2C)) { viaparinfo->chip_info->lvds_chip_info.i2c_port = - GPIOPORTINDEX; + VIA_PORT_2C; DEBUG_MSG(KERN_INFO - "Found VIA VT1636 LVDS on port gpio 0x2c \n"); + "Found VIA VT1636 LVDS on port gpio 0x2c\n"); } } @@ -398,6 +397,15 @@ static void fp_id_to_vindex(int panel_id) viaparinfo->lvds_setting_info->device_lcd_dualedge = 0; viaparinfo->lvds_setting_info->LCDDithering = 1; break; + case 0x17: + /* OLPC XO-1.5 panel */ + viaparinfo->lvds_setting_info->lcd_panel_hres = 1200; + viaparinfo->lvds_setting_info->lcd_panel_vres = 900; + viaparinfo->lvds_setting_info->lcd_panel_id = + LCD_PANEL_IDD_1200X900; + viaparinfo->lvds_setting_info->device_lcd_dualedge = 0; + viaparinfo->lvds_setting_info->LCDDithering = 0; + break; default: viaparinfo->lvds_setting_info->lcd_panel_hres = 800; viaparinfo->lvds_setting_info->lcd_panel_vres = 600; @@ -412,9 +420,8 @@ static int lvds_register_read(int index) { u8 data; - viaparinfo->shared->i2c_stuff.i2c_port = GPIOPORTINDEX; - viafb_i2c_readbyte((u8) viaparinfo->chip_info-> - lvds_chip_info.lvds_chip_slave_addr, + viafb_i2c_readbyte(VIA_PORT_2C, + (u8) viaparinfo->chip_info->lvds_chip_info.lvds_chip_slave_addr, (u8) index, &data); return data; } diff --git a/drivers/video/via/lcd.h b/drivers/video/via/lcd.h index 071f47c..9762ec6 100644 --- a/drivers/video/via/lcd.h +++ b/drivers/video/via/lcd.h @@ -60,6 +60,8 @@ #define LCD_PANEL_IDB_1360X768 0x0B /* Resolution: 480x640, Channel: single, Dithering: Enable */ #define LCD_PANEL_IDC_480X640 0x0C +/* Resolution: 1200x900, Channel: single, Dithering: Disable */ +#define LCD_PANEL_IDD_1200X900 0x0D extern int viafb_LCD2_ON; diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h index d55aaa7..7f0de7f 100644 --- a/drivers/video/via/share.h +++ b/drivers/video/via/share.h @@ -43,16 +43,9 @@ /* Video Memory Size */ #define VIDEO_MEMORY_SIZE_16M 0x1000000 -/* standard VGA IO port -*/ -#define VIARMisc 0x3CC -#define VIAWMisc 0x3C2 -#define VIAStatus 0x3DA -#define VIACR 0x3D4 -#define VIASR 0x3C4 -#define VIAGR 0x3CE -#define VIAAR 0x3C0 - +/* + * Lengths of the VPIT structure arrays. + */ #define StdCR 0x19 #define StdSR 0x04 #define StdGR 0x09 @@ -570,6 +563,10 @@ #define M1200X720_R60_HSP NEGATIVE #define M1200X720_R60_VSP POSITIVE +/* 1200x900@60 Sync Polarity (DCON) */ +#define M1200X900_R60_HSP NEGATIVE +#define M1200X900_R60_VSP NEGATIVE + /* 1280x600@60 Sync Polarity (GTF Mode) */ #define M1280x600_R60_HSP NEGATIVE #define M1280x600_R60_VSP POSITIVE @@ -651,6 +648,7 @@ #define CLK_52_406M 52406000 #define CLK_52_977M 52977000 #define CLK_56_250M 56250000 +#define CLK_57_275M 57275000 #define CLK_60_466M 60466000 #define CLK_61_500M 61500000 #define CLK_65_000M 65000000 @@ -939,6 +937,7 @@ #define VX855_52_406M 0x00580C03 #define VX855_52_977M 0x00940C05 #define VX855_56_250M 0x009D0C05 +#define VX855_57_275M 0x009D8C85 /* Used by XO panel */ #define VX855_60_466M 0x00A90C05 #define VX855_61_500M 0x00AC0C05 #define VX855_65_000M 0x006D0C03 @@ -1065,6 +1064,7 @@ #define RES_1600X1200_60HZ_PIXCLOCK 6172 #define RES_1600X1200_75HZ_PIXCLOCK 4938 #define RES_1280X720_60HZ_PIXCLOCK 13426 +#define RES_1200X900_60HZ_PIXCLOCK 17459 #define RES_1920X1080_60HZ_PIXCLOCK 5787 #define RES_1400X1050_60HZ_PIXCLOCK 8214 #define RES_1400X1050_75HZ_PIXCLOCK 6410 diff --git a/drivers/video/via/via-core.c b/drivers/video/via/via-core.c new file mode 100644 index 0000000..e8cfe83 --- /dev/null +++ b/drivers/video/via/via-core.c @@ -0,0 +1,668 @@ +/* + * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. + * Copyright 2009 Jonathan Corbet <corbet@lwn.net> + */ + +/* + * Core code for the Via multifunction framebuffer device. + */ +#include <linux/via-core.h> +#include <linux/via_i2c.h> +#include <linux/via-gpio.h> +#include "global.h" + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> + +/* + * The default port config. + */ +static struct via_port_cfg adap_configs[] = { + [VIA_PORT_26] = { VIA_PORT_I2C, VIA_MODE_OFF, VIASR, 0x26 }, + [VIA_PORT_31] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x31 }, + [VIA_PORT_25] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 }, + [VIA_PORT_2C] = { VIA_PORT_GPIO, VIA_MODE_I2C, VIASR, 0x2c }, + [VIA_PORT_3D] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x3d }, + { 0, 0, 0, 0 } +}; + +/* + * We currently only support one viafb device (will there ever be + * more than one?), so just declare it globally here. + */ +static struct viafb_dev global_dev; + + +/* + * Basic register access; spinlock required. + */ +static inline void viafb_mmio_write(int reg, u32 v) +{ + iowrite32(v, global_dev.engine_mmio + reg); +} + +static inline int viafb_mmio_read(int reg) +{ + return ioread32(global_dev.engine_mmio + reg); +} + +/* ---------------------------------------------------------------------- */ +/* + * Interrupt management. We have a single IRQ line for a lot of + * different functions, so we need to share it. The design here + * is that we don't want to reimplement the shared IRQ code here; + * we also want to avoid having contention for a single handler thread. + * So each subdev driver which needs interrupts just requests + * them directly from the kernel. We just have what's needed for + * overall access to the interrupt control register. + */ + +/* + * Which interrupts are enabled now? + */ +static u32 viafb_enabled_ints; + +static void viafb_int_init(void) +{ + viafb_enabled_ints = 0; + + viafb_mmio_write(VDE_INTERRUPT, 0); +} + +/* + * Allow subdevs to ask for specific interrupts to be enabled. These + * functions must be called with reg_lock held + */ +void viafb_irq_enable(u32 mask) +{ + viafb_enabled_ints |= mask; + viafb_mmio_write(VDE_INTERRUPT, viafb_enabled_ints | VDE_I_ENABLE); +} +EXPORT_SYMBOL_GPL(viafb_irq_enable); + +void viafb_irq_disable(u32 mask) +{ + viafb_enabled_ints &= ~mask; + if (viafb_enabled_ints == 0) + viafb_mmio_write(VDE_INTERRUPT, 0); /* Disable entirely */ + else + viafb_mmio_write(VDE_INTERRUPT, + viafb_enabled_ints | VDE_I_ENABLE); +} +EXPORT_SYMBOL_GPL(viafb_irq_disable); + +/* ---------------------------------------------------------------------- */ +/* + * Access to the DMA engine. This currently provides what the camera + * driver needs (i.e. outgoing only) but is easily expandable if need + * be. + */ + +/* + * There are four DMA channels in the vx855. For now, we only + * use one of them, though. Most of the time, the DMA channel + * will be idle, so we keep the IRQ handler unregistered except + * when some subsystem has indicated an interest. + */ +static int viafb_dma_users; +static DECLARE_COMPLETION(viafb_dma_completion); +/* + * This mutex protects viafb_dma_users and our global interrupt + * registration state; it also serializes access to the DMA + * engine. + */ +static DEFINE_MUTEX(viafb_dma_lock); + +/* + * The VX855 DMA descriptor (used for s/g transfers) looks + * like this. + */ +struct viafb_vx855_dma_descr { + u32 addr_low; /* Low part of phys addr */ + u32 addr_high; /* High 12 bits of addr */ + u32 fb_offset; /* Offset into FB memory */ + u32 seg_size; /* Size, 16-byte units */ + u32 tile_mode; /* "tile mode" setting */ + u32 next_desc_low; /* Next descriptor addr */ + u32 next_desc_high; + u32 pad; /* Fill out to 64 bytes */ +}; + +/* + * Flags added to the "next descriptor low" pointers + */ +#define VIAFB_DMA_MAGIC 0x01 /* ??? Just has to be there */ +#define VIAFB_DMA_FINAL_SEGMENT 0x02 /* Final segment */ + +/* + * The completion IRQ handler. + */ +static irqreturn_t viafb_dma_irq(int irq, void *data) +{ + int csr; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&global_dev.reg_lock); + csr = viafb_mmio_read(VDMA_CSR0); + if (csr & VDMA_C_DONE) { + viafb_mmio_write(VDMA_CSR0, VDMA_C_DONE); + complete(&viafb_dma_completion); + ret = IRQ_HANDLED; + } + spin_unlock(&global_dev.reg_lock); + return ret; +} + +/* + * Indicate a need for DMA functionality. + */ +int viafb_request_dma(void) +{ + int ret = 0; + + /* + * Only VX855 is supported currently. + */ + if (global_dev.chip_type != UNICHROME_VX855) + return -ENODEV; + /* + * Note the new user and set up our interrupt handler + * if need be. + */ + mutex_lock(&viafb_dma_lock); + viafb_dma_users++; + if (viafb_dma_users == 1) { + ret = request_irq(global_dev.pdev->irq, viafb_dma_irq, + IRQF_SHARED, "via-dma", &viafb_dma_users); + if (ret) + viafb_dma_users--; + else + viafb_irq_enable(VDE_I_DMA0TDEN); + } + mutex_unlock(&viafb_dma_lock); + return ret; +} +EXPORT_SYMBOL_GPL(viafb_request_dma); + +void viafb_release_dma(void) +{ + mutex_lock(&viafb_dma_lock); + viafb_dma_users--; + if (viafb_dma_users == 0) { + viafb_irq_disable(VDE_I_DMA0TDEN); + free_irq(global_dev.pdev->irq, &viafb_dma_users); + } + mutex_unlock(&viafb_dma_lock); +} +EXPORT_SYMBOL_GPL(viafb_release_dma); + + +#if 0 +/* + * Copy a single buffer from FB memory, synchronously. This code works + * but is not currently used. + */ +void viafb_dma_copy_out(unsigned int offset, dma_addr_t paddr, int len) +{ + unsigned long flags; + int csr; + + mutex_lock(&viafb_dma_lock); + init_completion(&viafb_dma_completion); + /* + * Program the controller. + */ + spin_lock_irqsave(&global_dev.reg_lock, flags); + viafb_mmio_write(VDMA_CSR0, VDMA_C_ENABLE|VDMA_C_DONE); + /* Enable ints; must happen after CSR0 write! */ + viafb_mmio_write(VDMA_MR0, VDMA_MR_TDIE); + viafb_mmio_write(VDMA_MARL0, (int) (paddr & 0xfffffff0)); + viafb_mmio_write(VDMA_MARH0, (int) ((paddr >> 28) & 0xfff)); + /* Data sheet suggests DAR0 should be <<4, but it lies */ + viafb_mmio_write(VDMA_DAR0, offset); + viafb_mmio_write(VDMA_DQWCR0, len >> 4); + viafb_mmio_write(VDMA_TMR0, 0); + viafb_mmio_write(VDMA_DPRL0, 0); + viafb_mmio_write(VDMA_DPRH0, 0); + viafb_mmio_write(VDMA_PMR0, 0); + csr = viafb_mmio_read(VDMA_CSR0); + viafb_mmio_write(VDMA_CSR0, VDMA_C_ENABLE|VDMA_C_START); + spin_unlock_irqrestore(&global_dev.reg_lock, flags); + /* + * Now we just wait until the interrupt handler says + * we're done. + */ + wait_for_completion_interruptible(&viafb_dma_completion); + viafb_mmio_write(VDMA_MR0, 0); /* Reset int enable */ + mutex_unlock(&viafb_dma_lock); +} +EXPORT_SYMBOL_GPL(viafb_dma_copy_out); +#endif + +/* + * Do a scatter/gather DMA copy from FB memory. You must have done + * a successful call to viafb_request_dma() first. + */ +int viafb_dma_copy_out_sg(unsigned int offset, struct scatterlist *sg, int nsg) +{ + struct viafb_vx855_dma_descr *descr; + void *descrpages; + dma_addr_t descr_handle; + unsigned long flags; + int i; + struct scatterlist *sgentry; + dma_addr_t nextdesc; + + /* + * Get a place to put the descriptors. + */ + descrpages = dma_alloc_coherent(&global_dev.pdev->dev, + nsg*sizeof(struct viafb_vx855_dma_descr), + &descr_handle, GFP_KERNEL); + if (descrpages == NULL) { + dev_err(&global_dev.pdev->dev, "Unable to get descr page.\n"); + return -ENOMEM; + } + mutex_lock(&viafb_dma_lock); + /* + * Fill them in. + */ + descr = descrpages; + nextdesc = descr_handle + sizeof(struct viafb_vx855_dma_descr); + for_each_sg(sg, sgentry, nsg, i) { + dma_addr_t paddr = sg_dma_address(sgentry); + descr->addr_low = paddr & 0xfffffff0; + descr->addr_high = ((u64) paddr >> 32) & 0x0fff; + descr->fb_offset = offset; + descr->seg_size = sg_dma_len(sgentry) >> 4; + descr->tile_mode = 0; + descr->next_desc_low = (nextdesc&0xfffffff0) | VIAFB_DMA_MAGIC; + descr->next_desc_high = ((u64) nextdesc >> 32) & 0x0fff; + descr->pad = 0xffffffff; /* VIA driver does this */ + offset += sg_dma_len(sgentry); + nextdesc += sizeof(struct viafb_vx855_dma_descr); + descr++; + } + descr[-1].next_desc_low = VIAFB_DMA_FINAL_SEGMENT|VIAFB_DMA_MAGIC; + /* + * Program the engine. + */ + spin_lock_irqsave(&global_dev.reg_lock, flags); + init_completion(&viafb_dma_completion); + viafb_mmio_write(VDMA_DQWCR0, 0); + viafb_mmio_write(VDMA_CSR0, VDMA_C_ENABLE|VDMA_C_DONE); + viafb_mmio_write(VDMA_MR0, VDMA_MR_TDIE | VDMA_MR_CHAIN); + viafb_mmio_write(VDMA_DPRL0, descr_handle | VIAFB_DMA_MAGIC); + viafb_mmio_write(VDMA_DPRH0, + (((u64)descr_handle >> 32) & 0x0fff) | 0xf0000); + (void) viafb_mmio_read(VDMA_CSR0); + viafb_mmio_write(VDMA_CSR0, VDMA_C_ENABLE|VDMA_C_START); + spin_unlock_irqrestore(&global_dev.reg_lock, flags); + /* + * Now we just wait until the interrupt handler says + * we're done. Except that, actually, we need to wait a little + * longer: the interrupts seem to jump the gun a little and we + * get corrupted frames sometimes. + */ + wait_for_completion_timeout(&viafb_dma_completion, 1); + msleep(1); + if ((viafb_mmio_read(VDMA_CSR0)&VDMA_C_DONE) == 0) + printk(KERN_ERR "VIA DMA timeout!\n"); + /* + * Clean up and we're done. + */ + viafb_mmio_write(VDMA_CSR0, VDMA_C_DONE); + viafb_mmio_write(VDMA_MR0, 0); /* Reset int enable */ + mutex_unlock(&viafb_dma_lock); + dma_free_coherent(&global_dev.pdev->dev, + nsg*sizeof(struct viafb_vx855_dma_descr), descrpages, + descr_handle); + return 0; +} +EXPORT_SYMBOL_GPL(viafb_dma_copy_out_sg); + + +/* ---------------------------------------------------------------------- */ +/* + * Figure out how big our framebuffer memory is. Kind of ugly, + * but evidently we can't trust the information found in the + * fbdev configuration area. + */ +static u16 via_function3[] = { + CLE266_FUNCTION3, KM400_FUNCTION3, CN400_FUNCTION3, CN700_FUNCTION3, + CX700_FUNCTION3, KM800_FUNCTION3, KM890_FUNCTION3, P4M890_FUNCTION3, + P4M900_FUNCTION3, VX800_FUNCTION3, VX855_FUNCTION3, +}; + +/* Get the BIOS-configured framebuffer size from PCI configuration space + * of function 3 in the respective chipset */ +static int viafb_get_fb_size_from_pci(int chip_type) +{ + int i; + u8 offset = 0; + u32 FBSize; + u32 VideoMemSize; + + /* search for the "FUNCTION3" device in this chipset */ + for (i = 0; i < ARRAY_SIZE(via_function3); i++) { + struct pci_dev *pdev; + + pdev = pci_get_device(PCI_VENDOR_ID_VIA, via_function3[i], + NULL); + if (!pdev) + continue; + + DEBUG_MSG(KERN_INFO "Device ID = %x\n", pdev->device); + + switch (pdev->device) { + case CLE266_FUNCTION3: + case KM400_FUNCTION3: + offset = 0xE0; + break; + case CN400_FUNCTION3: + case CN700_FUNCTION3: + case CX700_FUNCTION3: + case KM800_FUNCTION3: + case KM890_FUNCTION3: + case P4M890_FUNCTION3: + case P4M900_FUNCTION3: + case VX800_FUNCTION3: + case VX855_FUNCTION3: + /*case CN750_FUNCTION3: */ + offset = 0xA0; + break; + } + + if (!offset) + break; + + pci_read_config_dword(pdev, offset, &FBSize); + pci_dev_put(pdev); + } + + if (!offset) { + printk(KERN_ERR "cannot determine framebuffer size\n"); + return -EIO; + } + + FBSize = FBSize & 0x00007000; + DEBUG_MSG(KERN_INFO "FB Size = %x\n", FBSize); + + if (chip_type < UNICHROME_CX700) { + switch (FBSize) { + case 0x00004000: + VideoMemSize = (16 << 20); /*16M */ + break; + + case 0x00005000: + VideoMemSize = (32 << 20); /*32M */ + break; + + case 0x00006000: + VideoMemSize = (64 << 20); /*64M */ + break; + + default: + VideoMemSize = (32 << 20); /*32M */ + break; + } + } else { + switch (FBSize) { + case 0x00001000: + VideoMemSize = (8 << 20); /*8M */ + break; + + case 0x00002000: + VideoMemSize = (16 << 20); /*16M */ + break; + + case 0x00003000: + VideoMemSize = (32 << 20); /*32M */ + break; + + case 0x00004000: + VideoMemSize = (64 << 20); /*64M */ + break; + + case 0x00005000: + VideoMemSize = (128 << 20); /*128M */ + break; + + case 0x00006000: + VideoMemSize = (256 << 20); /*256M */ + break; + + case 0x00007000: /* Only on VX855/875 */ + VideoMemSize = (512 << 20); /*512M */ + break; + + default: + VideoMemSize = (32 << 20); /*32M */ + break; + } + } + + return VideoMemSize; +} + + +/* + * Figure out and map our MMIO regions. + */ +static int __devinit via_pci_setup_mmio(struct viafb_dev *vdev) +{ + int ret; + /* + * Hook up to the device registers. Note that we soldier + * on if it fails; the framebuffer can operate (without + * acceleration) without this region. + */ + vdev->engine_start = pci_resource_start(vdev->pdev, 1); + vdev->engine_len = pci_resource_len(vdev->pdev, 1); + vdev->engine_mmio = ioremap_nocache(vdev->engine_start, + vdev->engine_len); + if (vdev->engine_mmio == NULL) + dev_err(&vdev->pdev->dev, + "Unable to map engine MMIO; operation will be " + "slow and crippled.\n"); + /* + * Map in framebuffer memory. For now, failure here is + * fatal. Unfortunately, in the absence of significant + * vmalloc space, failure here is also entirely plausible. + * Eventually we want to move away from mapping this + * entire region. + */ + vdev->fbmem_start = pci_resource_start(vdev->pdev, 0); + ret = vdev->fbmem_len = viafb_get_fb_size_from_pci(vdev->chip_type); + if (ret < 0) + goto out_unmap; + vdev->fbmem = ioremap_nocache(vdev->fbmem_start, vdev->fbmem_len); + if (vdev->fbmem == NULL) { + ret = -ENOMEM; + goto out_unmap; + } + return 0; +out_unmap: + iounmap(vdev->engine_mmio); + return ret; +} + +static void __devexit via_pci_teardown_mmio(struct viafb_dev *vdev) +{ + iounmap(vdev->fbmem); + iounmap(vdev->engine_mmio); +} + +/* + * Create our subsidiary devices. + */ +static struct viafb_subdev_info { + char *name; + struct platform_device *platdev; +} viafb_subdevs[] = { + { + .name = "viafb-gpio", + }, + { + .name = "viafb-i2c", + } +}; +#define N_SUBDEVS ARRAY_SIZE(viafb_subdevs) + +static int __devinit via_create_subdev(struct viafb_dev *vdev, + struct viafb_subdev_info *info) +{ + int ret; + + info->platdev = platform_device_alloc(info->name, -1); + if (!info->platdev) { + dev_err(&vdev->pdev->dev, "Unable to allocate pdev %s\n", + info->name); + return -ENOMEM; + } + info->platdev->dev.parent = &vdev->pdev->dev; + info->platdev->dev.platform_data = vdev; + ret = platform_device_add(info->platdev); + if (ret) { + dev_err(&vdev->pdev->dev, "Unable to add pdev %s\n", + info->name); + platform_device_put(info->platdev); + info->platdev = NULL; + } + return ret; +} + +static int __devinit via_setup_subdevs(struct viafb_dev *vdev) +{ + int i; + + /* + * Ignore return values. Even if some of the devices + * fail to be created, we'll still be able to use some + * of the rest. + */ + for (i = 0; i < N_SUBDEVS; i++) + via_create_subdev(vdev, viafb_subdevs + i); + return 0; +} + +static void __devexit via_teardown_subdevs(void) +{ + int i; + + for (i = 0; i < N_SUBDEVS; i++) + if (viafb_subdevs[i].platdev) { + viafb_subdevs[i].platdev->dev.platform_data = NULL; + platform_device_unregister(viafb_subdevs[i].platdev); + } +} + + +static int __devinit via_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret; + + ret = pci_enable_device(pdev); + if (ret) + return ret; + /* + * Global device initialization. + */ + memset(&global_dev, 0, sizeof(global_dev)); + global_dev.pdev = pdev; + global_dev.chip_type = ent->driver_data; + global_dev.port_cfg = adap_configs; + spin_lock_init(&global_dev.reg_lock); + ret = via_pci_setup_mmio(&global_dev); + if (ret) + goto out_disable; + /* + * Set up interrupts and create our subdevices. Continue even if + * some things fail. + */ + viafb_int_init(); + via_setup_subdevs(&global_dev); + /* + * Set up the framebuffer device + */ + ret = via_fb_pci_probe(&global_dev); + if (ret) + goto out_subdevs; + return 0; + +out_subdevs: + via_teardown_subdevs(); + via_pci_teardown_mmio(&global_dev); +out_disable: + pci_disable_device(pdev); + return ret; +} + +static void __devexit via_pci_remove(struct pci_dev *pdev) +{ + via_teardown_subdevs(); + via_fb_pci_remove(pdev); + via_pci_teardown_mmio(&global_dev); + pci_disable_device(pdev); +} + + +static struct pci_device_id via_pci_table[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CLE266_DID), + .driver_data = UNICHROME_CLE266 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_PM800_DID), + .driver_data = UNICHROME_PM800 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K400_DID), + .driver_data = UNICHROME_K400 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K800_DID), + .driver_data = UNICHROME_K800 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M890_DID), + .driver_data = UNICHROME_CN700 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K8M890_DID), + .driver_data = UNICHROME_K8M890 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CX700_DID), + .driver_data = UNICHROME_CX700 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M900_DID), + .driver_data = UNICHROME_P4M900 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CN750_DID), + .driver_data = UNICHROME_CN750 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX800_DID), + .driver_data = UNICHROME_VX800 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX855_DID), + .driver_data = UNICHROME_VX855 }, + { } +}; +MODULE_DEVICE_TABLE(pci, via_pci_table); + +static struct pci_driver via_driver = { + .name = "viafb", + .id_table = via_pci_table, + .probe = via_pci_probe, + .remove = __devexit_p(via_pci_remove), +}; + +static int __init via_core_init(void) +{ + int ret; + + ret = viafb_init(); + if (ret) + return ret; + viafb_i2c_init(); + viafb_gpio_init(); + return pci_register_driver(&via_driver); +} + +static void __exit via_core_exit(void) +{ + pci_unregister_driver(&via_driver); + viafb_gpio_exit(); + viafb_i2c_exit(); + viafb_exit(); +} + +module_init(via_core_init); +module_exit(via_core_exit); diff --git a/drivers/video/via/via-gpio.c b/drivers/video/via/via-gpio.c new file mode 100644 index 0000000..595516a --- /dev/null +++ b/drivers/video/via/via-gpio.c @@ -0,0 +1,285 @@ +/* + * Support for viafb GPIO ports. + * + * Copyright 2009 Jonathan Corbet <corbet@lwn.net> + * Distributable under version 2 of the GNU General Public License. + */ + +#include <linux/spinlock.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/via-core.h> +#include <linux/via-gpio.h> + +/* + * The ports we know about. Note that the port-25 gpios are not + * mentioned in the datasheet. + */ + +struct viafb_gpio { + char *vg_name; /* Data sheet name */ + u16 vg_io_port; + u8 vg_port_index; + int vg_mask_shift; +}; + +static struct viafb_gpio viafb_all_gpios[] = { + { + .vg_name = "VGPIO0", /* Guess - not in datasheet */ + .vg_io_port = VIASR, + .vg_port_index = 0x25, + .vg_mask_shift = 1 + }, + { + .vg_name = "VGPIO1", + .vg_io_port = VIASR, + .vg_port_index = 0x25, + .vg_mask_shift = 0 + }, + { + .vg_name = "VGPIO2", /* aka DISPCLKI0 */ + .vg_io_port = VIASR, + .vg_port_index = 0x2c, + .vg_mask_shift = 1 + }, + { + .vg_name = "VGPIO3", /* aka DISPCLKO0 */ + .vg_io_port = VIASR, + .vg_port_index = 0x2c, + .vg_mask_shift = 0 + }, + { + .vg_name = "VGPIO4", /* DISPCLKI1 */ + .vg_io_port = VIASR, + .vg_port_index = 0x3d, + .vg_mask_shift = 1 + }, + { + .vg_name = "VGPIO5", /* DISPCLKO1 */ + .vg_io_port = VIASR, + .vg_port_index = 0x3d, + .vg_mask_shift = 0 + }, +}; + +#define VIAFB_NUM_GPIOS ARRAY_SIZE(viafb_all_gpios) + +/* + * This structure controls the active GPIOs, which may be a subset + * of those which are known. + */ + +struct viafb_gpio_cfg { + struct gpio_chip gpio_chip; + struct viafb_dev *vdev; + struct viafb_gpio *active_gpios[VIAFB_NUM_GPIOS]; + char *gpio_names[VIAFB_NUM_GPIOS]; +}; + +/* + * GPIO access functions + */ +static void via_gpio_set(struct gpio_chip *chip, unsigned int nr, + int value) +{ + struct viafb_gpio_cfg *cfg = container_of(chip, + struct viafb_gpio_cfg, + gpio_chip); + u8 reg; + struct viafb_gpio *gpio; + unsigned long flags; + + spin_lock_irqsave(&cfg->vdev->reg_lock, flags); + gpio = cfg->active_gpios[nr]; + reg = via_read_reg(VIASR, gpio->vg_port_index); + reg |= 0x40 << gpio->vg_mask_shift; /* output enable */ + if (value) + reg |= 0x10 << gpio->vg_mask_shift; + else + reg &= ~(0x10 << gpio->vg_mask_shift); + via_write_reg(VIASR, gpio->vg_port_index, reg); + spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); +} + +static int via_gpio_dir_out(struct gpio_chip *chip, unsigned int nr, + int value) +{ + via_gpio_set(chip, nr, value); + return 0; +} + +/* + * Set the input direction. I'm not sure this is right; we should + * be able to do input without disabling output. + */ +static int via_gpio_dir_input(struct gpio_chip *chip, unsigned int nr) +{ + struct viafb_gpio_cfg *cfg = container_of(chip, + struct viafb_gpio_cfg, + gpio_chip); + struct viafb_gpio *gpio; + unsigned long flags; + + spin_lock_irqsave(&cfg->vdev->reg_lock, flags); + gpio = cfg->active_gpios[nr]; + via_write_reg_mask(VIASR, gpio->vg_port_index, 0, + 0x40 << gpio->vg_mask_shift); + spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); + return 0; +} + +static int via_gpio_get(struct gpio_chip *chip, unsigned int nr) +{ + struct viafb_gpio_cfg *cfg = container_of(chip, + struct viafb_gpio_cfg, + gpio_chip); + u8 reg; + struct viafb_gpio *gpio; + unsigned long flags; + + spin_lock_irqsave(&cfg->vdev->reg_lock, flags); + gpio = cfg->active_gpios[nr]; + reg = via_read_reg(VIASR, gpio->vg_port_index); + spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); + return reg & (0x04 << gpio->vg_mask_shift); +} + + +static struct viafb_gpio_cfg gpio_config = { + .gpio_chip = { + .label = "VIAFB onboard GPIO", + .owner = THIS_MODULE, + .direction_output = via_gpio_dir_out, + .set = via_gpio_set, + .direction_input = via_gpio_dir_input, + .get = via_gpio_get, + .base = -1, + .ngpio = 0, + .can_sleep = 0 + } +}; + +/* + * Manage the software enable bit. + */ +static void viafb_gpio_enable(struct viafb_gpio *gpio) +{ + via_write_reg_mask(VIASR, gpio->vg_port_index, 0x02, 0x02); +} + +static void viafb_gpio_disable(struct viafb_gpio *gpio) +{ + via_write_reg_mask(VIASR, gpio->vg_port_index, 0, 0x02); +} + +/* + * Look up a specific gpio and return the number it was assigned. + */ +int viafb_gpio_lookup(const char *name) +{ + int i; + + for (i = 0; i < gpio_config.gpio_chip.ngpio; i++) + if (!strcmp(name, gpio_config.active_gpios[i]->vg_name)) + return gpio_config.gpio_chip.base + i; + return -1; +} +EXPORT_SYMBOL_GPL(viafb_gpio_lookup); + +/* + * Platform device stuff. + */ +static __devinit int viafb_gpio_probe(struct platform_device *platdev) +{ + struct viafb_dev *vdev = platdev->dev.platform_data; + struct via_port_cfg *port_cfg = vdev->port_cfg; + int i, ngpio = 0, ret; + struct viafb_gpio *gpio; + unsigned long flags; + + /* + * Set up entries for all GPIOs which have been configured to + * operate as such (as opposed to as i2c ports). + */ + for (i = 0; i < VIAFB_NUM_PORTS; i++) { + if (port_cfg[i].mode != VIA_MODE_GPIO) + continue; + for (gpio = viafb_all_gpios; + gpio < viafb_all_gpios + VIAFB_NUM_GPIOS; gpio++) + if (gpio->vg_port_index == port_cfg[i].ioport_index) { + gpio_config.active_gpios[ngpio] = gpio; + gpio_config.gpio_names[ngpio] = gpio->vg_name; + ngpio++; + } + } + gpio_config.gpio_chip.ngpio = ngpio; + gpio_config.gpio_chip.names = gpio_config.gpio_names; + gpio_config.vdev = vdev; + if (ngpio == 0) { + printk(KERN_INFO "viafb: no GPIOs configured\n"); + return 0; + } + /* + * Enable the ports. They come in pairs, with a single + * enable bit for both. + */ + spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags); + for (i = 0; i < ngpio; i += 2) + viafb_gpio_enable(gpio_config.active_gpios[i]); + spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags); + /* + * Get registered. + */ + gpio_config.gpio_chip.base = -1; /* Dynamic */ + ret = gpiochip_add(&gpio_config.gpio_chip); + if (ret) { + printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret); + gpio_config.gpio_chip.ngpio = 0; + } + return ret; +} + + +static int viafb_gpio_remove(struct platform_device *platdev) +{ + unsigned long flags; + int ret = 0, i; + + /* + * Get unregistered. + */ + if (gpio_config.gpio_chip.ngpio > 0) { + ret = gpiochip_remove(&gpio_config.gpio_chip); + if (ret) { /* Somebody still using it? */ + printk(KERN_ERR "Viafb: GPIO remove failed\n"); + return ret; + } + } + /* + * Disable the ports. + */ + spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags); + for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2) + viafb_gpio_disable(gpio_config.active_gpios[i]); + gpio_config.gpio_chip.ngpio = 0; + spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags); + return ret; +} + +static struct platform_driver via_gpio_driver = { + .driver = { + .name = "viafb-gpio", + }, + .probe = viafb_gpio_probe, + .remove = viafb_gpio_remove, +}; + +int viafb_gpio_init(void) +{ + return platform_driver_register(&via_gpio_driver); +} + +void viafb_gpio_exit(void) +{ + platform_driver_unregister(&via_gpio_driver); +} diff --git a/drivers/video/via/via_i2c.c b/drivers/video/via/via_i2c.c index 15543e9..da9e4ca 100644 --- a/drivers/video/via/via_i2c.c +++ b/drivers/video/via/via_i2c.c @@ -1,5 +1,5 @@ /* - * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved. + * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. * This program is free software; you can redistribute it and/or @@ -19,77 +19,106 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include "global.h" +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/module.h> +#include <linux/via-core.h> +#include <linux/via_i2c.h> + +/* + * There can only be one set of these, so there's no point in having + * them be dynamically allocated... + */ +#define VIAFB_NUM_I2C 5 +static struct via_i2c_stuff via_i2c_par[VIAFB_NUM_I2C]; +struct viafb_dev *i2c_vdev; /* Passed in from core */ static void via_i2c_setscl(void *data, int state) { u8 val; - struct via_i2c_stuff *via_i2c_chan = (struct via_i2c_stuff *)data; + struct via_port_cfg *adap_data = data; + unsigned long flags; - val = viafb_read_reg(VIASR, via_i2c_chan->i2c_port) & 0xF0; + spin_lock_irqsave(&i2c_vdev->reg_lock, flags); + val = via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0xF0; if (state) val |= 0x20; else val &= ~0x20; - switch (via_i2c_chan->i2c_port) { - case I2CPORTINDEX: + switch (adap_data->type) { + case VIA_PORT_I2C: val |= 0x01; break; - case GPIOPORTINDEX: + case VIA_PORT_GPIO: val |= 0x80; break; default: - DEBUG_MSG("via_i2c: specify wrong i2c port.\n"); + printk(KERN_ERR "viafb_i2c: specify wrong i2c type.\n"); } - viafb_write_reg(via_i2c_chan->i2c_port, VIASR, val); + via_write_reg(adap_data->io_port, adap_data->ioport_index, val); + spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags); } static int via_i2c_getscl(void *data) { - struct via_i2c_stuff *via_i2c_chan = (struct via_i2c_stuff *)data; + struct via_port_cfg *adap_data = data; + unsigned long flags; + int ret = 0; - if (viafb_read_reg(VIASR, via_i2c_chan->i2c_port) & 0x08) - return 1; - return 0; + spin_lock_irqsave(&i2c_vdev->reg_lock, flags); + if (via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0x08) + ret = 1; + spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags); + return ret; } static int via_i2c_getsda(void *data) { - struct via_i2c_stuff *via_i2c_chan = (struct via_i2c_stuff *)data; + struct via_port_cfg *adap_data = data; + unsigned long flags; + int ret = 0; - if (viafb_read_reg(VIASR, via_i2c_chan->i2c_port) & 0x04) - return 1; - return 0; + spin_lock_irqsave(&i2c_vdev->reg_lock, flags); + if (via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0x04) + ret = 1; + spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags); + return ret; } static void via_i2c_setsda(void *data, int state) { u8 val; - struct via_i2c_stuff *via_i2c_chan = (struct via_i2c_stuff *)data; + struct via_port_cfg *adap_data = data; + unsigned long flags; - val = viafb_read_reg(VIASR, via_i2c_chan->i2c_port) & 0xF0; + spin_lock_irqsave(&i2c_vdev->reg_lock, flags); + val = via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0xF0; if (state) val |= 0x10; else val &= ~0x10; - switch (via_i2c_chan->i2c_port) { - case I2CPORTINDEX: + switch (adap_data->type) { + case VIA_PORT_I2C: val |= 0x01; break; - case GPIOPORTINDEX: + case VIA_PORT_GPIO: val |= 0x40; break; default: - DEBUG_MSG("via_i2c: specify wrong i2c port.\n"); + printk(KERN_ERR "viafb_i2c: specify wrong i2c type.\n"); } - viafb_write_reg(via_i2c_chan->i2c_port, VIASR, val); + via_write_reg(adap_data->io_port, adap_data->ioport_index, val); + spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags); } -int viafb_i2c_readbyte(u8 slave_addr, u8 index, u8 *pdata) +int viafb_i2c_readbyte(u8 adap, u8 slave_addr, u8 index, u8 *pdata) { u8 mm1[] = {0x00}; struct i2c_msg msgs[2]; + if (!via_i2c_par[adap].is_active) + return -ENODEV; *pdata = 0; msgs[0].flags = 0; msgs[1].flags = I2C_M_RD; @@ -97,81 +126,144 @@ int viafb_i2c_readbyte(u8 slave_addr, u8 index, u8 *pdata) mm1[0] = index; msgs[0].len = 1; msgs[1].len = 1; msgs[0].buf = mm1; msgs[1].buf = pdata; - i2c_transfer(&viaparinfo->shared->i2c_stuff.adapter, msgs, 2); - - return 0; + return i2c_transfer(&via_i2c_par[adap].adapter, msgs, 2); } -int viafb_i2c_writebyte(u8 slave_addr, u8 index, u8 data) +int viafb_i2c_writebyte(u8 adap, u8 slave_addr, u8 index, u8 data) { u8 msg[2] = { index, data }; struct i2c_msg msgs; + if (!via_i2c_par[adap].is_active) + return -ENODEV; msgs.flags = 0; msgs.addr = slave_addr / 2; msgs.len = 2; msgs.buf = msg; - return i2c_transfer(&viaparinfo->shared->i2c_stuff.adapter, &msgs, 1); + return i2c_transfer(&via_i2c_par[adap].adapter, &msgs, 1); } -int viafb_i2c_readbytes(u8 slave_addr, u8 index, u8 *buff, int buff_len) +int viafb_i2c_readbytes(u8 adap, u8 slave_addr, u8 index, u8 *buff, int buff_len) { u8 mm1[] = {0x00}; struct i2c_msg msgs[2]; + if (!via_i2c_par[adap].is_active) + return -ENODEV; msgs[0].flags = 0; msgs[1].flags = I2C_M_RD; msgs[0].addr = msgs[1].addr = slave_addr / 2; mm1[0] = index; msgs[0].len = 1; msgs[1].len = buff_len; msgs[0].buf = mm1; msgs[1].buf = buff; - i2c_transfer(&viaparinfo->shared->i2c_stuff.adapter, msgs, 2); - return 0; + return i2c_transfer(&via_i2c_par[adap].adapter, msgs, 2); } -int viafb_create_i2c_bus(void *viapar) +/* + * Allow other viafb subdevices to look up a specific adapter + * by port name. + */ +struct i2c_adapter *viafb_find_i2c_adapter(enum viafb_i2c_adap which) { - int ret; - struct via_i2c_stuff *i2c_stuff = - &((struct viafb_par *)viapar)->shared->i2c_stuff; - - strcpy(i2c_stuff->adapter.name, "via_i2c"); - i2c_stuff->i2c_port = 0x0; - i2c_stuff->adapter.owner = THIS_MODULE; - i2c_stuff->adapter.id = 0x01FFFF; - i2c_stuff->adapter.class = 0; - i2c_stuff->adapter.algo_data = &i2c_stuff->algo; - i2c_stuff->adapter.dev.parent = NULL; - i2c_stuff->algo.setsda = via_i2c_setsda; - i2c_stuff->algo.setscl = via_i2c_setscl; - i2c_stuff->algo.getsda = via_i2c_getsda; - i2c_stuff->algo.getscl = via_i2c_getscl; - i2c_stuff->algo.udelay = 40; - i2c_stuff->algo.timeout = 20; - i2c_stuff->algo.data = i2c_stuff; - - i2c_set_adapdata(&i2c_stuff->adapter, i2c_stuff); + struct via_i2c_stuff *stuff = &via_i2c_par[which]; - /* Raise SCL and SDA */ - i2c_stuff->i2c_port = I2CPORTINDEX; - via_i2c_setsda(i2c_stuff, 1); - via_i2c_setscl(i2c_stuff, 1); + return &stuff->adapter; +} +EXPORT_SYMBOL_GPL(viafb_find_i2c_adapter); - i2c_stuff->i2c_port = GPIOPORTINDEX; - via_i2c_setsda(i2c_stuff, 1); - via_i2c_setscl(i2c_stuff, 1); - udelay(20); - ret = i2c_bit_add_bus(&i2c_stuff->adapter); - if (ret == 0) - DEBUG_MSG("I2C bus %s registered.\n", i2c_stuff->adapter.name); +static int create_i2c_bus(struct i2c_adapter *adapter, + struct i2c_algo_bit_data *algo, + struct via_port_cfg *adap_cfg, + struct pci_dev *pdev) +{ + algo->setsda = via_i2c_setsda; + algo->setscl = via_i2c_setscl; + algo->getsda = via_i2c_getsda; + algo->getscl = via_i2c_getscl; + algo->udelay = 40; + algo->timeout = 20; + algo->data = adap_cfg; + + sprintf(adapter->name, "viafb i2c io_port idx 0x%02x", + adap_cfg->ioport_index); + adapter->owner = THIS_MODULE; + adapter->id = 0x01FFFF; + adapter->class = I2C_CLASS_DDC; + adapter->algo_data = algo; + if (pdev) + adapter->dev.parent = &pdev->dev; else - DEBUG_MSG("Failed to register I2C bus %s.\n", - i2c_stuff->adapter.name); - return ret; + adapter->dev.parent = NULL; + /* i2c_set_adapdata(adapter, adap_cfg); */ + + /* Raise SCL and SDA */ + via_i2c_setsda(adap_cfg, 1); + via_i2c_setscl(adap_cfg, 1); + udelay(20); + + return i2c_bit_add_bus(adapter); +} + +static int viafb_i2c_probe(struct platform_device *platdev) +{ + int i, ret; + struct via_port_cfg *configs; + + i2c_vdev = platdev->dev.platform_data; + configs = i2c_vdev->port_cfg; + + for (i = 0; i < VIAFB_NUM_PORTS; i++) { + struct via_port_cfg *adap_cfg = configs++; + struct via_i2c_stuff *i2c_stuff = &via_i2c_par[i]; + + i2c_stuff->is_active = 0; + if (adap_cfg->type == 0 || adap_cfg->mode != VIA_MODE_I2C) + continue; + ret = create_i2c_bus(&i2c_stuff->adapter, + &i2c_stuff->algo, adap_cfg, + NULL); /* FIXME: PCIDEV */ + if (ret < 0) { + printk(KERN_ERR "viafb: cannot create i2c bus %u:%d\n", + i, ret); + continue; /* Still try to make the rest */ + } + i2c_stuff->is_active = 1; + } + + return 0; +} + +static int viafb_i2c_remove(struct platform_device *platdev) +{ + int i; + + for (i = 0; i < VIAFB_NUM_PORTS; i++) { + struct via_i2c_stuff *i2c_stuff = &via_i2c_par[i]; + /* + * Only remove those entries in the array that we've + * actually used (and thus initialized algo_data) + */ + if (i2c_stuff->is_active) + i2c_del_adapter(&i2c_stuff->adapter); + } + return 0; +} + +static struct platform_driver via_i2c_driver = { + .driver = { + .name = "viafb-i2c", + }, + .probe = viafb_i2c_probe, + .remove = viafb_i2c_remove, +}; + +int viafb_i2c_init(void) +{ + return platform_driver_register(&via_i2c_driver); } -void viafb_delete_i2c_buss(void *par) +void viafb_i2c_exit(void) { - i2c_del_adapter(&((struct viafb_par *)par)->shared->i2c_stuff.adapter); + platform_driver_unregister(&via_i2c_driver); } diff --git a/drivers/video/via/via_modesetting.c b/drivers/video/via/via_modesetting.c new file mode 100644 index 0000000..3cddcff --- /dev/null +++ b/drivers/video/via/via_modesetting.c @@ -0,0 +1,126 @@ +/* + * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. + * Copyright 2010 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * 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, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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. + */ +/* + * basic modesetting functions + */ + +#include <linux/kernel.h> +#include <linux/via-core.h> +#include "via_modesetting.h" +#include "share.h" +#include "debug.h" + +void via_set_primary_address(u32 addr) +{ + DEBUG_MSG(KERN_DEBUG "via_set_primary_address(0x%08X)\n", addr); + via_write_reg(VIACR, 0x0D, addr & 0xFF); + via_write_reg(VIACR, 0x0C, (addr >> 8) & 0xFF); + via_write_reg(VIACR, 0x34, (addr >> 16) & 0xFF); + via_write_reg_mask(VIACR, 0x48, (addr >> 24) & 0x1F, 0x1F); +} + +void via_set_secondary_address(u32 addr) +{ + DEBUG_MSG(KERN_DEBUG "via_set_secondary_address(0x%08X)\n", addr); + /* secondary display supports only quadword aligned memory */ + via_write_reg_mask(VIACR, 0x62, (addr >> 2) & 0xFE, 0xFE); + via_write_reg(VIACR, 0x63, (addr >> 10) & 0xFF); + via_write_reg(VIACR, 0x64, (addr >> 18) & 0xFF); + via_write_reg_mask(VIACR, 0xA3, (addr >> 26) & 0x07, 0x07); +} + +void via_set_primary_pitch(u32 pitch) +{ + DEBUG_MSG(KERN_DEBUG "via_set_primary_pitch(0x%08X)\n", pitch); + /* spec does not say that first adapter skips 3 bits but old + * code did it and seems to be reasonable in analogy to 2nd adapter + */ + pitch = pitch >> 3; + via_write_reg(VIACR, 0x13, pitch & 0xFF); + via_write_reg_mask(VIACR, 0x35, (pitch >> (8 - 5)) & 0xE0, 0xE0); +} + +void via_set_secondary_pitch(u32 pitch) +{ + DEBUG_MSG(KERN_DEBUG "via_set_secondary_pitch(0x%08X)\n", pitch); + pitch = pitch >> 3; + via_write_reg(VIACR, 0x66, pitch & 0xFF); + via_write_reg_mask(VIACR, 0x67, (pitch >> 8) & 0x03, 0x03); + via_write_reg_mask(VIACR, 0x71, (pitch >> (10 - 7)) & 0x80, 0x80); +} + +void via_set_primary_color_depth(u8 depth) +{ + u8 value; + + DEBUG_MSG(KERN_DEBUG "via_set_primary_color_depth(%d)\n", depth); + switch (depth) { + case 8: + value = 0x00; + break; + case 15: + value = 0x04; + break; + case 16: + value = 0x14; + break; + case 24: + value = 0x0C; + break; + case 30: + value = 0x08; + break; + default: + printk(KERN_WARNING "via_set_primary_color_depth: " + "Unsupported depth: %d\n", depth); + return; + } + + via_write_reg_mask(VIASR, 0x15, value, 0x1C); +} + +void via_set_secondary_color_depth(u8 depth) +{ + u8 value; + + DEBUG_MSG(KERN_DEBUG "via_set_secondary_color_depth(%d)\n", depth); + switch (depth) { + case 8: + value = 0x00; + break; + case 16: + value = 0x40; + break; + case 24: + value = 0xC0; + break; + case 30: + value = 0x80; + break; + default: + printk(KERN_WARNING "via_set_secondary_color_depth: " + "Unsupported depth: %d\n", depth); + return; + } + + via_write_reg_mask(VIACR, 0x67, value, 0xC0); +} diff --git a/drivers/video/via/via_i2c.h b/drivers/video/via/via_modesetting.h index 3a13242..ae35cfd 100644 --- a/drivers/video/via/via_i2c.h +++ b/drivers/video/via/via_modesetting.h @@ -1,46 +1,38 @@ /* * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved. * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. - + * Copyright 2010 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * * 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, or (at your option) any later version. - + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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. */ -#ifndef __VIA_I2C_H__ -#define __VIA_I2C_H__ +/* + * basic modesetting functions + */ -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> +#ifndef __VIA_MODESETTING_H__ +#define __VIA_MODESETTING_H__ -struct via_i2c_stuff { - u16 i2c_port; /* GPIO or I2C port */ - struct i2c_adapter adapter; - struct i2c_algo_bit_data algo; -}; +#include <linux/types.h> -#define I2CPORT 0x3c4 -#define I2CPORTINDEX 0x31 -#define GPIOPORT 0x3C4 -#define GPIOPORTINDEX 0x2C -#define I2C_BUS 1 -#define GPIO_BUS 2 -#define DELAYPORT 0x3C3 +void via_set_primary_address(u32 addr); +void via_set_secondary_address(u32 addr); +void via_set_primary_pitch(u32 pitch); +void via_set_secondary_pitch(u32 pitch); +void via_set_primary_color_depth(u8 depth); +void via_set_secondary_color_depth(u8 depth); -int viafb_i2c_readbyte(u8 slave_addr, u8 index, u8 *pdata); -int viafb_i2c_writebyte(u8 slave_addr, u8 index, u8 data); -int viafb_i2c_readbytes(u8 slave_addr, u8 index, u8 *buff, int buff_len); -int viafb_create_i2c_bus(void *par); -void viafb_delete_i2c_buss(void *par); -#endif /* __VIA_I2C_H__ */ +#endif /* __VIA_MODESETTING_H__ */ diff --git a/drivers/video/via/via_utility.c b/drivers/video/via/via_utility.c index aefdeee..d05ccb6 100644 --- a/drivers/video/via/via_utility.c +++ b/drivers/video/via/via_utility.c @@ -19,6 +19,7 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <linux/via-core.h> #include "global.h" void viafb_get_device_support_state(u32 *support_state) diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 777b38a..2bc40e6 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -1,5 +1,5 @@ /* - * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved. + * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. * This program is free software; you can redistribute it and/or @@ -23,8 +23,9 @@ #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/stat.h> -#define _MASTER_FILE +#include <linux/via-core.h> +#define _MASTER_FILE #include "global.h" static char *viafb_name = "Via"; @@ -221,7 +222,7 @@ static int viafb_check_var(struct fb_var_screeninfo *var, /* Adjust var according to our driver's own table */ viafb_fill_var_timing_info(var, viafb_refresh, vmode_entry); if (info->var.accel_flags & FB_ACCELF_TEXT && - !ppar->shared->engine_mmio) + !ppar->shared->vdev->engine_mmio) info->var.accel_flags = 0; return 0; @@ -317,12 +318,12 @@ static int viafb_pan_display(struct fb_var_screeninfo *var, DEBUG_MSG(KERN_DEBUG "viafb_pan_display, address = %d\n", vram_addr); if (!viafb_dual_fb) { - viafb_set_primary_address(vram_addr); - viafb_set_secondary_address(vram_addr); + via_set_primary_address(vram_addr); + via_set_secondary_address(vram_addr); } else if (viapar->iga_path == IGA1) - viafb_set_primary_address(vram_addr); + via_set_primary_address(vram_addr); else - viafb_set_secondary_address(vram_addr); + via_set_secondary_address(vram_addr); return 0; } @@ -696,7 +697,7 @@ static void viafb_fillrect(struct fb_info *info, rop = 0xF0; DEBUG_MSG(KERN_DEBUG "viafb 2D engine: fillrect\n"); - if (shared->hw_bitblt(shared->engine_mmio, VIA_BITBLT_FILL, + if (shared->hw_bitblt(shared->vdev->engine_mmio, VIA_BITBLT_FILL, rect->width, rect->height, info->var.bits_per_pixel, viapar->vram_addr, info->fix.line_length, rect->dx, rect->dy, NULL, 0, 0, 0, 0, fg_color, 0, rop)) @@ -718,7 +719,7 @@ static void viafb_copyarea(struct fb_info *info, return; DEBUG_MSG(KERN_DEBUG "viafb 2D engine: copyarea\n"); - if (shared->hw_bitblt(shared->engine_mmio, VIA_BITBLT_COLOR, + if (shared->hw_bitblt(shared->vdev->engine_mmio, VIA_BITBLT_COLOR, area->width, area->height, info->var.bits_per_pixel, viapar->vram_addr, info->fix.line_length, area->dx, area->dy, NULL, viapar->vram_addr, info->fix.line_length, @@ -755,7 +756,7 @@ static void viafb_imageblit(struct fb_info *info, op = VIA_BITBLT_COLOR; DEBUG_MSG(KERN_DEBUG "viafb 2D engine: imageblit\n"); - if (shared->hw_bitblt(shared->engine_mmio, op, + if (shared->hw_bitblt(shared->vdev->engine_mmio, op, image->width, image->height, info->var.bits_per_pixel, viapar->vram_addr, info->fix.line_length, image->dx, image->dy, (u32 *)image->data, 0, 0, 0, 0, fg_color, bg_color, 0)) @@ -765,7 +766,7 @@ static void viafb_imageblit(struct fb_info *info, static int viafb_cursor(struct fb_info *info, struct fb_cursor *cursor) { struct viafb_par *viapar = info->par; - void __iomem *engine = viapar->shared->engine_mmio; + void __iomem *engine = viapar->shared->vdev->engine_mmio; u32 temp, xx, yy, bg_color = 0, fg_color = 0, chip_name = viapar->shared->chip_info.gfx_chip_name; int i, j = 0, cur_size = 64; @@ -1018,8 +1019,8 @@ static void viafb_set_device(struct device_t active_dev) viafb_SAMM_ON = active_dev.samm; viafb_primary_dev = active_dev.primary_dev; - viafb_set_primary_address(0); - viafb_set_secondary_address(viafb_SAMM_ON ? viafb_second_offset : 0); + via_set_primary_address(0); + via_set_secondary_address(viafb_SAMM_ON ? viafb_second_offset : 0); viafb_set_iga_path(); } @@ -1165,8 +1166,9 @@ static int apply_device_setting(struct viafb_ioctl_setting setting_info, if (viafb_SAMM_ON) viafb_primary_dev = setting_info.primary_device; - viafb_set_primary_address(0); - viafb_set_secondary_address(viafb_SAMM_ON ? viafb_second_offset : 0); + via_set_primary_address(0); + via_set_secondary_address(viafb_SAMM_ON ? + viafb_second_offset : 0); viafb_set_iga_path(); } need_set_mode = 1; @@ -1325,6 +1327,8 @@ static void parse_dvi_port(void) output_interface); } +#ifdef CONFIG_FB_VIA_DIRECT_PROCFS + /* * The proc filesystem read/write function, a simple proc implement to * get/set the value of DPA DVP0, DVP0DataDriving, DVP0ClockDriving, DVP1, @@ -1701,16 +1705,21 @@ static void viafb_init_proc(struct proc_dir_entry **viafb_entry) } static void viafb_remove_proc(struct proc_dir_entry *viafb_entry) { - /* no problem if it was not registered */ + struct chip_information *chip_info = &viaparinfo->shared->chip_info; + remove_proc_entry("dvp0", viafb_entry);/* parent dir */ remove_proc_entry("dvp1", viafb_entry); remove_proc_entry("dfph", viafb_entry); remove_proc_entry("dfpl", viafb_entry); - remove_proc_entry("vt1636", viafb_entry); - remove_proc_entry("vt1625", viafb_entry); + if (chip_info->lvds_chip_info.lvds_chip_name == VT1636_LVDS + || chip_info->lvds_chip_info2.lvds_chip_name == VT1636_LVDS) + remove_proc_entry("vt1636", viafb_entry); + remove_proc_entry("viafb", NULL); } +#endif /* CONFIG_FB_VIA_DIRECT_PROCFS */ + static int parse_mode(const char *str, u32 *xres, u32 *yres) { char *ptr; @@ -1732,12 +1741,13 @@ static int parse_mode(const char *str, u32 *xres, u32 *yres) return 0; } -static int __devinit via_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) + +int __devinit via_fb_pci_probe(struct viafb_dev *vdev) { u32 default_xres, default_yres; struct VideoModeTable *vmode_entry; struct fb_var_screeninfo default_var; + int rc; u32 viafb_par_length; DEBUG_MSG(KERN_INFO "VIAFB PCI Probe!!\n"); @@ -1749,14 +1759,15 @@ static int __devinit via_pci_probe(struct pci_dev *pdev, */ viafbinfo = framebuffer_alloc(viafb_par_length + ALIGN(sizeof(struct viafb_shared), BITS_PER_LONG/8), - &pdev->dev); + &vdev->pdev->dev); if (!viafbinfo) { printk(KERN_ERR"Could not allocate memory for viafb_info.\n"); - return -ENODEV; + return -ENOMEM; } viaparinfo = (struct viafb_par *)viafbinfo->par; viaparinfo->shared = viafbinfo->par + viafb_par_length; + viaparinfo->shared->vdev = vdev; viaparinfo->vram_addr = 0; viaparinfo->tmds_setting_info = &viaparinfo->shared->tmds_setting_info; viaparinfo->lvds_setting_info = &viaparinfo->shared->lvds_setting_info; @@ -1774,23 +1785,20 @@ static int __devinit via_pci_probe(struct pci_dev *pdev, if (!viafb_SAMM_ON) viafb_dual_fb = 0; - /* Set up I2C bus stuff */ - viafb_create_i2c_bus(viaparinfo); - - viafb_init_chip_info(pdev, ent); - viaparinfo->fbmem = pci_resource_start(pdev, 0); - viaparinfo->memsize = viafb_get_fb_size_from_pci(); + viafb_init_chip_info(vdev->chip_type); + /* + * The framebuffer will have been successfully mapped by + * the core (or we'd not be here), but we still need to + * set up our own accounting. + */ + viaparinfo->fbmem = vdev->fbmem_start; + viaparinfo->memsize = vdev->fbmem_len; viaparinfo->fbmem_free = viaparinfo->memsize; viaparinfo->fbmem_used = 0; - viafbinfo->screen_base = ioremap_nocache(viaparinfo->fbmem, - viaparinfo->memsize); - if (!viafbinfo->screen_base) { - printk(KERN_INFO "ioremap failed\n"); - return -ENOMEM; - } + viafbinfo->screen_base = vdev->fbmem; - viafbinfo->fix.mmio_start = pci_resource_start(pdev, 1); - viafbinfo->fix.mmio_len = pci_resource_len(pdev, 1); + viafbinfo->fix.mmio_start = vdev->engine_start; + viafbinfo->fix.mmio_len = vdev->engine_len; viafbinfo->node = 0; viafbinfo->fbops = &viafb_ops; viafbinfo->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; @@ -1858,12 +1866,13 @@ static int __devinit via_pci_probe(struct pci_dev *pdev, viafbinfo->var = default_var; if (viafb_dual_fb) { - viafbinfo1 = framebuffer_alloc(viafb_par_length, &pdev->dev); + viafbinfo1 = framebuffer_alloc(viafb_par_length, + &vdev->pdev->dev); if (!viafbinfo1) { printk(KERN_ERR "allocate the second framebuffer struct error\n"); - framebuffer_release(viafbinfo); - return -ENOMEM; + rc = -ENOMEM; + goto out_fb_release; } viaparinfo1 = viafbinfo1->par; memcpy(viaparinfo1, viaparinfo, viafb_par_length); @@ -1914,48 +1923,66 @@ static int __devinit via_pci_probe(struct pci_dev *pdev, viaparinfo->depth = fb_get_color_depth(&viafbinfo->var, &viafbinfo->fix); default_var.activate = FB_ACTIVATE_NOW; - fb_alloc_cmap(&viafbinfo->cmap, 256, 0); + rc = fb_alloc_cmap(&viafbinfo->cmap, 256, 0); + if (rc) + goto out_fb1_release; if (viafb_dual_fb && (viafb_primary_dev == LCD_Device) && (viaparinfo->chip_info->gfx_chip_name == UNICHROME_CLE266)) { - if (register_framebuffer(viafbinfo1) < 0) - return -EINVAL; + rc = register_framebuffer(viafbinfo1); + if (rc) + goto out_dealloc_cmap; } - if (register_framebuffer(viafbinfo) < 0) - return -EINVAL; + rc = register_framebuffer(viafbinfo); + if (rc) + goto out_fb1_unreg_lcd_cle266; if (viafb_dual_fb && ((viafb_primary_dev != LCD_Device) || (viaparinfo->chip_info->gfx_chip_name != UNICHROME_CLE266))) { - if (register_framebuffer(viafbinfo1) < 0) - return -EINVAL; + rc = register_framebuffer(viafbinfo1); + if (rc) + goto out_fb_unreg; } DEBUG_MSG(KERN_INFO "fb%d: %s frame buffer device %dx%d-%dbpp\n", viafbinfo->node, viafbinfo->fix.id, default_var.xres, default_var.yres, default_var.bits_per_pixel); +#ifdef CONFIG_FB_VIA_DIRECT_PROCFS viafb_init_proc(&viaparinfo->shared->proc_entry); +#endif viafb_init_dac(IGA2); return 0; + +out_fb_unreg: + unregister_framebuffer(viafbinfo); +out_fb1_unreg_lcd_cle266: + if (viafb_dual_fb && (viafb_primary_dev == LCD_Device) + && (viaparinfo->chip_info->gfx_chip_name == UNICHROME_CLE266)) + unregister_framebuffer(viafbinfo1); +out_dealloc_cmap: + fb_dealloc_cmap(&viafbinfo->cmap); +out_fb1_release: + if (viafbinfo1) + framebuffer_release(viafbinfo1); +out_fb_release: + framebuffer_release(viafbinfo); + return rc; } -static void __devexit via_pci_remove(struct pci_dev *pdev) +void __devexit via_fb_pci_remove(struct pci_dev *pdev) { DEBUG_MSG(KERN_INFO "via_pci_remove!\n"); fb_dealloc_cmap(&viafbinfo->cmap); unregister_framebuffer(viafbinfo); if (viafb_dual_fb) unregister_framebuffer(viafbinfo1); - iounmap((void *)viafbinfo->screen_base); - iounmap(viaparinfo->shared->engine_mmio); - - viafb_delete_i2c_buss(viaparinfo); - +#ifdef CONFIG_FB_VIA_DIRECT_PROCFS + viafb_remove_proc(viaparinfo->shared->proc_entry); +#endif framebuffer_release(viafbinfo); if (viafb_dual_fb) framebuffer_release(viafbinfo1); - - viafb_remove_proc(viaparinfo->shared->proc_entry); } #ifndef MODULE @@ -2031,41 +2058,10 @@ static int __init viafb_setup(char *options) } #endif -static struct pci_device_id viafb_pci_table[] __devinitdata = { - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CLE266_DID), - .driver_data = UNICHROME_CLE266 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_PM800_DID), - .driver_data = UNICHROME_PM800 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K400_DID), - .driver_data = UNICHROME_K400 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K800_DID), - .driver_data = UNICHROME_K800 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M890_DID), - .driver_data = UNICHROME_CN700 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K8M890_DID), - .driver_data = UNICHROME_K8M890 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CX700_DID), - .driver_data = UNICHROME_CX700 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M900_DID), - .driver_data = UNICHROME_P4M900 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CN750_DID), - .driver_data = UNICHROME_CN750 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX800_DID), - .driver_data = UNICHROME_VX800 }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX855_DID), - .driver_data = UNICHROME_VX855 }, - { } -}; -MODULE_DEVICE_TABLE(pci, viafb_pci_table); - -static struct pci_driver viafb_driver = { - .name = "viafb", - .id_table = viafb_pci_table, - .probe = via_pci_probe, - .remove = __devexit_p(via_pci_remove), -}; - -static int __init viafb_init(void) +/* + * These are called out of via-core for now. + */ +int __init viafb_init(void) { u32 dummy; #ifndef MODULE @@ -2084,13 +2080,12 @@ static int __init viafb_init(void) printk(KERN_INFO "VIA Graphics Intergration Chipset framebuffer %d.%d initializing\n", VERSION_MAJOR, VERSION_MINOR); - return pci_register_driver(&viafb_driver); + return 0; } -static void __exit viafb_exit(void) +void __exit viafb_exit(void) { DEBUG_MSG(KERN_INFO "viafb_exit!\n"); - pci_unregister_driver(&viafb_driver); } static struct fb_ops viafb_ops = { @@ -2110,8 +2105,6 @@ static struct fb_ops viafb_ops = { .fb_sync = viafb_sync, }; -module_init(viafb_init); -module_exit(viafb_exit); #ifdef MODULE module_param(viafb_mode, charp, S_IRUSR); diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h index 61b5953..52a35fa 100644 --- a/drivers/video/via/viafbdev.h +++ b/drivers/video/via/viafbdev.h @@ -24,12 +24,12 @@ #include <linux/proc_fs.h> #include <linux/fb.h> +#include <linux/spinlock.h> #include "ioctl.h" #include "share.h" #include "chip.h" #include "hw.h" -#include "via_i2c.h" #define VERSION_MAJOR 2 #define VERSION_KERNEL 6 /* For kernel 2.6 */ @@ -37,11 +37,11 @@ #define VERSION_OS 0 /* 0: for 32 bits OS, 1: for 64 bits OS */ #define VERSION_MINOR 4 +#define VIAFB_NUM_I2C 5 + struct viafb_shared { struct proc_dir_entry *proc_entry; /*viafb proc entry */ - - /* I2C stuff */ - struct via_i2c_stuff i2c_stuff; + struct viafb_dev *vdev; /* Global dev info */ /* All the information will be needed to set engine */ struct tmds_setting_information tmds_setting_info; @@ -51,7 +51,6 @@ struct viafb_shared { struct chip_information chip_info; /* hardware acceleration stuff */ - void __iomem *engine_mmio; u32 cursor_vram_addr; u32 vq_vram_addr; /* virtual queue address in video ram */ int (*hw_bitblt)(void __iomem *engine, u8 op, u32 width, u32 height, @@ -99,4 +98,9 @@ u8 viafb_gpio_i2c_read_lvds(struct lvds_setting_information void viafb_gpio_i2c_write_mask_lvds(struct lvds_setting_information *plvds_setting_info, struct lvds_chip_information *plvds_chip_info, struct IODATA io_data); +int via_fb_pci_probe(struct viafb_dev *vdev); +void via_fb_pci_remove(struct pci_dev *pdev); +/* Temporary */ +int viafb_init(void); +void viafb_exit(void); #endif /* __VIAFBDEV_H__ */ diff --git a/drivers/video/via/viamode.c b/drivers/video/via/viamode.c index af50e24..2dbad3c 100644 --- a/drivers/video/via/viamode.c +++ b/drivers/video/via/viamode.c @@ -19,6 +19,7 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <linux/via-core.h> #include "global.h" struct res_map_refresh res_map_refresh_tbl[] = { /*hres, vres, vclock, vmode_refresh*/ @@ -66,6 +67,7 @@ struct res_map_refresh res_map_refresh_tbl[] = { {1088, 612, RES_1088X612_60HZ_PIXCLOCK, 60}, {1152, 720, RES_1152X720_60HZ_PIXCLOCK, 60}, {1200, 720, RES_1200X720_60HZ_PIXCLOCK, 60}, + {1200, 900, RES_1200X900_60HZ_PIXCLOCK, 60}, {1280, 600, RES_1280X600_60HZ_PIXCLOCK, 60}, {1280, 720, RES_1280X720_50HZ_PIXCLOCK, 50}, {1280, 768, RES_1280X768_50HZ_PIXCLOCK, 50}, @@ -759,6 +761,16 @@ struct crt_mode_table CRTM1200x720[] = { {1568, 1200, 1200, 368, 1256, 128, 746, 720, 720, 26, 721, 3} } }; +/* 1200x900 (DCON) */ +struct crt_mode_table DCON1200x900[] = { + /* r_rate, vclk, hsp, vsp */ + {REFRESH_60, CLK_57_275M, M1200X900_R60_HSP, M1200X900_R60_VSP, + /* The correct htotal is 1240, but this doesn't raster on VX855. */ + /* Via suggested changing to a multiple of 16, hence 1264. */ + /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ + {1264, 1200, 1200, 64, 1211, 32, 912, 900, 900, 12, 901, 10} } +}; + /* 1280x600 (GTF) */ struct crt_mode_table CRTM1280x600[] = { /* r_rate, vclk, hsp, vsp */ @@ -937,6 +949,9 @@ struct VideoModeTable viafb_modes[] = { /* Display : 1200x720 (GTF) */ {CRTM1200x720, ARRAY_SIZE(CRTM1200x720)}, + /* Display : 1200x900 (DCON) */ + {DCON1200x900, ARRAY_SIZE(DCON1200x900)}, + /* Display : 1280x600 (GTF) */ {CRTM1280x600, ARRAY_SIZE(CRTM1280x600)}, diff --git a/drivers/video/via/vt1636.c b/drivers/video/via/vt1636.c index a6b3749..d65bf1a 100644 --- a/drivers/video/via/vt1636.c +++ b/drivers/video/via/vt1636.c @@ -19,6 +19,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <linux/via-core.h> +#include <linux/via_i2c.h> #include "global.h" u8 viafb_gpio_i2c_read_lvds(struct lvds_setting_information @@ -27,9 +29,8 @@ u8 viafb_gpio_i2c_read_lvds(struct lvds_setting_information { u8 data; - viaparinfo->shared->i2c_stuff.i2c_port = plvds_chip_info->i2c_port; - viafb_i2c_readbyte(plvds_chip_info->lvds_chip_slave_addr, index, &data); - + viafb_i2c_readbyte(plvds_chip_info->i2c_port, + plvds_chip_info->lvds_chip_slave_addr, index, &data); return data; } @@ -39,14 +40,13 @@ void viafb_gpio_i2c_write_mask_lvds(struct lvds_setting_information { int index, data; - viaparinfo->shared->i2c_stuff.i2c_port = plvds_chip_info->i2c_port; - index = io_data.Index; data = viafb_gpio_i2c_read_lvds(plvds_setting_info, plvds_chip_info, index); data = (data & (~io_data.Mask)) | io_data.Data; - viafb_i2c_writebyte(plvds_chip_info->lvds_chip_slave_addr, index, data); + viafb_i2c_writebyte(plvds_chip_info->i2c_port, + plvds_chip_info->lvds_chip_slave_addr, index, data); } void viafb_init_lvds_vt1636(struct lvds_setting_information @@ -159,7 +159,7 @@ void viafb_disable_lvds_vt1636(struct lvds_setting_information } } -bool viafb_lvds_identify_vt1636(void) +bool viafb_lvds_identify_vt1636(u8 i2c_adapter) { u8 Buffer[2]; @@ -167,26 +167,20 @@ bool viafb_lvds_identify_vt1636(void) /* Sense VT1636 LVDS Transmiter */ viaparinfo->chip_info->lvds_chip_info.lvds_chip_slave_addr = - VT1636_LVDS_I2C_ADDR; + VT1636_LVDS_I2C_ADDR; /* Check vendor ID first: */ - viafb_i2c_readbyte((u8) viaparinfo->chip_info->lvds_chip_info. - lvds_chip_slave_addr, - 0x00, &Buffer[0]); - viafb_i2c_readbyte((u8) viaparinfo->chip_info->lvds_chip_info. - lvds_chip_slave_addr, - 0x01, &Buffer[1]); + if (viafb_i2c_readbyte(i2c_adapter, VT1636_LVDS_I2C_ADDR, + 0x00, &Buffer[0])) + return false; + viafb_i2c_readbyte(i2c_adapter, VT1636_LVDS_I2C_ADDR, 0x01, &Buffer[1]); if (!((Buffer[0] == 0x06) && (Buffer[1] == 0x11))) return false; /* Check Chip ID: */ - viafb_i2c_readbyte((u8) viaparinfo->chip_info->lvds_chip_info. - lvds_chip_slave_addr, - 0x02, &Buffer[0]); - viafb_i2c_readbyte((u8) viaparinfo->chip_info->lvds_chip_info. - lvds_chip_slave_addr, - 0x03, &Buffer[1]); + viafb_i2c_readbyte(i2c_adapter, VT1636_LVDS_I2C_ADDR, 0x02, &Buffer[0]); + viafb_i2c_readbyte(i2c_adapter, VT1636_LVDS_I2C_ADDR, 0x03, &Buffer[1]); if ((Buffer[0] == 0x45) && (Buffer[1] == 0x33)) { viaparinfo->chip_info->lvds_chip_info.lvds_chip_name = VT1636_LVDS; diff --git a/drivers/video/via/vt1636.h b/drivers/video/via/vt1636.h index 2a150c5..4c1314e 100644 --- a/drivers/video/via/vt1636.h +++ b/drivers/video/via/vt1636.h @@ -22,7 +22,7 @@ #ifndef _VT1636_H_ #define _VT1636_H_ #include "chip.h" -bool viafb_lvds_identify_vt1636(void); +bool viafb_lvds_identify_vt1636(u8 i2c_adapter); void viafb_init_lvds_vt1636(struct lvds_setting_information *plvds_setting_info, struct lvds_chip_information *plvds_chip_info); void viafb_enable_lvds_vt1636(struct lvds_setting_information |