Logo Search packages:      
Sourcecode: lg-issue91 version File versions

vpx322xd.c

/*  Micronas vpx 3224D/3225D video processor i2c device driver. 
    Copyright Cherry George Mathew <cherry@freeshell.org>
    
    Based on Frodo Looijaard's <frodol@dds.nl> document on
    writing i2c clients. See Documentation/i2c/writing-clients.
    
    Special thanks to the i2c authors for excellent code templates and
    useful documentation.
    
*/

/*  This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/* #include <linux/version.h>  */
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/init.h> 
#include <linux/spinlock.h>   
#include <linux/byteorder/generic.h>
#include <linux/byteorder/swab.h>
#include <linux/sched.h> 
#include <linux/videodev.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include "pvcard.h"
#include "pvproc.h"

/* Module params. */
static int debug;

/* Functional level debugging */
#define dprintk(fmt, args...) if (debug>=1) printk(KERN_DEBUG "pvcl-debug: " fmt, ## args);
/* Debugging single functions */
#define tprintk(fmt, args...) if (debug>=2) printk(KERN_DEBUG "pvcl-debug: " fmt, ## args);
/* Warning - too verbose. Debugging port conversations. */
#define vprintk(fmt, args...) if (debug>=3) printk(KERN_DEBUG "pvcl-debug:" fmt, ## args);


/* module parameters: 
 */

const char *client_name = "vpx i2c bus interface"; 
static int re_entry = 0;

/* currently, vmode is the only "sticky" status variable in vpx.
 * Oh, yes, and vpx_client.
 */
static __u16 vmode;

/* This client pointer contains the address of a detected vpx chip, if any.
 * Multiple chips, are unlikely on the same adapter, and are ignored. 
 * In such cases, the chip with a lower I2C address is detected.
 */

static struct i2c_client * vpx_client;


int vpx_init_client(struct i2c_client *client);
int vpx_attach_adapter(struct i2c_adapter *adapter);
int vpx_detach_client(struct i2c_client *client);
int vpx_command(struct i2c_client *client, unsigned int cmd, void *arg);
void vpx_inc_use(struct i2c_client *client);
void vpx_dec_use(struct i2c_client *client);
void vpx_poll_fp_busy(struct i2c_client *client);

/* TODO: Clearup I2C_DRIVERID_EXP0 issue on the v4l list. */

struct i2c_driver vpx_driver =
{  
      name:              "Micronas vpx 3224/5d i2c driver",
      id:                I2C_DRIVERID_EXP0,
      flags:             I2C_DF_NOTIFY,
      attach_adapter:    &vpx_attach_adapter,
      detach_client:     &vpx_detach_client,
      command:           &vpx_command,   
      inc_use:           &vpx_inc_use,   
      dec_use:           &vpx_dec_use    
};




/* Scan ports 0x43 - 0x47 (7-bit addresses) for the vpx */

static unsigned short normal_i2c[] = { I2C_CLIENT_END };

static unsigned short normal_i2c_range[] = { 0x43, 0x47, I2C_CLIENT_END };

/* Magic definition of all other variables and things */
I2C_CLIENT_INSMOD;


/* byte banging routines. i2c-algo-bit.c (lines 363 - 364 assumes data
   to be little endian. Am I right or have I read the code wrong ?
*/


s32 vpx_read_byte(struct i2c_client *client, u8 reg)
{
        /* byte-sized register */
      return i2c_smbus_read_byte_data(client,reg);
}

s32 vpx_read_word(struct i2c_client *client, u8 reg)
{
      /* word-sized register */
      
#ifdef __LITTLE_ENDIAN
      /* The vpx takes MSB first. OK. topsy turvy! */
      
      return swab16(i2c_smbus_read_word_data(client,reg));

#endif

      return i2c_smbus_read_word_data(client,reg);
}

s32 vpx_write_byte(struct i2c_client *client, u8 reg, u16 value)
{
      
      /* byte-sized register */
        return i2c_smbus_write_byte_data(client,reg,value);
        
}

