linux/drivers/video/intelfb/intelfb_i2c.c
<<
>>
Prefs
   1/**************************************************************************
   2
   3 Copyright 2006 Dave Airlie <airlied@linux.ie>
   4
   5All Rights Reserved.
   6
   7Permission is hereby granted, free of charge, to any person obtaining a
   8copy of this software and associated documentation files (the "Software"),
   9to deal in the Software without restriction, including without limitation
  10on the rights to use, copy, modify, merge, publish, distribute, sub
  11license, and/or sell copies of the Software, and to permit persons to whom
  12the Software is furnished to do so, subject to the following conditions:
  13
  14The above copyright notice and this permission notice (including the next
  15paragraph) shall be included in all copies or substantial portions of the
  16Software.
  17
  18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
  21THE COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
  22DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  23OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  24USE OR OTHER DEALINGS IN THE SOFTWARE.
  25
  26**************************************************************************/
  27
  28#include <linux/module.h>
  29#include <linux/kernel.h>
  30#include <linux/delay.h>
  31#include <linux/pci.h>
  32#include <linux/fb.h>
  33
  34#include <linux/i2c.h>
  35#include <linux/i2c-id.h>
  36#include <linux/i2c-algo-bit.h>
  37
  38#include <asm/io.h>
  39
  40#include "intelfb.h"
  41#include "intelfbhw.h"
  42
  43/* bit locations in the registers */
  44#define SCL_DIR_MASK            0x0001
  45#define SCL_DIR                 0x0002
  46#define SCL_VAL_MASK            0x0004
  47#define SCL_VAL_OUT             0x0008
  48#define SCL_VAL_IN              0x0010
  49#define SDA_DIR_MASK            0x0100
  50#define SDA_DIR                 0x0200
  51#define SDA_VAL_MASK            0x0400
  52#define SDA_VAL_OUT             0x0800
  53#define SDA_VAL_IN              0x1000
  54
  55static void intelfb_gpio_setscl(void *data, int state)
  56{
  57        struct intelfb_i2c_chan *chan = data;
  58        struct intelfb_info *dinfo = chan->dinfo;
  59        u32 val;
  60
  61        OUTREG(chan->reg, (state ? SCL_VAL_OUT : 0) |
  62               SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK);
  63        val = INREG(chan->reg);
  64}
  65
  66static void intelfb_gpio_setsda(void *data, int state)
  67{
  68        struct intelfb_i2c_chan *chan = data;
  69        struct intelfb_info *dinfo = chan->dinfo;
  70        u32 val;
  71
  72        OUTREG(chan->reg, (state ? SDA_VAL_OUT : 0) |
  73               SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK);
  74        val = INREG(chan->reg);
  75}
  76
  77static int intelfb_gpio_getscl(void *data)
  78{
  79        struct intelfb_i2c_chan *chan = data;
  80        struct intelfb_info *dinfo = chan->dinfo;
  81        u32 val;
  82
  83        OUTREG(chan->reg, SCL_DIR_MASK);
  84        OUTREG(chan->reg, 0);
  85        val = INREG(chan->reg);
  86        return ((val & SCL_VAL_IN) != 0);
  87}
  88
  89static int intelfb_gpio_getsda(void *data)
  90{
  91        struct intelfb_i2c_chan *chan = data;
  92        struct intelfb_info *dinfo = chan->dinfo;
  93        u32 val;
  94
  95        OUTREG(chan->reg, SDA_DIR_MASK);
  96        OUTREG(chan->reg, 0);
  97        val = INREG(chan->reg);
  98        return ((val & SDA_VAL_IN) != 0);
  99}
 100
 101static int intelfb_setup_i2c_bus(struct intelfb_info *dinfo,
 102                                 struct intelfb_i2c_chan *chan,
 103                                 const u32 reg, const char *name,
 104                                 int class)
 105{
 106        int rc;
 107
 108        chan->dinfo                     = dinfo;
 109        chan->reg                       = reg;
 110        snprintf(chan->adapter.name, sizeof(chan->adapter.name),
 111                 "intelfb %s", name);
 112        chan->adapter.class             = class;
 113        chan->adapter.owner             = THIS_MODULE;
 114        chan->adapter.algo_data         = &chan->algo;
 115        chan->adapter.dev.parent        = &chan->dinfo->pdev->dev;
 116        chan->algo.setsda               = intelfb_gpio_setsda;
 117        chan->algo.setscl               = intelfb_gpio_setscl;
 118        chan->algo.getsda               = intelfb_gpio_getsda;
 119        chan->algo.getscl               = intelfb_gpio_getscl;
 120        chan->algo.udelay               = 40;
 121        chan->algo.timeout              = 20;
 122        chan->algo.data                 = chan;
 123
 124        i2c_set_adapdata(&chan->adapter, chan);
 125
 126        /* Raise SCL and SDA */
 127        intelfb_gpio_setsda(chan, 1);
 128        intelfb_gpio_setscl(chan, 1);
 129        udelay(20);
 130
 131        rc = i2c_bit_add_bus(&chan->adapter);
 132        if (rc == 0)
 133                DBG_MSG("I2C bus %s registered.\n", name);
 134        else
 135                WRN_MSG("Failed to register I2C bus %s.\n", name);
 136        return rc;
 137}
 138
 139void intelfb_create_i2c_busses(struct intelfb_info *dinfo)
 140{
 141        int i = 0;
 142
 143        /* everyone has at least a single analog output */
 144        dinfo->num_outputs = 1;
 145        dinfo->output[i].type = INTELFB_OUTPUT_ANALOG;
 146
 147        /* setup the DDC bus for analog output */
 148        intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOA,
 149                              "CRTDDC_A", I2C_CLASS_DDC);
 150        i++;
 151
 152        /* need to add the output busses for each device
 153           - this function is very incomplete
 154           - i915GM has LVDS and TVOUT for example
 155        */
 156        switch(dinfo->chipset) {
 157        case INTEL_830M:
 158        case INTEL_845G:
 159        case INTEL_854:
 160        case INTEL_855GM:
 161        case INTEL_865G:
 162                dinfo->output[i].type = INTELFB_OUTPUT_DVO;
 163                intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus,
 164                                      GPIOD, "DVODDC_D", I2C_CLASS_DDC);
 165                intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus,
 166                                      GPIOE, "DVOI2C_E", 0);
 167                i++;
 168                break;
 169        case INTEL_915G:
 170        case INTEL_915GM:
 171                /* has some LVDS + tv-out */
 172        case INTEL_945G:
 173        case INTEL_945GM:
 174        case INTEL_945GME:
 175        case INTEL_965G:
 176        case INTEL_965GM:
 177                /* SDVO ports have a single control bus - 2 devices */
 178                dinfo->output[i].type = INTELFB_OUTPUT_SDVO;
 179                intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus,
 180                                      GPIOE, "SDVOCTRL_E", 0);
 181                /* TODO: initialize the SDVO */
 182                /* I830SDVOInit(pScrn, i, DVOB); */
 183                i++;
 184
 185                /* set up SDVOC */
 186                dinfo->output[i].type = INTELFB_OUTPUT_SDVO;
 187                dinfo->output[i].i2c_bus = dinfo->output[i - 1].i2c_bus;
 188                /* TODO: initialize the SDVO */
 189                /* I830SDVOInit(pScrn, i, DVOC); */
 190                i++;
 191                break;
 192        }
 193        dinfo->num_outputs = i;
 194}
 195
 196void intelfb_delete_i2c_busses(struct intelfb_info *dinfo)
 197{
 198        int i;
 199
 200        for (i = 0; i < MAX_OUTPUTS; i++) {
 201                if (dinfo->output[i].i2c_bus.dinfo) {
 202                        i2c_del_adapter(&dinfo->output[i].i2c_bus.adapter);
 203                        dinfo->output[i].i2c_bus.dinfo = NULL;
 204                }
 205                if (dinfo->output[i].ddc_bus.dinfo) {
 206                        i2c_del_adapter(&dinfo->output[i].ddc_bus.adapter);
 207                        dinfo->output[i].ddc_bus.dinfo = NULL;
 208                }
 209        }
 210}
 211