1/* 2 * Driver for Philips SAB3036 "CITAC" tuner control chip. 3 * 4 * Author: Phil Blundell <philb@gnu.org> 5 * 6 * The SAB3036 is just about different enough from the chips that 7 * tuner.c copes with to make it not worth the effort to crowbar 8 * the support into that file. So instead we have a separate driver. 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public License 12 * as published by the Free Software Foundation; either version 13 * 2 of the License, or (at your option) any later version. 14 */ 15 16#include <linux/module.h> 17#include <linux/kernel.h> 18#include <linux/sched.h> 19#include <linux/string.h> 20#include <linux/timer.h> 21#include <linux/delay.h> 22#include <linux/errno.h> 23#include <linux/slab.h> 24#include <linux/init.h> 25 26#include <linux/i2c.h> 27#include <linux/videodev.h> 28#include <media/v4l2-common.h> 29 30#include <media/tuner.h> 31 32static int debug; /* insmod parameter */ 33static int this_adap; 34 35static struct i2c_client client_template; 36 37/* Addresses to scan */ 38static unsigned short normal_i2c[] = { 0x60, 0x61, I2C_CLIENT_END }; 39static unsigned short ignore = I2C_CLIENT_END; 40 41static struct i2c_client_address_data addr_data = { 42 .normal_i2c = normal_i2c, 43 .probe = &ignore, 44 .ignore = &ignore, 45}; 46 47/* ---------------------------------------------------------------------- */ 48 49static unsigned char 50tuner_getstatus (struct i2c_client *c) 51{ 52 unsigned char byte; 53 if (i2c_master_recv(c, &byte, 1) != 1) 54 printk(KERN_ERR "tuner-3036: I/O error.\n"); 55 return byte; 56} 57 58#define TUNER_FL 0x80 59 60static int 61tuner_islocked (struct i2c_client *c) 62{ 63 return (tuner_getstatus(c) & TUNER_FL); 64} 65 66/* ---------------------------------------------------------------------- */ 67 68static void 69set_tv_freq(struct i2c_client *c, int freq) 70{ 71 u16 div = ((freq * 20) / 16); 72 unsigned long give_up = jiffies + HZ; 73 unsigned char buffer[2]; 74 75 if (debug) 76 printk(KERN_DEBUG "tuner: setting frequency %dMHz, divisor %x\n", freq / 16, div); 77 78 /* Select high tuning current */ 79 buffer[0] = 0x29; 80 buffer[1] = 0x3e; 81 82 if (i2c_master_send(c, buffer, 2) != 2) 83 printk("tuner: i2c i/o error 1\n"); 84 85 buffer[0] = 0x80 | ((div>>8) & 0x7f); 86 buffer[1] = div & 0xff; 87 88 if (i2c_master_send(c, buffer, 2) != 2) 89 printk("tuner: i2c i/o error 2\n"); 90 91 while (!tuner_islocked(c) && time_before(jiffies, give_up)) 92 schedule(); 93 94 if (!tuner_islocked(c)) 95 printk(KERN_WARNING "tuner: failed to achieve PLL lock\n"); 96 97 /* Select low tuning current and engage AFC */ 98 buffer[0] = 0x29; 99 buffer[1] = 0xb2; 100 101 if (i2c_master_send(c, buffer, 2) != 2) 102 printk("tuner: i2c i/o error 3\n"); 103 104 if (debug) 105 printk(KERN_DEBUG "tuner: status %02x\n", tuner_getstatus(c)); 106} 107 108/* ---------------------------------------------------------------------- */ 109 110static int 111tuner_attach(struct i2c_adapter *adap, int addr, int kind) 112{ 113 static unsigned char buffer[] = { 0x29, 0x32, 0x2a, 0, 0x2b, 0 }; 114 115 struct i2c_client *client; 116 117 if (this_adap > 0) 118 return -1; 119 this_adap++; 120 121 client_template.adapter = adap; 122 client_template.addr = addr; 123 124 client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); 125 if (client == NULL) 126 return -ENOMEM; 127 memcpy(client, &client_template, sizeof(struct i2c_client)); 128 129 printk("tuner: SAB3036 found, status %02x\n", tuner_getstatus(client)); 130 131 i2c_attach_client(client); 132 133 if (i2c_master_send(client, buffer, 2) != 2) 134 printk("tuner: i2c i/o error 1\n"); 135 if (i2c_master_send(client, buffer+2, 2) != 2) 136 printk("tuner: i2c i/o error 2\n"); 137 if (i2c_master_send(client, buffer+4, 2) != 2) 138 printk("tuner: i2c i/o error 3\n"); 139 return 0; 140} 141 142static int 143tuner_detach(struct i2c_client *c) 144{ 145 return 0; 146} 147 148static int 149tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) 150{ 151 int *iarg = (int*)arg; 152 153 switch (cmd) 154 { 155 case VIDIOCSFREQ: 156 set_tv_freq(client, *iarg); 157 break; 158 159 default: 160 return -EINVAL; 161 } 162 return 0; 163} 164 165static int 166tuner_probe(struct i2c_adapter *adap) 167{ 168 this_adap = 0; 169 if (adap->id == I2C_HW_B_LP) 170 return i2c_probe(adap, &addr_data, tuner_attach); 171 return 0; 172} 173 174/* ----------------------------------------------------------------------- */ 175 176static struct i2c_driver 177i2c_driver_tuner = 178{ 179 .driver = { 180 .name = "sab3036", 181 }, 182 .id = I2C_DRIVERID_SAB3036, 183 .attach_adapter = tuner_probe, 184 .detach_client = tuner_detach, 185 .command = tuner_command 186}; 187 188static struct i2c_client client_template = 189{ 190 .driver = &i2c_driver_tuner, 191 .name = "SAB3036", 192}; 193 194static int __init 195tuner3036_init(void) 196{ 197 return i2c_add_driver(&i2c_driver_tuner); 198} 199 200static void __exit 201tuner3036_exit(void) 202{ 203 i2c_del_driver(&i2c_driver_tuner); 204} 205 206MODULE_DESCRIPTION("SAB3036 tuner driver"); 207MODULE_AUTHOR("Philip Blundell <philb@gnu.org>"); 208MODULE_LICENSE("GPL"); 209 210module_param(debug, int, 0); 211MODULE_PARM_DESC(debug,"Enable debugging output"); 212 213module_init(tuner3036_init); 214module_exit(tuner3036_exit); 215