s32 vpx_write_word(struct i2c_client *client, u8 reg, u16 value)
{
      /* word-sized register */
      /* Do the byte swap */

#ifdef __LITTLE_ENDIAN

      value=swab16(value);

#endif
      return i2c_smbus_write_word_data(client,reg,value);
}


s32 vpx_fp_write(struct i2c_client *client, u16 fp_reg, u16 value)
{
      int status;
      vpx_poll_fp_busy(client);
      if((status=vpx_write_word(client, FPWR, fp_reg))) return status;
      vpx_poll_fp_busy(client);
      return vpx_write_word(client, FPDAT, (value & 0x3fff)); 
}

s32 vpx_fp_read(struct i2c_client *client, u16 fp_reg)
{
        s32 status;
        vpx_poll_fp_busy(client);
        if((status=vpx_write_word(client, FPRD, fp_reg))) return status;
        vpx_poll_fp_busy(client);
         return (0x3fff & vpx_read_word(client, FPDAT)); 
}
 
void vpx_poll_fp_busy(struct i2c_client *client)
{

      unsigned long status;
      status = (unsigned long) vpx_read_byte(client, FPSTA);
      while(test_bit(2, &status))
            {
                  status = (unsigned long) vpx_read_byte(client, FPSTA);
                  
                  /* This is a half hearted attempt to sleep 
                   * for 20 milliseconds. The vpx manual says that if 
                   * the onboard FP (FP stands for 
                   * "Fast Processor" :-O ) is busy, read/write
                   * to it needs to be retried after 20ms.
                   * The priority here is for other processes 
                   * to run. Not for us to encourage 
                   * couch potatoes... ;)
                   */
                  
                  set_current_state(TASK_UNINTERRUPTIBLE);
                  schedule_timeout(VPX_BUSY_TIMEOUT);
            }
      
}     

                        
/* vpx functional layer */

/* initialize the video processor */

void vpx_pinit(struct i2c_client *client, int model)
{
      int portword;
      /* I've gone to the trouble of customizing vpx parameters here
       * with extensibility in mind. For each card, there should be a 
       * special set of settings for the vpx.
       */ 

      switch(model)
            {
            case PVCLPP_COMBO:
                  /* The pvclpp combo card uses a philips TEA5582
                   * FM demodulator to demodulate sound.
                   * The TEA5582 has a mute control, which is connected
                   * via VPort B, bit 1. Therefore, we have to guess that
                   * the video capture takes place via the 8bits of
                   * VPort A. This is set via FPreg, 0x154. 
                   */

                  portword = vpx_fp_read(client, 0x154) | 0x302;
                  tprintk("Writing %04x to FPreg 0x154 \n.",
                        portword);
                  
                  vpx_fp_write(client, 0x154, portword);
                  

                  /* Set VPort Driver strength. 
                   * Got this from the default setting of the pvclpp 
                   * application (tvtap) for win98. Booted into 
                   * windows, ran tvtap, killed it with softice,
                   * warm booted into linux through int 19, 
                   * and read the I2C 
                   * registers. 
                   */
                  
                  vpx_write_byte(client, 0xf9, 0x24);
                  vpx_write_byte(client, 0xf8, 0x24);
                  
                  /* VREF pulse width = 6. 
                   * Guess how I found out ?? ;-)
                   */
                  
                  vpx_fp_write(client, 0x153, 0x20);

                  /* Set the input source. There are 3 input 
                   * sources, VIN1, VIN2, VIN3.
                   * FPReg(0x21) [1:0] determines 
                   * hardcoding to television tuner input 
                   * for just now. */

                  portword = vpx_fp_read(client, 0x21) & 0xfc;

                  vpx_fp_write(client, 0x21, portword | 0x01);

            }

      /* Disable Winload table #2. We use Winload table #1 for both fields.*/

      vpx_fp_write(client, 0x12B, 0xc00);
      
      /* Latch current Values */
      portword = vpx_fp_read(client, 0x140);
      portword |= 0x40;
      vpx_fp_write(client, 0x140, portword);


      /* Switch off slicer. */
      vpx_write_byte(client, 0xaa, 0x40);
      
      tprintk("I2C reg. 0xAA reads %02x. \n", 
            vpx_read_byte(client, 0xaa));
      

      
      /* Set to PAL - B, G, H, I (50Hz) for testing on my VCR.
       * Here is the vpx manual listing for setting various standards. 
       *
       * FPReg(0x20) [2:0]         Standard    Vert.       IF. (MHz)
       *             0          PAL B,G,H,I    50Hz         4.433618
       *             1               NTSC M    60Hz         3.579545
       *             2                SECAM    50Hz         4.286
       *             3               NTSC44    60Hz         4.433618
       *             4                PAL M    60Hz         3.575611
       *             5                PAL N    50Hz         3.582056
       *             6               PAL 60    60Hz         4.433618
       *             7            NTSC COMB    60Hz         3.579545
       */


      portword = vpx_fp_read(client, 0x20) & 0xff8; 

      vpx_fp_write(client, 0x20, portword | 0x00); 
      
      /* There's some confusion in the ITU 601 format. Cr and Cb get
       * Swapped. Let's re-swap them.
       */

      portword = vpx_fp_read(client, 0x126) & 0xeff;

      vpx_fp_write(client, 0x126, portword | 0x100);


}



