您的位置:首页 > 其它

如何在mtk andorid6.0上添加一个I2C驱动(这里是添加一个FM 芯片驱动)

2017-09-21 11:20 561 查看
平台:mt6737 android 6.0

#include <linux/init.h>

#include <linux/module.h>

//#include <stdlib.h>

#include <linux/i2c.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/device.h>

#include <linux/ioctl.h>

#include <linux/uaccess.h>

#include <linux/delay.h>

#include <linux/string.h>

#include <linux/wait.h>

#include <linux/platform_device.h>

#include <linux/gpio.h>

#include <linux/pinctrl/consumer.h>

#include <linux/of_gpio.h>

#include <linux/delay.h>

static void fm_set_gpio_output(unsigned int GPIO, unsigned int output);

static unsigned int GPIO_FM_PWR_EN;

static unsigned int GPIO_SPK_PWR_EN;

#define HS6760_I2C_ID0x18

//#define Clock_12M // Clock_7v6M//Clock_12M//Clock_24M//Clock_32v768K//选择时钟频率 

static struct i2c_client *HS6760_client = NULL;

static struct class *cls = NULL;

static unsigned int major;

static char *name = "fm_hs6760";

typedef enum 

{
hs6760_normal = 0,
hs6760_mute = 1,
hs6760_sleep = 2,
MODE_NULL

}MODE;

typedef enum

{
_75K = 0,
_50K = 1,
_22v5K = 2,
DEV_NULL

}DEV;

typedef enum

{
disable = 0,
enable = 1,
STATE_NULL

}STATE;

void HS6760_Initial(void);

void HS6760_SetFreq(uint16_t curFreq);

void HS6760_SetPow(uint8_t power);

void HS6760_Reset(void);

void HS6760_Fre_dev(DEV deviation);

void HS6760_Sel_mode(MODE mode);

void HS6760_Stereo(STATE flag);

static int Delayms(u32 data)

{
printk("delay %dms\n", data);
mdelay(data);//msleep(data);
return 0;

}

void ext_spkamp_enable(void)

{
printk(" fm  --- ext_spkamp_enable!\n");
fm_set_gpio_output(GPIO_SPK_PWR_EN , 1);

}

void ext_spkamp_disable(void)

{
printk(" fm  --- ext_spkamp_disable!\n");
fm_set_gpio_output(GPIO_SPK_PWR_EN , 0);

}

void HS6760_FM3V3_enable(void) //打开HS6760

{
printk(" fm  --- HS6760_FM3V3_enable!\n");
fm_set_gpio_output(GPIO_FM_PWR_EN, 1);

}

void HS6760_FM3V3_disable(void) //关闭HS6760

{
printk(" fm  --- HS6760_FM3V3_disable!\n");
fm_set_gpio_output(GPIO_FM_PWR_EN, 0);

}

static int HS6760_i2c_read(u8 reg)

{
unsigned char val[1] = {0};
int ret = 0;
val[0] = reg;
ret = i2c_master_send(HS6760_client, val, 1);
if (ret < 0) 
{
printk(" fm  --- HS6760_i2c_read I2C i/o error ret = %d\n", ret);
return ret;
}
mdelay(10);
ret = i2c_master_recv(HS6760_client, val, 1);
if (ret < 0) 
{
printk(" fm  --- HS6760_i2c_read I2C read error ret = %d\n", ret);
return ret;
}
return val[0];

}

static int HS6760_i2c_write(u8 reg, u8 writedata)

{
u8 databuf[2] = {0};
int ret = 0;
databuf[0] = reg;
databuf[1] = writedata;
ret = i2c_master_send(HS6760_client, databuf, 2);
printk(" fm  --- HS6760_i2c_write ret=%d, databuf[0]=%d, databuf[1]=%d\n", ret, databuf[0], databuf[1]);
if(ret < 0)
{
printk(" fm  --- HS6760_i2c_write send data failed !\n");
return -1;
}
return ret;

}

void HS6760_Initial(void)

{
uint8_t RegData;

#ifdef Clock_24M
RegData = 0x36;//PGA12dB,24M晶振
printk(" fm  ---HS6760 : HS6760_Initial Clock_24M !!\n");

#else

#ifdef Clock_12M
RegData = 0x34;//PGA12dB,12M晶振
printk(" fm  ---HS6760 : HS6760_Initial Clock_12M !!\n");

#else

#ifdef Clock_7v6M
RegData = 0x32;//PGA为12dB,7.6M晶振
printk(" fm  ---HS6760 : HS6760_Initial Clock_7v6M !!\n");

#else

#ifdef Clock_3
101ee
2v768K
RegData = 0x30;//PGA为12dB,32.768K晶振
printk(" fm  ---HS6760 : HS6760_Initial Clock_32v768K !!\n");

#endif

#endif

#endif

#endif

printk(" fm  ---HS6760 : HS6760_Initial !!\n");
HS6760_i2c_write(0x02,RegData);
Delayms(10);

RegData = 0x06;
HS6760_i2c_write(0x01,RegData);// normal模式
Delayms(10);

HS6760_Stereo(enable);//开启立体声
HS6760_SetPow(31);//设置功率最大

}

