[Armadeus-commitlog] SF.net SVN: armadeus:[1122] trunk/buildroot/target/device/armadeus/linux/ ker
Brought to you by:
sszy
|
From: <th...@us...> - 2009-03-07 18:13:48
|
Revision: 1122
http://armadeus.svn.sourceforge.net/armadeus/?rev=1122&view=rev
Author: thom25
Date: 2009-03-07 18:13:30 +0000 (Sat, 07 Mar 2009)
Log Message:
-----------
[LINUX] add partialy working ad9889 driver
Added Paths:
-----------
trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.27.13/314-apf27-armadeus-ad9889.patch
Added: trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.27.13/314-apf27-armadeus-ad9889.patch
===================================================================
--- trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.27.13/314-apf27-armadeus-ad9889.patch (rev 0)
+++ trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.27.13/314-apf27-armadeus-ad9889.patch 2009-03-07 18:13:30 UTC (rev 1122)
@@ -0,0 +1,450 @@
+--- linux-2.6.27.13.orig/drivers/media/video/ad9889.c 2008-10-10 00:13:53.000000000 +0200
++++ linux-2.6.27.13/drivers/media/video/ad9889.c 2009-03-01 08:29:01.000000000 +0100
+@@ -0,0 +1,411 @@
++/*
++ * ad9889 - Analog Devices AD9889 video encoder driver
++ *
++ * Copyright (C) 2009 Armadeus Systems <nic...@ar...>
++ *
++ *
++ * 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/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-common.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
++#include <media/ad9889.h>
++#include <linux/interrupt.h>
++//#include <linux/fb.h>
++#include <linux/delay.h>
++
++#define HPD_INT 0x80
++#define MSEN_INT 0x40
++#define VS_INT 0x20
++#define AUDIO_FIFO_FULL_INT 0x10
++#define ITU656_ERR_INT 0x08
++#define EDID_RDY_INT 0x04
++
++static int video_in_format;
++static int debug;
++
++MODULE_DESCRIPTION("Analog Devices AD9889 video encoder driver");
++MODULE_AUTHOR("Nicolas Colombain");
++MODULE_LICENSE("GPL");
++module_param(video_in_format, int, 0644);
++module_param(debug, int, 0644);
++MODULE_PARM_DESC(debug, "debug");
++MODULE_PARM_DESC(video_in_format, "video in format");
++
++
++struct ad9889_dev {
++ struct mutex lock_sync;
++ unsigned char edid_ready;
++ int edid_block_map_offset;
++ int next_segment;
++
++ struct i2c_client *client;
++ int force_hpd;
++ struct work_struct work;
++ struct i2c_client *edid_ram;
++};
++
++static int ad9889_read(struct i2c_client *client, u8 reg)
++{
++ return i2c_smbus_read_byte_data(client, reg);
++}
++
++static int ad9889_write(struct i2c_client *client, u8 reg, u8 val)
++{
++ int i;
++
++ for (i = 0; i < 3; i++) {
++ if (i2c_smbus_write_byte_data(client, reg, val) == 0)
++ return 0;
++ }
++ printk(KERN_ERR "I2C Write Problem\n");
++ return -1;
++}
++
++static void ad9889_av_mute_on(struct i2c_client *client)
++{
++ /* set chxPwrDwnI2C */
++ ad9889_write(client, 0xa1, 0x38);
++ /* set avmute*/
++ ad9889_write(client, 0x45, 0x40);
++}
++
++static void ad9889_av_mute_off(struct i2c_client *client)
++{
++ /* clear chxPwrDwnI2C */
++ ad9889_write(client, 0xa1, 0x0);
++ /* clear avmute*/
++ ad9889_write(client, 0x45, 0x80);
++}
++
++
++static void ad9889_init(struct i2c_client *client)
++{
++ ad9889_av_mute_on(client);
++ /* enable I2S0 and I2S standard format*/
++ ad9889_write(client, 0x0c, 0x04);
++ /* power up */
++ ad9889_write(client, 0x41, 0x10);
++
++ /* set static reserved registers*/
++ ad9889_write(client,0x0a, 0);
++ ad9889_write(client, 0x98, 0x03);
++ ad9889_write(client, 0x9C, 0x38);
++ /* set low speed pixel clock */
++ ad9889_write(client, 0xA2, 0x84);
++ ad9889_write(client, 0xA3, 0x84);
++
++ ad9889_write(client, 0xBB, 0xff);
++
++ /* set capture edge */
++ ad9889_write(client, 0xba, 0x60);
++ ad9889_write(client, 0x47, 0x80);
++}
++
++static void setup_audio_video(struct i2c_client *client)
++{
++ unsigned char rgb_rgb[24] = {8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0};
++
++ /* dvi*/
++ ad9889_write(client, 0xaf, 0x00);
++ /* write color map */
++ i2c_smbus_write_block_data(client, 0x18,24,rgb_rgb);
++ /* set CSC mode 1*/
++ ad9889_write(client, 0x17, 0x08);
++ /* enable CSC */
++ ad9889_write(client, 0x3b, 0x01);
++ /* check input mode */
++ if( (ad9889_read(client, 0x3E)>>2) == 1)
++ printk("640x480 input mode detected\n");
++ else
++ printk(KERN_ERR "input mode not supported, must be 640x480\n");
++}
++
++/* Returns the number of read bytes */
++int EDID_ram_read_data(struct i2c_client *client, u8 *values)
++{
++ union i2c_smbus_data data;
++ struct ad9889_config* config = client->dev.platform_data;
++ struct ad9889_dev *ad9889 = i2c_get_clientdata(client);
++ int status, i;
++
++ /* EDID ram base address */
++ ad9889_write(client, 0x43, config->EDID_I2C_addr<<1);
++
++ for( i=0; i<256/I2C_SMBUS_BLOCK_MAX; i++){
++ data.block[0] = I2C_SMBUS_BLOCK_MAX;
++ status = i2c_smbus_xfer(ad9889->edid_ram->adapter, config->EDID_I2C_addr,
++ ad9889->edid_ram->flags,
++ I2C_SMBUS_READ, i*32,
++ I2C_SMBUS_I2C_BLOCK_DATA, &data);
++ if (status < 0)
++ return status;
++ memcpy(&(values[i*32]), &data.block[1], data.block[0]);
++ }
++ return 0;
++}
++
++int handle_edid_interrupt(struct i2c_client *client)
++{
++ unsigned char edid_dat[256];
++ struct i2c_client temp_ram_client;
++
++ if( EDID_ram_read_data(client, edid_dat) < 0 )
++ printk(KERN_ERR "error when reading EDID\n");
++ else{
++ int i;
++ int xwidth;
++ int aspect;
++ int freq;
++
++ //printk("ESTABLISHED TIMING\n");
++ //edid_dat[35];
++ //edid_dat[36];
++
++ printk("Standard monitor timings\n");
++ for(i=38; i<53; i+=2 ) {
++ xwidth = edid_dat[i]*8 + 248;
++ aspect = (edid_dat[i+1] & 0xC0) >> 6;
++ freq = (edid_dat[i+1] &0x3f) +60;
++ printk("xwidth:%d, aspect:%s, freq:%d\n", xwidth, aspect?"4:3":"16/9", freq);
++ }
++ }
++ return 0;
++}
++
++static void handle_hpd_interrupt(struct i2c_client *client)
++{
++ if(ad9889_read(client, 0x42)&0x40) {
++ /* wait until the cable is really inserted */
++ mdelay(500);
++ /* set chxPwrDwnI2C down */
++ ad9889_write(client, 0xa1, 0x38);
++ /* power up chip */
++ ad9889_write(client, 0x41, 0x10);
++ ad9889_write(client, 0x94, 0x84);
++ ad9889_write(client, 0x95, 0xC3);
++ }
++}
++
++static void ad9889_work(struct work_struct *work)
++{
++ struct ad9889_dev *ad9889 = container_of(work, struct ad9889_dev, work);
++ struct i2c_client *client = ad9889->client;
++ int error;
++
++ unsigned char interrupt_registers1;
++ unsigned char interrupt_registers2;
++ unsigned char interrupt_code = 0;
++ unsigned char interrupt_remain1 = 0xff;
++ unsigned char interrupt_remain2 = 0xff;
++
++ mutex_lock(&ad9889->lock_sync);
++
++ while((interrupt_remain1&0xC4)|(interrupt_remain2&0xc0)){
++ interrupt_registers1 = ad9889_read(client, 0x96);
++ interrupt_registers2 = ad9889_read(client, 0x97);
++ printk("interrupt reg1 %x, interrupt reg2 %x\n",
++ interrupt_registers1, interrupt_registers2);
++
++ /* check HPD then call hpd handler */
++ if((interrupt_registers1 & HPD_INT) || ad9889->force_hpd ) {
++ handle_hpd_interrupt(client);
++ ad9889->edid_ready = 0;
++ ad9889->edid_block_map_offset = 0;
++ if( ad9889->force_hpd ){
++ ad9889->force_hpd = 0;
++ ad9889_write(client,0x96, HPD_INT);
++ interrupt_registers1 |= HPD_INT;
++ }
++ interrupt_code |= HPD_INT;
++ }
++
++ /* check for EDID ready flag, then call EDID Handler */
++ if((interrupt_registers1 & EDID_RDY_INT)) {
++ if(!ad9889->edid_ready) {
++ ad9889->next_segment = handle_edid_interrupt(client);
++ interrupt_code |= EDID_RDY_INT;
++ }
++ }
++
++ /* re-enable the interrupt before requesting additional EDID */
++ ad9889_write(client,0x96, interrupt_registers1);
++ ad9889_write(client, 0x97, interrupt_registers2);
++
++ /* if final EDID interrupt was processed tell system that EDID is ready and
++ initiate audio video setup */
++
++ if(interrupt_code&EDID_RDY_INT) {
++ if(ad9889->next_segment==0) {
++ ad9889_av_mute_on(client);
++ ad9889_init(client);
++ ad9889->edid_ready = 1;
++ setup_audio_video(client);
++ ad9889_av_mute_off(client);
++ }
++ else
++ ad9889_write(client,0xc4, ad9889->edid_block_map_offset+ad9889->next_segment);
++
++ if(ad9889->next_segment==0x40)
++ ad9889->edid_block_map_offset=0x40;
++ }
++ /*Check if additional interrupts occured during processing*/
++ interrupt_remain1 = ad9889_read(client, 0x96);
++ interrupt_remain2 = ad9889_read(client, 0x97);
++ }
++ mutex_unlock(&ad9889->lock_sync);
++}
++
++/* ad9889 has new data for us available. */
++static irqreturn_t ad9889_handler(int irq, void *dev_id)
++{
++ struct ad9889_dev *dev = (struct ad9889_dev *) dev_id;
++ schedule_work(&dev->work);
++ return IRQ_HANDLED;
++}
++
++static int ad9889_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ struct ad9889_config *pdata = client->dev.platform_data;
++ struct ad9889_dev *ad9889;
++ int err = -EINVAL;
++
++ /* Check if the adapter supports the needed features */
++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
++ return -EIO;
++
++ if (!pdata) {
++ printk(KERN_ERR "AD9889: Platform data not supplied\n");
++ return -ENOENT;
++ }
++
++ if (!client->irq) {
++ printk(KERN_ERR "AD9889: Invalid irq value\n");
++ return -ENOENT;
++ }
++
++ ad9889 = kzalloc(sizeof(struct ad9889_dev), GFP_KERNEL);
++
++ if (ad9889 == NULL)
++ return -ENOMEM;
++
++ ad9889->client = client;
++ i2c_set_clientdata(client, ad9889);
++
++ INIT_WORK(&ad9889->work, ad9889_work);
++ mutex_init(&ad9889->lock_sync);
++ mutex_lock(&ad9889->lock_sync);
++
++
++ /* allocate GPIO / IRQ if necessary */
++ if (pdata->init)
++ pdata->init();
++
++ if (request_irq(client->irq, ad9889_handler, IRQF_DISABLED | IRQF_SHARED
++ | IRQF_TRIGGER_FALLING, "ad9889", ad9889)) {
++ printk(KERN_ERR "Could not allocate touchscreen IRQ (n %d)\n", client->irq);
++ err = -EINVAL;
++ goto err_irq;
++ }
++
++ printk("detecting ad9889 client on address 0x%x\n", client->addr << 1);
++
++ /* check whether the AD9889 is present */
++ if( ad9889_read(client, 0xA5) != 0x04 ){
++ printk("ad9889 not found\n");
++ err = -ENODEV;
++ goto err_presence;
++ }
++
++ ad9889->edid_ram = i2c_new_dummy(client->adapter, pdata->EDID_I2C_addr);
++ if( !ad9889->edid_ram ){
++ printk("error: can't add i2c device at 0x%x\n", pdata->EDID_I2C_addr);
++ goto err_presence;
++ }
++
++ printk("ad9889 detected!\n");
++ /* reset */
++ ad9889_write(client, 0x41, 0X20);
++ mdelay(10);
++
++ mutex_unlock(&ad9889->lock_sync);
++
++ /* if a monitor is already connected, then simulate interrupt */
++ err = ad9889_read(client, 0x42);
++ if( err & 0x40){
++ ad9889->force_hpd = 1;
++ schedule_work(&ad9889->work);
++ }
++
++ return 0;
++
++err_presence:
++ free_irq(client->irq, ad9889);
++
++err_irq:
++ flush_scheduled_work();
++ kfree(i2c_get_clientdata(client));
++ if (pdata->exit)
++ pdata->exit();
++
++ return 0;
++}
++
++/* ----------------------------------------------------------------------- */
++
++static int ad9889_remove(struct i2c_client *client)
++{
++ struct ad9889_config *pdata = client->dev.platform_data;
++ struct ad9889_dev *ad9889 = i2c_get_clientdata(client);
++
++ free_irq(client->irq, ad9889);
++
++ flush_scheduled_work();
++
++ if (pdata->exit)
++ pdata->exit();
++
++ i2c_unregister_device(ad9889->edid_ram);
++
++ kfree(i2c_get_clientdata(client));
++ kfree(ad9889);
++
++ mutex_unlock(&ad9889->lock_sync);
++ return 0;
++}
++
++/* ----------------------------------------------------------------------- */
++
++static struct i2c_device_id ad9889_id[] = {
++ { "ad9889", 0 }, /* auto-detection */
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, ad9889_id);
++
++static struct v4l2_i2c_driver_data v4l2_i2c_data = {
++ .name = "ad9889",
++ //.command = ad9889_command,
++ .probe = ad9889_probe,
++ .remove = ad9889_remove,
++ .id_table = ad9889_id,
++};
+--- linux-2.6.27.13.orig/include/media/ad9889.h 2008-10-10 00:13:53.000000000 +0200
++++ linux-2.6.27.13/include/media/ad9889.h 2009-03-01 08:29:01.000000000 +0100
+@@ -0,0 +1,33 @@
++/*
++ * AD9889 driver
++ *
++ * Copyright (c) 2009 Armadeus Systems
++ *
++ * This package 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 package 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 package; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#ifndef __LINUX_VIDE0_AD9889_H
++#define __LINUX_VIDE0_AD9889_H
++
++struct ad9889_config {
++ int (*init)(void);
++ int (*exit)(void);
++ unsigned char EDID_I2C_addr; /*base address of the EDID ram*/
++ unsigned char high_speed_clock; /* set to 1 if pixel clock >80MHz */
++ unsigned char edge_capture; /* set to 1 for positive pixel clock capture*/
++};
++
++
++#endif /* __LINUX_VIDE0_AD9889_H */
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|