void vpx_start_capture(struct i2c_client *client)
{
      int portword;
      

      /* Enable VPortA, VPortB, Pixclk, HREF, VREF, FIELD, VACT, LLC, LLC2 */
      
      portword = vpx_read_byte(client, 0xf2) & 0xf0;
      portword |= 0x0f;
      
      vpx_write_byte(client, 0xf2, portword);
}

void vpx_stop_capture(struct i2c_client *client)
{
      int portword;
      
      /* Disable VPortA, VPortB, Pixclk, 
         HREF, VREF, FIELD, VACT, LLC, LLC2 */
      
      portword = vpx_read_byte(client, 0xf2) & 0xf0;
      
      vpx_write_byte(client, 0xf2, portword);
}

void vpx_set_window(struct i2c_client *client, struct video_window *vwin)
{
      /* Winloadtab1 is loaded here. It is active for both fields.
       */

      int portword;
      
      switch(vmode)
            {
            case VIDEO_MODE_PAL: {
                                    
                  /* Vertical Begin Scanline */
                  vpx_fp_write(client, 0x120, 20);

                  /* Horizontal Begin, Pixel */
                  vpx_fp_write(client, 0x123, 5);

                  /* Maximum Lines input 310:  PAL is a 625 line s/m. */
                  vpx_fp_write(client, 0x121, 305);
                  
                  /* Scaled number of Vertical Lines. */
                  vwin->height = 
                        (vwin->height > 305 ? 
                         300 : vwin->height) & 0x1ff;

                  vwin->height =
                        vwin->height < 24 ?
                        24 : vwin->height;

                  /* Scaled Width of raster line. */
                  vwin->width = 
                        (vwin->width > 800 ? 
                         800 : vwin->width) & 0x7ff;

                  vwin->width = vwin->width < 32 ? 
                        32 : vwin->width;

                  break;
            }                 

            default:
                  return;
            }

      /* Update No. of Vlines to vpx */

      vpx_fp_write(client, 0x122, vwin->height);

      /* Width of raster, upto 800 pixels */

      vpx_fp_write(client, 0x125, vwin->width);
      vpx_fp_write(client, 0x124, vwin->width);


      /* Latch current Values */
      portword = vpx_fp_read(client, 0x140);
      portword |= 0x20;
      vpx_fp_write(client, 0x140, portword);


}