void HS6760_Reset(void)

{
uint8_t Data8;

printk(" fm  ---HS6760 : HS6760_Reset !!\n");

/*使PA发射功率生效*/
Data8 = HS6760_i2c_read(0x07);
Data8 &= 0x7f;
HS6760_i2c_write(0x07,Data8); 
Delayms(10);
Data8 |= 0x80;
HS6760_i2c_write(0x07,Data8); 

}

/**************************************************         

Function: HS6760_SetFreq()

Parameter: 
curFreq: FM frequency *10 ,such as  1017,933,1077..                                      

Description:
set FM to a frequency 80-1080M Hz

**************************************************/ 

char buffer [33]; //用于存放转换好的十六进制字符串,可根据需要定义长度

 

char * inttohex(int aa)

{

    static int i = 0;

    if (aa < 16)            //递归结束条件 

    {

        if (aa < 10)        //当前数转换成字符放入字符串 

            buffer[i] = aa + '0';

        else

            buffer[i] = aa - 10 + 'A';

        buffer[i+1] = '\0'; //字符串结束标志 

    }

    else

    {

        inttohex(aa / 16);  //递归调用 

        i++;                //字符串索引+1 

        aa %= 16;           //计算当前值

        if (aa < 10)        //当前数转换成字符放入字符串 

            buffer[i] = aa + '0';

        else

            buffer[i] = aa - 10 + 'A';

    }

    return (buffer);

}

void HS6760_SetFreq(uint16_t curFreq)

{
uint8_t cy1;
uint8_t cy0;
uint16_t curChan;

uint8_t Data8;
uint8_t TmpData8[2];

 printk("curFreq is %d add to HS6760_SetFreq\n",curFreq);
curChan = curFreq*2;

printk("curChan is %x add to HS6760_SetFreq\n",curChan);

TmpData8[0] = curChan>>8;//取高8位
TmpData8[1] = curChan&0xff;//取低8位

//TmpData8[0] = 0x06;//取高8位   6D8
//TmpData8[1] = 0xd8;//取低8位
printk("TmpData8[0] is %x add to HS6760_SetFreq\n",TmpData8[0]);
printk("TmpData8[1] is %x add to HS6760_SetFreq\n",TmpData8[1]);

HS6760_i2c_write(0x00,TmpData8[1]);
Delayms(10);

Data8 = HS6760_i2c_read(0x01);

// printk("0x01 is %x add to HS6760_SetFreq\n",Data8);
TmpData8[0] = TmpData8[0]|(Data8&0xc0);
HS6760_i2c_write(0x01,TmpData8[0]);
Delayms(10);

  cy0 = HS6760_i2c_read(0x00);

  cy1 = HS6760_i2c_read(0x01);

  

  printk("cy0 is %x add to HS6760_SetFreq\n",cy0);

  printk("cy1 is %x add to HS6760_SetFreq\n",cy1);

/*使写入的频点生效 */
Data8 =  HS6760_i2c_read(0x02);
printk("0x02 is %x add to HS6760_SetFreq\n",Data8);
Data8 &= 0xfe;//最低位置0
HS6760_i2c_write(0x02,Data8);
Delayms(10);

Data8 |= 0x01;//最高位置1
HS6760_i2c_write(0x02,Data8);
Delayms(10);

}

/**************************************************         

Function: HS6760_SetPower(uint8_t power)

Parameter: power=1~31 (Decimalism)
curFreq: FM power  dBm ,such as  1、2、…… 31                               

Description:
set FM to a Power

**************************************************/ 

void HS6760_SetPow(uint8_t power)

{
uint8_t Data8;

printk(" fm  ---HS6760 : HS6760_SetPow !!\n");
Data8 = HS6760_i2c_read(0x07);  
printk(" fm  --- HS6760 : HS6760_SetPow read(0x07)=%d !!\n", Data8);

Data8 &= 0xe0;//把低5位清零
Data8 += power;
HS6760_i2c_write(0x07,Data8); 
HS6760_Reset();//复位HS6760芯片

}

void HS6760_start(void)

{  

   printk(" fm  ---HS6760 : HS6760_start !!\n");
HS6760_Initial();//初始化HS6760
//HS6760_SetFreq(905);//设置频点为90.5M Hz
HS6760_SetPow(31);//设置功率最大
HS6760_Fre_dev(_22v5K);//设置频偏为22.5K Hz
HS6760_Stereo(enable);//开启立体声

}

