linux/drivers/video/fbdev/savage/savagefb-i2c.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/video/savage/savagefb-i2c.c - S3 Savage DDC2
   3 *
   4 * Copyright 2004 Antonino A. Daplas <adaplas @pol.net>
   5 *
   6 * Based partly on rivafb-i2c.c
   7 *
   8 * This file is subject to the terms and conditions of the GNU General Public
   9 * License.  See the file COPYING in the main directory of this archive
  10 * for more details.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/kernel.h>
  15#include <linux/delay.h>
  16#include <linux/gfp.h>
  17#include <linux/pci.h>
  18#include <linux/fb.h>
  19
  20#include <asm/io.h>
  21#include "savagefb.h"
  22
  23#define SAVAGE_DDC      0x50
  24
  25#define VGA_CR_IX       0x3d4
  26#define VGA_CR_DATA     0x3d5
  27
  28#define CR_SERIAL1      0xa0    /* I2C serial communications interface */
  29#define MM_SERIAL1      0xff20
  30#define CR_SERIAL2      0xb1    /* DDC2 monitor communications interface */
  31
  32/* based on vt8365 documentation */
  33#define PROSAVAGE_I2C_ENAB      0x10
  34#define PROSAVAGE_I2C_SCL_OUT   0x01
  35#define PROSAVAGE_I2C_SDA_OUT   0x02
  36#define PROSAVAGE_I2C_SCL_IN    0x04
  37#define PROSAVAGE_I2C_SDA_IN    0x08
  38
  39#define SAVAGE4_I2C_ENAB        0x00000020
  40#define SAVAGE4_I2C_SCL_OUT     0x00000001
  41#define SAVAGE4_I2C_SDA_OUT     0x00000002
  42#define SAVAGE4_I2C_SCL_IN      0x00000008
  43#define SAVAGE4_I2C_SDA_IN      0x00000010
  44
  45static void savage4_gpio_setscl(void *data, int val)
  46{
  47        struct savagefb_i2c_chan *chan = data;
  48        unsigned int r;
  49
  50        r = readl(chan->ioaddr + chan->reg);
  51        if(val)
  52                r |= SAVAGE4_I2C_SCL_OUT;
  53        else
  54                r &= ~SAVAGE4_I2C_SCL_OUT;
  55        writel(r, chan->ioaddr + chan->reg);
  56        readl(chan->ioaddr + chan->reg);        /* flush posted write */
  57}
  58
  59static void savage4_gpio_setsda(void *data, int val)
  60{
  61        struct savagefb_i2c_chan *chan = data;
  62
  63        unsigned int r;
  64        r = readl(chan->ioaddr + chan->reg);
  65        if(val)
  66                r |= SAVAGE4_I2C_SDA_OUT;
  67        else
  68                r &= ~SAVAGE4_I2C_SDA_OUT;
  69        writel(r, chan->ioaddr + chan->reg);
  70        readl(chan->ioaddr + chan->reg);        /* flush posted write */
  71}
  72
  73static int savage4_gpio_getscl(void *data)
  74{
  75        struct savagefb_i2c_chan *chan = data;
  76
  77        return (0 != (readl(chan->ioaddr + chan->reg) & SAVAGE4_I2C_SCL_IN));
  78}
  79
  80static int savage4_gpio_getsda(void *data)
  81{
  82        struct savagefb_i2c_chan *chan = data;
  83
  84        return (0 != (readl(chan->ioaddr + chan->reg) & SAVAGE4_I2C_SDA_IN));
  85}
  86
  87static void prosavage_gpio_setscl(void* data, int val)
  88{
  89        struct savagefb_i2c_chan *chan = data;
  90        u32                       r;
  91
  92        r = VGArCR(chan->reg, chan->par);
  93        r |= PROSAVAGE_I2C_ENAB;
  94        if (val) {
  95                r |= PROSAVAGE_I2C_SCL_OUT;
  96        } else {
  97                r &= ~PROSAVAGE_I2C_SCL_OUT;
  98        }
  99
 100        VGAwCR(chan->reg, r, chan->par);
 101}
 102
 103static void prosavage_gpio_setsda(void* data, int val)
 104{
 105        struct savagefb_i2c_chan *chan = data;
 106        unsigned int r;
 107
 108        r = VGArCR(chan->reg, chan->par);
 109        r |= PROSAVAGE_I2C_ENAB;
 110        if (val) {
 111                r |= PROSAVAGE_I2C_SDA_OUT;
 112        } else {
 113                r &= ~PROSAVAGE_I2C_SDA_OUT;
 114        }
 115
 116        VGAwCR(chan->reg, r, chan->par);
 117}
 118
 119static int prosavage_gpio_getscl(void* data)
 120{
 121        struct savagefb_i2c_chan *chan = data;
 122
 123        return (VGArCR(chan->reg, chan->par) & PROSAVAGE_I2C_SCL_IN) ? 1 : 0;
 124}
 125
 126static int prosavage_gpio_getsda(void* data)
 127{
 128        struct savagefb_i2c_chan *chan = data;
 129
 130        return (VGArCR(chan->reg, chan->par) & PROSAVAGE_I2C_SDA_IN) ? 1 : 0;
 131}
 132
 133static int savage_setup_i2c_bus(struct savagefb_i2c_chan *chan,
 134                                const char *name)
 135{
 136        int rc = 0;
 137
 138        if (chan->par) {
 139                strcpy(chan->adapter.name, name);
 140                chan->adapter.owner             = THIS_MODULE;
 141                chan->adapter.algo_data         = &chan->algo;
 142                chan->adapter.dev.parent        = &chan->par->pcidev->dev;
 143                chan->algo.udelay               = 10;
 144                chan->algo.timeout              = 20;
 145                chan->algo.data                 = chan;
 146
 147                i2c_set_adapdata(&chan->adapter, chan);
 148
 149                /* Raise SCL and SDA */
 150                chan->algo.setsda(chan, 1);
 151                chan->algo.setscl(chan, 1);
 152                udelay(20);
 153
 154                rc = i2c_bit_add_bus(&chan->adapter);
 155
 156                if (rc == 0)
 157                        dev_dbg(&chan->par->pcidev->dev,
 158                                "I2C bus %s registered.\n", name);
 159                else
 160                        dev_warn(&chan->par->pcidev->dev,
 161                                 "Failed to register I2C bus %s.\n", name);
 162        }
 163
 164        return rc;
 165}
 166
 167void savagefb_create_i2c_busses(struct fb_info *info)
 168{
 169        struct savagefb_par *par = info->par;
 170        par->chan.par   = par;
 171
 172        switch (par->chip) {
 173        case S3_PROSAVAGE:
 174        case S3_PROSAVAGEDDR:
 175        case S3_TWISTER:
 176                par->chan.reg         = CR_SERIAL2;
 177                par->chan.ioaddr      = par->mmio.vbase;
 178                par->chan.algo.setsda = prosavage_gpio_setsda;
 179                par->chan.algo.setscl = prosavage_gpio_setscl;
 180                par->chan.algo.getsda = prosavage_gpio_getsda;
 181                par->chan.algo.getscl = prosavage_gpio_getscl;
 182                break;
 183        case S3_SAVAGE4:
 184                par->chan.reg = CR_SERIAL1;
 185                if (par->pcidev->revision > 1 && !(VGArCR(0xa6, par) & 0x40))
 186                        par->chan.reg = CR_SERIAL2;
 187                par->chan.ioaddr      = par->mmio.vbase;
 188                par->chan.algo.setsda = prosavage_gpio_setsda;
 189                par->chan.algo.setscl = prosavage_gpio_setscl;
 190                par->chan.algo.getsda = prosavage_gpio_getsda;
 191                par->chan.algo.getscl = prosavage_gpio_getscl;
 192                break;
 193        case S3_SAVAGE2000:
 194                par->chan.reg         = MM_SERIAL1;
 195                par->chan.ioaddr      = par->mmio.vbase;
 196                par->chan.algo.setsda = savage4_gpio_setsda;
 197                par->chan.algo.setscl = savage4_gpio_setscl;
 198                par->chan.algo.getsda = savage4_gpio_getsda;
 199                par->chan.algo.getscl = savage4_gpio_getscl;
 200                break;
 201        default:
 202                par->chan.par = NULL;
 203        }
 204
 205        savage_setup_i2c_bus(&par->chan, "SAVAGE DDC2");
 206}
 207
 208void savagefb_delete_i2c_busses(struct fb_info *info)
 209{
 210        struct savagefb_par *par = info->par;
 211
 212        if (par->chan.par)
 213                i2c_del_adapter(&par->chan.adapter);
 214
 215        par->chan.par = NULL;
 216}
 217
 218int savagefb_probe_i2c_connector(struct fb_info *info, u8 **out_edid)
 219{
 220        struct savagefb_par *par = info->par;
 221        u8 *edid;
 222
 223        if (par->chan.par)
 224                edid = fb_ddc_read(&par->chan.adapter);
 225        else
 226                edid = NULL;
 227
 228        if (!edid) {
 229                /* try to get from firmware */
 230                const u8 *e = fb_firmware_edid(info->device);
 231
 232                if (e)
 233                        edid = kmemdup(e, EDID_LENGTH, GFP_KERNEL);
 234        }
 235
 236        *out_edid = edid;
 237
 238        return (edid) ? 0 : 1;
 239}
 240
 241MODULE_LICENSE("GPL");
 242