void vpx_set_picture(struct i2c_client *client, struct video_picture *vpict)
{
      
      int portword;
      
      tprintk("Brightness: %d, Contrast: %d, Colour: %d, Hue %d \n",
             vpict->brightness, vpict->contrast, vpict->colour,
             vpict->hue);

      /* set contrast FPReg 0x132 [5].[4:0] = contrast ratio */   

            portword = vpx_fp_read(client, 0x132) & 0xfd0;

      portword |= (vpict->contrast >> 10) & 0x3f;

      vpx_fp_write(client, 0x132, portword);

      tprintk("H/W contrast set to %d.%d \n", vpict->contrast >> 15,
            vpict->contrast >> 11 & 0x1f);

      /* Set Brightness */
      portword = vpx_fp_read(client, 0x131) & 0xf00;

      portword |= vpict->brightness >> 8;

      vpx_fp_write(client, 0x131, portword);
      
      


}

int vpx_detect_client(struct i2c_adapter *adapter, int address, 
                  unsigned short flags, int kind)
{
      int err = 0;

      struct i2c_client *new_client;

      dprintk("in vpx_detect_client() \n");
      
      if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_EMUL)){
            return 0;
      }
            
       
      if(! (new_client = 
                kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) {
                  
            return -ENOMEM;
      }

      new_client->addr = address;
      new_client->adapter = adapter;
      new_client->driver = &vpx_driver;
      new_client->flags = 0;
      
      /* Now, we do the remaining detection. No `force' parameter is used. */
                  
      if (vpx_read_byte(new_client, VPX_REG_JEDEC) != VPX_JEDEC_ID)
            {
                  goto BAILOUT;
            }
      if (vpx_read_byte(new_client, VPX_REG_PARTNUM1) != VPX_PARTNUM1_ID)
            {
                  goto BAILOUT;
            }
      
      if ((vpx_read_byte(new_client, VPX_REG_PARTNUM0) == VPX_3224D_ID)){
            
            printk( KERN_INFO 
                  "vpx: Found: vpx 3224d chip @ 0x%02x \n", 
                  (new_client->addr << 1) );
      
            if((err=vpx_init_client(new_client))) goto BAILOUT;
            return 0;
      }
      
      if (vpx_read_byte(new_client, 
                    VPX_REG_PARTNUM0) == VPX_3225D_ID){
            printk( KERN_INFO
                  "vpx: Found: vpx 3225d chip @ 0x%02x \n", 
                  (new_client->addr << 1));

            if((err=vpx_init_client(new_client))) goto BAILOUT;
            return 0;
      }
      else{
            printk( KERN_WARNING
                  "vpx:  Can't find supported vpx chip 
                               on this adapter. Sorry.\n");       


            goto BAILOUT;
      }
      

 BAILOUT:   
      kfree(new_client);
      return err; 
      
}



int vpx_init_client(struct i2c_client *client)
{
      
      int err=0;


      /* This driver is non re-entrant; ie; can only support one chip 
         at a time.
      */

      if(re_entry) 
            {
                  printk(KERN_INFO
                         "Sorry, only one client allowed per adapter. \n");
                  return -EBUSY;
            }
      
      re_entry = 1;

      /* Fill in the remaining client fields. */
      strcpy(client->name, client_name);
      
      
      /* Tell the i2c layer a new client has arrived */
      
      if ((err = i2c_attach_client(client)))
            {
                  return err; 
            }

      /* Finally link this one client with our global client pointer */
      vpx_client = client;

      return 0;
}

      

int vpx_detach_client(struct i2c_client *client)
{
      int err;
      
      
      /* Try to detach the client from i2c space */
      if ((err = i2c_detach_client(client))) {
            printk("vpx322xd: Client deregistration failed, client not detached.\n");

            return err;
      }

      re_entry=0;

      MOD_DEC_USE_COUNT;

      /* Frees client data too, if allocated at the same time */
      kfree(client); 
      return 0;
}

int vpx_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
      /* Please note that it is the caller's responsibility to 
       * maintain state information about the video processor. 
       * The only VPROC_GET command is
       * used to query the video standard. ie; PAL, NTSC and such.
       */

      switch(cmd)
            {
            case VPROC_INIT:
                  {
                        int model = * ((int *) arg);

                        vpx_pinit(client, model);
                        dprintk("VPROC_INIT called. \n");
                        break;
                  }

            case VPROC_START_CAPTURE:
                  
                  vpx_start_capture(client);

                  dprintk("VPROC_START_CAPTURE called. \n");
                  break;
                  
                  
            case VPROC_STOP_CAPTURE:
                  
                  vpx_stop_capture(client);
                  
                  dprintk("VPROC_STOP_CAPTURE called. \n");
                  break;
                  
                  
            case VPROC_SET_CAP_MODE:
                  
                  /* SET to PAL/NTSC/SECAM via struct */
                  vmode = *((int *)(arg));
                  dprintk("VPROC_SET_CAP_MODE called. \n"); 
                  break;
                  

            case VPROC_GET_CAP_MODE:
                  memcpy(arg, &vmode, sizeof(int));
                  break;
                  
            case VPROC_SET_WINDOW:
                  {
                        struct video_window * vwin = arg;
                        vpx_set_window(client, vwin);
                        dprintk("VPROC_SET_WINDOW called. \n");
                        break;
                  }
                  
            case VPROC_SET_PICTURE:
                  {
                        struct video_picture * vpict = arg;
                        vpx_set_picture(client, vpict);
                        dprintk("VPROC_SET_PICTURE called. \n");
                        break;
                  }
            }
      
      
      return 0;
}