/**************************************************         

Function: HS6760_Sel_mode(MODE mode)

Parameter: mode=normal/mute/sleep;                          

Description:
set FM to a work mode 

**************************************************/ 

void HS6760_Sel_mode(MODE mode)

{
uint8_t Data8;

switch (mode)
{
case hs6760_normal://最高两位设为 00'b
Data8 = HS6760_i2c_read(0x01); 
Data8 &= 0x3f;
break;

case hs6760_mute://最高两位设为 01'b
Data8 = HS6760_i2c_read(0x01); 
Data8 &= 0x3f;
Data8 |= 0x40;
break;

case hs6760_sleep://最高两位设为 10'b
Data8 = HS6760_i2c_read(0x01); 
Data8 &= 0x3f;
Data8 |= 0x80;
break;

default:
break;
}
HS6760_i2c_write(0x01,Data8);

if(mode == hs6760_normal)//(注:A3版不需要此操作)
{
HS6760_Reset();//复位HS6760芯片
}

}

/**************************************************         

Function: HS6760_Fre_dev(DEV deviation)

Parameter: deviation=_75K/_50K/_22v5K;                          

Description:
set HS6760  frequency deviation 

**************************************************/ 

void HS6760_Fre_dev(DEV deviation)

{
uint8_t Data8;

Data8 = HS6760_i2c_read(0x03);
Data8 &= 0xfc;//最低两位设为 00'b

printk(" fm  ---HS6760 : HS6760_Fre_dev HS6760_Fre_dev= !!\n");
switch (deviation)
{
case _75K:
break;
case _50K:
Data8 += 0x01;
break;
//最低两位设为 01'b
case _22v5K:
Data8 += 0x02;
break;
//最低两位设为 10'b
default:
break;
}
HS6760_i2c_write(0x03,Data8); 

}

/**************************************************         

Function: HS6760_Stereo(STATE flag)

Parameter: flag=enable/disable;                          

Description:
set HS6760  Stereo mode or mono mode. 

**************************************************/ 

void HS6760_Stereo(STATE flag)

{
uint8_t Data8;

Data8 = HS6760_i2c_read(0x03);
printk(" fm  ---HS6760 : HS6760_Stereo enable read(0x0c)=%d !!\n", Data8);
if(flag)
{
Data8 |= 0x80;
printk(" fm  ---HS6760 : HS6760_Stereo enable !!\n");
}
else
{
Data8 &= 0x7f;
printk(" fm  ---HS6760 : HS6760_Stereo disable !!\n");
}
HS6760_i2c_write(0x03,Data8); 

}

/************************************************************/

static int HS6760_open(struct inode *inode, struct file *file)

{
printk(" fm  ---HS6760 open:: %s\n", __func__);

//HS6760_Initial();
return 0;

}

static int HS6760_release(struct inode *inode, struct file *file)

{
printk(" fm  --- HS6760 release:: %s\n", __func__);
return 0;

}

static long HS6760_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

{

    void __user *dat;

    int cydat1[2] = {0,0};

    // int cydat1 = 0;

    switch(cmd)

        {

          case 1:

          {

          
dat = (void __user *) arg;
//strcpy(pinf.model,"S19");
    cydat1[0] = HS6760_i2c_read(0x00);
 
    cydat1[1] = HS6760_i2c_read(0x02);

//pinf.id=19;
copy_to_user(dat, cydat1, sizeof(cydat1));

            break; 

          }

         

          default:break;

         }

   

//printk(" fm  --- qn8027_ioctl : cmd = %d\n", cmd);
return 0;

}

ssize_t HS6760_read(struct file *fp, char __user *buf, size_t count, loff_t * off)

{
printk("  fm  --- HS6760 read:: %s\n", __func__);
return 0;

}

ssize_t HS6760_write(struct file *fp, const char __user *buf, size_t count, loff_t *off)