int vpx_attach_adapter(struct i2c_adapter *adapter)
{
      int retval;
      switch ((adapter->id & I2C_ALGO_BIT)) {
      case I2C_ALGO_BIT:

            printk(KERN_INFO "vpx: probing i2c adapter %s ... \n",
                   adapter->name);
            retval = i2c_probe(adapter,&addr_data,&vpx_detect_client);

            break;

      default:

            printk("vpx: skipping adapter %s, 
                            adapter type not supported.\n",
                   adapter->name);
            retval = 0;
      }

      /* Had to put this here because of messy code in vpx_detect_client()
       * TODO: Cleanup the chip detect code in vpx_detect_client().
       */

      if(!retval) 
            MOD_INC_USE_COUNT;

      return retval;

}

void vpx_inc_use(struct i2c_client *client)
{
      MOD_INC_USE_COUNT;
}

void vpx_dec_use(struct i2c_client *client)
{
      MOD_DEC_USE_COUNT;
}




int __init vpx_init(void)
{
    int res;
    printk("vpx version %s (%s)\n", VPX_VERSION, VPX_DATE);
    
    if ((res = i2c_add_driver(&vpx_driver))) {
          printk("vpx: Driver registration failed, module not inserted.\n");
          return res;
    }

    return 0;
}

int __init vpx_cleanup(void)
{
      int res;
      if ((res = i2c_del_driver(&vpx_driver))) {
            printk("vpx: Driver de-registration failed, module not removed.\n");
            return res;
      }
      

      return 0;
}


EXPORT_NO_SYMBOLS;
MODULE_PARM(debug, "i");


MODULE_PARM_DESC(debug,
         "debug level - 0 off; 1 normal; 2 verbose; ");

#ifdef MODULE
MODULE_AUTHOR("Cherry George Mathew <cherry@freeshell.org>");
MODULE_DESCRIPTION("i2c driver for the micronas vpx322xd 
                    video processor family");
MODULE_LICENSE("GPL");


int init_module(void)
{
      return vpx_init();
}


int cleanup_module(void)
{
      return vpx_cleanup();
}

#endif                        /* MODULE */




/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-basic-offset: 8
 * End:
 */





Generated by  Doxygen 1.6.0   Back to index