{
int data[2] = {0,0};/* cmd, data*/
int freq;

/*这里是从用户空间传递过来的频率*/
copy_from_user(data, buf, sizeof(data));

printk("  fm  --- HS6760 write:: %s, data[0] = %d, data[1] = %d \n\r", __func__, data[0], data[1]);

switch(data[0])
{
case 0:
//hs6760_open:
printk("  fm  --- hs6760_open:: %s\n", __func__);

//HS6760_Reset();
HS6760_FM3V3_enable();//打开HS6760
ext_spkamp_disable();//关闭机器功放输出
HS6760_start();
HS6760_Sel_mode(hs6760_normal);
break;

case 1:
//hs6760_silent_mode:
printk("  fm  --- hs6760_silent_mode:: %s\n", __func__);

HS6760_FM3V3_enable();//打开HS6760
ext_spkamp_disable();//关闭机器功放输出
HS6760_Sel_mode(hs6760_mute);
break;

case 2:
//hs6760_close:
printk("  fm  --- hs6760_close:: %s\n", __func__);

HS6760_Sel_mode(hs6760_sleep);
HS6760_FM3V3_disable();//关闭HS6760
ext_spkamp_enable();//打开机器功放输出
break;

case 9:
//设置频率
freq = data[1];

printk("  fm  --- hs6760_set freq:: freq = %d, %s\n", freq, __func__);

/*这里调用设置频率的函数*/
HS6760_SetFreq(freq);

break;
}

return 0;

}

static struct file_operations HS6760_fops = {
.owner = THIS_MODULE,
.open = HS6760_open,
.read = HS6760_read,
.write = HS6760_write,
.release = HS6760_release,
.unlocked_ioctl = HS6760_ioctl,

};

static int HS6760_probe(struct i2c_client *client, const struct i2c_device_id *id)

{
int ret;
printk(" fm  --- HS6760 probe:: %s\n", __func__);

HS6760_client = client;
HS6760_client->addr = HS6760_I2C_ID;

// 1. register character device
major = register_chrdev(0, name, &HS6760_fops);  //向内核注册一个设备,返回值为注册的主设备号
if(major < 0)
{
printk("<xxxx> register_chrdev(probe) failed\n");
goto out1;
}

// 2. class create
cls = class_create(THIS_MODULE, name);     //注册一个类,使mdev可以在"/dev/"目录下
面建立设备节点
if(IS_ERR(cls))
{
printk("<xxxx> class create failed\n");
goto out2;
}

// 3. device create
device_create(cls, NULL, MKDEV(major, 0), NULL, name); //创建一个设备节点,节点名为name

HS6760_FM3V3_enable();//打开HS6760

HS6760_start();//初始化

return 0;

out2:
unregister_chrdev(major, name);

out1:
return -1;

}

static int HS6760_remove(struct i2c_client *client)

{
printk(" fm  ---HS6760 remove:: %s\n", __func__);

device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, name);

return 0;

}

static const struct i2c_device_id HS6760_ids[] = {
{ "fm_hs6760", 0 },
{  }

};

#ifdef CONFIG_OF

static const struct of_device_id FM_HW_i2c_of_ids[] = {

    { .compatible = "mediatek,ext_speaker_amp", },
{}

};

#endif

static struct i2c_driver HS6760_driver = {
.driver = {
.name = "fm_hs6760",
.owner = THIS_MODULE,

#ifdef CONFIG_OF
.of_match_table = FM_HW_i2c_of_ids,
#endif
},
.probe = HS6760_probe,
.remove = HS6760_remove,
.id_table = HS6760_ids,

};

void fm_get_gpio_infor(void)

{
static struct device_node *node;

node = of_find_compatible_node(NULL, NULL, "mediatek,c66_fm");
if (!node) {

printk("Failed to find device-tree node: rm_hs6760\n");
return -ENODEV;
}
GPIO_FM_PWR_EN = of_get_named_gpio(node, "fm_power_gpio", 0);
GPIO_SPK_PWR_EN = of_get_named_gpio(node, "spk_power_gpio", 0);

}

static void fm_set_gpio_output(unsigned int GPIO, unsigned int output)

{

gpio_direction_output(GPIO, output);
gpio_set_value(GPIO, output);

}

static int fm_probe(struct device *dev)

{

printk(" fm  ---HS6760 fm_probe\n");
fm_get_gpio_infor();

return i2c_add_driver(&HS6760_driver);

}

static const struct of_device_id fm_of_ids[] = {
{.compatible = "mediatek,c66_fm",},
{}

};

static struct platform_driver fm_driver = {
.driver = {
   .name = "c66_fm",
   .owner = THIS_MODULE,
   .probe = fm_probe,

#ifdef CONFIG_OF
   .of_match_table = fm_of_ids,

#endif
   },

};

static int __init HS6760_init(void)
{
printk(" fm  ---HS6760 module_init()\n");
if (platform_driver_register(&fm_driver)) {
pr_err("LCM: failed to register disp driver\n");
return -ENODEV;
}

return 0;

}

/*----------------------------------------------------------------------------*/

static void __exit HS6760_exit(void)

{

}

/*----------------------------------------------------------------------------*/

MODULE_DESCRIPTION("Fm Register Driver");

MODULE_LICENSE("GPL");

module_init(HS6760_init);

module_exit(HS6760_exit);

MODULE_AUTHOR("mediatek");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: