[Feature][T106_eSDK]17.09(SDK4.8)diff_19.00(SDK5.0)

Only Configure: No
Affected branch: master
Affected module: unknow
Is it affected on both ZXIC and MTK: only ZXIC
Self-test: Yes
Doc Update: No

Change-Id: I768f6d42285f04acf919b9f8f6cd34af460c3ef4
diff --git a/upstream/linux-5.10/drivers/mfd/zx234290-core.c b/upstream/linux-5.10/drivers/mfd/zx234290-core.c
new file mode 100755
index 0000000..d43085f
--- /dev/null
+++ b/upstream/linux-5.10/drivers/mfd/zx234290-core.c
@@ -0,0 +1,680 @@
+/*
+ * zx234290-core.c  --  Device access for ZX234290 PMICs
+ *
+ * Copyright 2016 ZTE Inc.
+ *
+ * Author: yuxiang<yu.xiang5@zte.com.cn>
+ *
+ *  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/zx234290.h>
+
+#include <linux/debugfs.h>
+#include <asm/uaccess.h>
+
+#include <linux/of_gpio.h>
+#include <linux/gpio_keys.h>
+
+#include <linux/delay.h>
+#include <dma_cfg.h>
+#include <linux/reboot.h>
+
+
+#define	USER_RST_TO_NORMAL  	1
+
+//#include <mach/peri_cfg.h>
+extern int zx234290_i2c_write_simple(u8 reg, void *src);
+extern int zx234290_i2c_read_simple(u8 reg, void *dest);
+
+//void __iomem * s_poweron_type_addr;
+ unsigned long s_poweron_type_addr;
+
+/*the power on info, boot_reason */
+typedef enum
+{
+	POWER_ON_NORMAL = 0,
+	POWER_ON_FOTA,
+	POWER_ON_CHARGING,
+	POWER_ON_RTC,
+	POWER_ON_RESET,
+	POWER_ON_HDT_TEST,
+	POWER_ON_EXCEPTRESET,
+	POWER_ON_LOCALUPDATE,
+	POWER_ON_BOOST_IN,
+	POWER_ON_AMT,
+	POWER_ON_PRODUCTION,
+	POWER_ON_INVALID,
+}T_ZDrvSys_PowerOn_Type;
+
+static struct resource regulator_resources[] = {
+	{
+		.name	= "bulk-error",
+		.start	= ZX234290_INT_BUCK_FAUL,
+		.end	= ZX234290_INT_BUCK_FAUL,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.name	= "ldo_error",
+		.start	= ZX234290_INT_LDO_FAUL,
+		.end	= ZX234290_INT_LDO_FAUL,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource rtc_resources[] = {
+	{
+		.name	= "zx234290-rtc-alarm",
+		.start	= ZX234290_INT_RTC_ALRM,
+		.end	= ZX234290_INT_RTC_ALRM,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.name	= "zx234290-rtc-min",
+		.start	= ZX234290_INT_RTC_MIN,
+		.end	= ZX234290_INT_RTC_MIN,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.name	= "zx234290-rtc-hour",
+		.start	= ZX234290_INT_RTC_HOUR,
+		.end	= ZX234290_INT_RTC_HOUR,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+static struct resource powerkey_resources[] = {
+	{
+		.name	= "zx234290-pwrkey-int",
+		.start	= ZX234290_INT_PWRON,
+		.end	= ZX234290_INT_PWRON,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+
+static struct mfd_cell zx234290_cell[] = {
+	{
+		.name = "zx234290-regulators",		
+		.num_resources	= 2,
+		.resources	= &regulator_resources[0],
+		.id		= -1,
+	},
+	{
+		.name = "zx234290-rtc",		
+		.num_resources	= 3,
+		.resources	= &rtc_resources[0],
+		.id		= -1,
+	},
+	{
+		.name = "zx234290-gpadc",
+	},
+	{
+		.name = "zx234290-powerkey",			
+		.num_resources	= 1,
+		.resources	= &powerkey_resources[0],
+		.id 	= -1,
+	},
+};
+
+unsigned int boot_reason = POWER_ON_NORMAL;
+struct wakeup_source * adc_wakelock;
+
+unsigned int * get_boot_reason_addr(void)
+{
+	return (unsigned int *)s_poweron_type_addr;
+}
+EXPORT_SYMBOL(get_boot_reason_addr);
+
+static void get_boot_reason(void)
+{
+	//boot_reason = *(unsigned int *)POWERON_TYPE_BASE;
+	if(s_poweron_type_addr){
+		boot_reason = readl(s_poweron_type_addr/*+0xf8*/);
+
+		printk(KERN_INFO "[PMU] get boot_reason = %d from 0x%x.\n",boot_reason,s_poweron_type_addr);
+	}
+	else
+		printk(KERN_INFO "[PMU] boot_reason is unknown.\n");
+}
+
+#if 1
+int zx234290_set_bits(struct zx234290 *zx234290, u8 reg, u8 mask)
+{
+	u8 data;
+	int err;
+
+	mutex_lock(&zx234290->io_mutex);
+
+	err = zx234290->read(zx234290, reg, 1, &data);
+	if (err) {
+		dev_err(zx234290->dev, "Read from reg 0x%x failed\n", reg);
+		goto out;
+	}
+
+	data |= mask;
+	err = zx234290->write(zx234290, reg, 1, &data);
+	if (err)
+		dev_err(zx234290->dev, "Write to reg 0x%x failed\n", reg);
+
+out:
+	mutex_unlock(&zx234290->io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(zx234290_set_bits);
+
+int zx234290_clear_bits(struct zx234290 *zx234290, u8 reg, u8 mask)
+{
+	u8 data;
+	int err;
+
+	mutex_lock(&zx234290->io_mutex);
+	err = zx234290->read(zx234290, reg, 1, &data);
+	if (err) {
+		dev_err(zx234290->dev, "Read from reg 0x%x failed\n", reg);
+		goto out;
+	}
+
+	data &= ~mask;
+	err = zx234290->write(zx234290, reg, 1, &data);
+	if (err)
+		dev_err(zx234290->dev, "Write to reg 0x%x failed\n", reg);
+
+out:
+	mutex_unlock(&zx234290->io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(zx234290_clear_bits);
+#endif
+
+static inline int zx234290_read(struct zx234290 *zx234290, u8 reg)
+{
+	u8 val;
+	int err;
+
+	err = zx234290->read(zx234290, reg, 1, &val);
+	if (err < 0)
+		return err;
+
+	return val;
+}
+
+static inline int zx234290_write(struct zx234290 *zx234290, u8 reg, u8 val)
+{
+	return zx234290->write(zx234290, reg, 1, &val);
+}
+
+#if 1
+int zx234290_reg_read(struct zx234290 *zx234290, u8 reg)
+{
+	int data;
+
+	mutex_lock(&zx234290->io_mutex);
+
+	data = zx234290_read(zx234290, reg);
+	if (data < 0)
+		dev_err(zx234290->dev, "Read from reg 0x%x failed\n", reg);
+
+	mutex_unlock(&zx234290->io_mutex);
+	return data;
+}
+EXPORT_SYMBOL_GPL(zx234290_reg_read);
+
+int zx234290_reg_write(struct zx234290 *zx234290, u8 reg, u8 val)
+{
+	int err;
+
+	mutex_lock(&zx234290->io_mutex);
+
+	err = zx234290_write(zx234290, reg, val);
+	if (err < 0)
+		dev_err(zx234290->dev, "Write for reg 0x%x failed\n", reg);
+
+	mutex_unlock(&zx234290->io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(zx234290_reg_write);
+#endif
+#if 1
+extern int Zx234290_SetUserReg_PSM(unsigned char data);
+
+void zx29_restart(const char * cmd)
+{
+	/*set reset value = 1*/
+	unsigned char  status = ZX234290_USER_RST_TO_NORMAL;
+
+	printk(KERN_INFO"restart:enter reboot  :reset to normal\n");
+	
+	status = ZX234290_USER_RST_TO_NORMAL;
+	Zx234290_SetUserReg_PSM(status);
+}
+
+
+int pmu_reboot_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+
+	printk(" pmu reboot,in user,task is: %s\n", current->comm);
+	zx29_restart((char *) ptr);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block pmu_reboot_notifier = {
+	.notifier_call = pmu_reboot_event
+};
+
+#endif
+
+
+
+int Zx234290_SetVldo6Onoff(void)
+{
+    int ret = 0;
+    u8 reg_addr=0, reg_val=0;
+    reg_addr = 0x21;
+    ret = zx234290_i2c_read_simple(reg_addr,&reg_val);
+    if (ret) {
+        return -EIO;
+    }
+    reg_val = reg_val&(~(1<<5));
+    ret = zx234290_i2c_write_simple(reg_addr, &reg_val);
+    if (ret) {
+        return -EIO;
+    }
+    return 0;
+}
+EXPORT_SYMBOL(Zx234290_SetVldo6Onoff);
+#if 0
+int zx297510_write_pmu_flag_charging(void)
+{
+	int ret = 0;
+	unsigned char reg = 0;
+	ret = zx234290_i2c_read_simple(0xf, &reg);
+	reg = reg|0xff;
+	ret += zx234290_i2c_write_simple(0xf, &reg);
+	ret = zx234290_i2c_read_simple(0xe, &reg);
+	reg = reg|0x3;
+	ret += zx234290_i2c_write_simple(0xe, &reg);
+	return ret;
+}
+#endif
+//static void __iomem* PMU_ADDR_VIR;
+//#define GPIO_PMU_PSHOLD ZX29_GPIO_51
+unsigned int gpio_num_pshold;
+
+void zx234290_pshold_pull_down(void)
+{
+	//PMU_ADDR_VIR = ioremap(0x10d6c0,4);
+	//__raw_writel(0x0,PMU_ADDR_VIR);
+	if(gpio_num_pshold)
+		gpio_direction_output(gpio_num_pshold,0);
+	else
+		printk("zx234290_pshold_pull_down error\n");
+}
+
+//extern int zx234290_rtc_disable_timer_alarm();
+EXPORT_SYMBOL(zx234290_pshold_pull_down);
+
+/***********yuwei added at 20170523**************/
+void zx234290_pshold_pull_up(void)
+{
+	if(gpio_num_pshold)
+		gpio_direction_output(gpio_num_pshold,1);
+	else
+		printk("zx234290_pshold_pull_up error\n");
+}
+/**************/
+
+static int zx234290_set_softon(int on)
+{
+	u8 reg = 0;
+    int ret;
+
+    ret = zx234290_i2c_read_simple(ZX234290_REG_ADDR_SYS_CTRL, &reg);
+    if (ret) {
+        return -EIO;
+    }
+
+    if ((reg >> ZX234290_SOFTON_LSH) != on) {
+        reg ^= (0x01 << ZX234290_SOFTON_LSH);
+        ret = zx234290_i2c_write_simple(ZX234290_REG_ADDR_SYS_CTRL, &reg);
+        if (ret) {
+            return -EIO;
+        }
+    }
+
+    return 0;
+}
+static int zx234290_set_softon_PSM(int on)
+{
+	u8 reg = 0;
+    int ret;
+
+    ret = zx234290_i2c_read_simple_PSM(ZX234290_REG_ADDR_SYS_CTRL, &reg);
+    if (ret) {
+        return -EIO;
+    }
+
+    if ((reg >> ZX234290_SOFTON_LSH) != on) {
+        reg ^= (0x01 << ZX234290_SOFTON_LSH);
+        ret = zx234290_i2c_write_simple_PSM(ZX234290_REG_ADDR_SYS_CTRL, &reg);
+        if (ret) {
+            return -EIO;
+        }
+    }
+
+    return 0;
+}
+
+
+static bool debug_stop_poweroff = false;
+module_param(debug_stop_poweroff, bool, 0644);
+//extern void zx29_restart(char str,const char * cmd);
+static void zx234290_power_off(void)
+{
+	//void __iomem *reset_charging_reg;
+	//reset_charging_reg = ZX29_TOP_VA;
+	//zx234290_rtc_disable_timer_alarm();
+	//Zx234290_SetVldo6Onoff();
+	u8 reg_poweron = 0;
+    int ret;
+
+	if(debug_stop_poweroff )
+	{
+		printk(KERN_INFO"debug_stop_poweroff= 0x%x, for debug, bug_on!!!!\n", debug_stop_poweroff);
+		panic("poweroff");
+	}
+	zx234290_set_softon_PSM(0);
+	zx234290_pshold_pull_down();
+#if 1
+	while(1){
+		ret = zx234290_i2c_read_simple_PSM(ZX234290_REG_ADDR_STSA, &reg_poweron);	
+		if (ret) {
+			printk(KERN_INFO"power off pmu i2c read err\n");
+			break;
+		}
+		if((reg_poweron&(1<<ZX234290_STATUSA_POWERON_LSH))== 0)
+			break;
+	}
+	mdelay(50);
+	/*reset to charging*/
+	//zx29_restart(NULL,"drv_key reboot");
+#endif
+}
+
+
+#if defined(CONFIG_DEBUG_FS)
+static ssize_t debugfs_regs_write(struct file *file, const char __user *buf,size_t nbytes, loff_t *ppos)
+{
+	struct zx234290 *zx234290 = file->private_data;
+
+	unsigned int val1, val2;
+	u8 reg, value;
+	int ret;
+	char *kern_buf;
+
+	kern_buf = kzalloc(nbytes, GFP_KERNEL);
+
+	if (!kern_buf) {
+		printk(KERN_INFO "zx234290-core: Failed to allocate buffer\n");
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(kern_buf, (void  __user *)buf, nbytes)) {
+		kfree(kern_buf);
+		return -ENOMEM;
+	}
+	printk(KERN_INFO "%s input str=%s,nbytes=%d \n", __func__, kern_buf,nbytes);
+
+	ret = sscanf(kern_buf, "%x:%x", &val1, &val2);
+	if (ret < 2 || val1 > ZX234290_MAX_REGISTER ) {
+		printk(KERN_INFO "zx234290-core: failed to read user buf, ret=%d, input 0x%x:0x%x\n",
+				ret, val1, val2);
+		kfree(kern_buf);
+		return -EINVAL;
+	}
+	kfree(kern_buf);
+
+	reg = val1 & 0xff;
+	value = val2 & 0xff;
+	printk(KERN_INFO "%s input %x,%x; reg=%x,value=%x\n", __func__, val1, val2, reg, value);
+	ret = zx234290_i2c_write_simple(reg, &value);
+
+	return ret ? ret : nbytes;
+}
+
+static int debugfs_regs_show(struct seq_file *s, void *v)
+{
+	int i;
+	u8 value[ZX234290_MAX_REGISTER];
+	int ret=0;
+	u8 reg_rtc_ctrl2 = 0;
+
+	printk(KERN_INFO "%s\n", __func__);
+	memset(value, 0, sizeof(value));
+	for (i = 0; i < ZX234290_MAX_REGISTER; i++){
+		ret = zx234290_i2c_read_simple(i, &(value[i]));
+		if(ret){
+			printk(KERN_INFO "%s err=%d, break\n", __func__, ret);
+			seq_printf(s, "%s err=%d, break", __func__, ret);
+			return ret;
+		}
+	}
+
+	for (i = 0; i < ZX234290_MAX_REGISTER; i++) {
+		if((i+1)%9 == 0)
+			seq_printf(s, "\n");
+
+		seq_printf(s, "[0x%x]%02x ", i, value[i]);
+	}
+
+	reg_rtc_ctrl2 = value[ZX234290_REG_ADDR_RTC_CTRL2];
+	seq_printf(s, "\nAF=%d,TF=%d,Alarm %s,Timer %s\n",(reg_rtc_ctrl2&0x8),(reg_rtc_ctrl2&0x4),
+		  			(reg_rtc_ctrl2&0x2)? "enable":"disable",(reg_rtc_ctrl2&0x1)? "enable":"disable");
+	if(value[ZX234290_REG_ADDR_BUCK_FAULT_STATUS]||value[ZX234290_REG_ADDR_LDO_FAULT_STATUS])
+		seq_printf(s, "ldo or bulk fault!!!!!\n ");
+	else
+		seq_printf(s, "no ldo or bulk fault\n ");
+	if(value[ZX234290_REG_ADDR_TIMER_CTRL]&0x80)
+		seq_printf(s, "timer enable\n ");
+	else
+		seq_printf(s, "timer disable\n ");
+	
+
+	return ret;
+}
+
+#define DEBUGFS_FILE_ENTRY(name) \
+static int debugfs_##name##_open(struct inode *inode, struct file *file) \
+{\
+return single_open(file, debugfs_##name##_show, inode->i_private); \
+}\
+\
+static const struct file_operations debugfs_##name##_fops = { \
+.owner= THIS_MODULE, \
+.open= debugfs_##name##_open, \
+.write=debugfs_##name##_write, \
+.read= seq_read, \
+.llseek= seq_lseek, \
+.release= single_release, \
+}
+
+DEBUGFS_FILE_ENTRY(regs);
+
+int zx234290_rtc_settimer(int sec);
+
+static int debugfs_adc_get(void *data, u64 *val)
+{
+	switch ((int)data) {
+	case 0:		
+		*val = get_battery_voltage();
+		//zx234290_rtc_settimer(10);		
+		break;
+	case 1:
+		*val = get_adc1_voltage();
+		break;
+	case 2:
+		*val = get_adc2_voltage();
+		break;
+	default:
+		*val = -1;
+		break;
+	}
+
+    return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_adc_ro, debugfs_adc_get, NULL, "%llumV\n");
+
+static struct dentry *g_pmu_root;
+
+extern u32 int_irq_times;
+extern u32 int_thread_times;
+
+static void debugfs_pmu_init(struct zx234290 *zx234290)
+{
+	struct dentry *root;
+	struct dentry *node;
+	int i;
+	
+	if(!zx234290)
+		return; 
+	//create root
+	root = debugfs_create_dir("pmu_zx29", NULL);
+	if (!root){
+		dev_err(zx234290->dev, "debugfs_create_dir err=%d\n", IS_ERR(root));
+		goto err;
+	}
+	//print regs;	
+	node = debugfs_create_file("regs", S_IRUGO | S_IWUGO, root, zx234290,  &debugfs_regs_fops); 
+	if (!node){ 
+		dev_err(zx234290->dev, "debugfs_create_dir err=%d\n", IS_ERR(node));
+		goto err;
+	}
+	//print adc0;
+	node = debugfs_create_file("adc0", S_IRUGO, root, 0,  &fops_adc_ro);
+	if (!node){
+		dev_err(zx234290->dev, "debugfs_create_dir err=%d\n", IS_ERR(node));
+		goto err;
+	}
+	//print adc1;
+	node = debugfs_create_file("adc1", S_IRUGO, root, 1,  &fops_adc_ro);
+	if (!node){ 
+		dev_err(zx234290->dev, "debugfs_create_dir err=%d\n", IS_ERR(node));
+		goto err;
+	}
+	//print adc2;
+	node = debugfs_create_file("adc2", S_IRUGO, root, 2,  &fops_adc_ro);
+	if (!node){ 
+		dev_err(zx234290->dev, "debugfs_create_dir err=%d\n", IS_ERR(node));
+		goto err;
+	}
+	//print u32
+	debugfs_create_u32("irq_cnt", S_IRUGO, root, &int_irq_times);
+
+	//print u32
+	debugfs_create_u32("thread_cnt", S_IRUGO, root, &int_thread_times);
+	
+	g_pmu_root = (void *)root;
+	return;
+err: 
+	dev_err(zx234290->dev, "debugfs_pmu_init err\n");
+}
+
+#endif
+
+
+int zx234290_device_init(struct zx234290 *zx234290)
+{
+	//struct zx234290_board *pmic_plat_data = zx234290->dev->platform_data;
+	enum of_gpio_flags flags;
+	//struct zx234290_platform_data *init_data;
+	int ret;
+	int irq;
+	
+	s_poweron_type_addr = (unsigned long)ioremap(POWERON_TYPE_ADDR,0x800);
+	get_boot_reason();
+    /*
+	init_data = kzalloc(sizeof(struct zx234290_platform_data), GFP_KERNEL);
+	if (init_data == NULL)
+		return -ENOMEM;
+    */
+	mutex_init(&zx234290->io_mutex);
+	dev_set_drvdata(zx234290->dev, zx234290);
+
+	ret = mfd_add_devices(zx234290->dev, -1,
+			      zx234290_cell, ARRAY_SIZE(zx234290_cell),
+			      NULL,0, 0);
+	if (ret < 0)
+		goto err;
+	
+	gpio_num_pshold= of_get_gpio_flags(zx234290->dev->of_node, 0, &flags);
+	if (!gpio_is_valid(gpio_num_pshold)) {
+		pr_info("pmu pshold error\n");
+	}
+	gpio_direction_input(gpio_num_pshold);
+
+    //gpio_num_pshold = pmic_plat_data->pshold_gpio_num;//by yuxiang
+   // gpio_func_pshold= pmic_plat_data->pshold_gpio_func;//by yuxiang
+	if (!pm_power_off)
+		pm_power_off = zx234290_power_off;
+
+#ifdef PSHOLD_PULLUP_IN_POWEROFFCHARGING
+	/* CPE MDL don't control ps_hold pin. */
+	if (boot_reason == POWER_ON_CHARGING) {
+		zx234290_pshold_pull_up();
+	}
+#endif
+/***********PJT added **************/
+	zx234290_get_chip_version();
+	adc_wakelock = wakeup_source_register(NULL, "adc_wake");
+	if (!adc_wakelock)
+		return -ENOMEM;
+
+	//init_data->irq = pmic_plat_data->irq;
+	//init_data->irq_base = pmic_plat_data->irq_base;
+	//irq = gpio_to_irq(pmic_plat_data->irq_gpio_num);
+	ret = zx234290_irq_init(zx234290);
+	if (ret < 0)
+		goto err;
+
+	register_reboot_notifier(&pmu_reboot_notifier);
+
+#if defined(CONFIG_DEBUG_FS)
+	debugfs_pmu_init(zx234290);
+#endif
+	//kfree(init_data);
+	return ret;
+
+err:
+	//kfree(init_data);
+	mfd_remove_devices(zx234290->dev);
+	kfree(zx234290);
+	return ret;
+}
+
+void zx234290_device_exit(struct zx234290 *zx234290)
+{
+#if defined(CONFIG_DEBUG_FS)
+	if(g_pmu_root){
+		printk(KERN_INFO "zx234290_device_exit:debugfs_remove_recursive \n");
+		debugfs_remove_recursive(g_pmu_root);
+	}
+#endif
+	mfd_remove_devices(zx234290->dev);
+	kfree(zx234290);
+}
+
+
+MODULE_AUTHOR("yuxiang");
+MODULE_DESCRIPTION("ZX234290 chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/upstream/linux-5.10/drivers/mtd/mtdcore.c b/upstream/linux-5.10/drivers/mtd/mtdcore.c
new file mode 100755
index 0000000..a52a2c8
--- /dev/null
+++ b/upstream/linux-5.10/drivers/mtd/mtdcore.c
@@ -0,0 +1,2257 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Core registration and callback routines for MTD
+ * drivers and users.
+ *
+ * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
+ * Copyright © 2006      Red Hat UK Limited 
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/ioctl.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/proc_fs.h>
+#include <linux/idr.h>
+#include <linux/backing-dev.h>
+#include <linux/gfp.h>
+#include <linux/slab.h>
+#include <linux/reboot.h>
+#include <linux/leds.h>
+#include <linux/debugfs.h>
+#include <linux/nvmem-provider.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include "mtdcore.h"
+
+struct backing_dev_info *mtd_bdi;
+
+#ifdef CONFIG_PM_SLEEP
+
+static int mtd_cls_suspend(struct device *dev)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return mtd ? mtd_suspend(mtd) : 0;
+}
+
+static int mtd_cls_resume(struct device *dev)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	if (mtd)
+		mtd_resume(mtd);
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mtd_cls_pm_ops, mtd_cls_suspend, mtd_cls_resume);
+#define MTD_CLS_PM_OPS (&mtd_cls_pm_ops)
+#else
+#define MTD_CLS_PM_OPS NULL
+#endif
+
+static struct class mtd_class = {
+	.name = "mtd",
+	.owner = THIS_MODULE,
+	.pm = MTD_CLS_PM_OPS,
+};
+
+static DEFINE_IDR(mtd_idr);
+
+/* These are exported solely for the purpose of mtd_blkdevs.c. You
+   should not use them for _anything_ else */
+DEFINE_MUTEX(mtd_table_mutex);
+EXPORT_SYMBOL_GPL(mtd_table_mutex);
+
+struct mtd_info *__mtd_next_device(int i)
+{
+	return idr_get_next(&mtd_idr, &i);
+}
+EXPORT_SYMBOL_GPL(__mtd_next_device);
+
+static LIST_HEAD(mtd_notifiers);
+
+
+#define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2)
+
+/* REVISIT once MTD uses the driver model better, whoever allocates
+ * the mtd_info will probably want to use the release() hook...
+ */
+static void mtd_release(struct device *dev)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+	dev_t index = MTD_DEVT(mtd->index);
+
+	/* remove /dev/mtdXro node */
+	device_destroy(&mtd_class, index + 1);
+}
+
+static ssize_t mtd_type_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+	char *type;
+
+	switch (mtd->type) {
+	case MTD_ABSENT:
+		type = "absent";
+		break;
+	case MTD_RAM:
+		type = "ram";
+		break;
+	case MTD_ROM:
+		type = "rom";
+		break;
+	case MTD_NORFLASH:
+		type = "nor";
+		break;
+	case MTD_NANDFLASH:
+		type = "nand";
+		break;
+	case MTD_DATAFLASH:
+		type = "dataflash";
+		break;
+	case MTD_UBIVOLUME:
+		type = "ubi";
+		break;
+	case MTD_MLCNANDFLASH:
+		type = "mlc-nand";
+		break;
+	default:
+		type = "unknown";
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", type);
+}
+static DEVICE_ATTR(type, S_IRUGO, mtd_type_show, NULL);
+
+static ssize_t mtd_flags_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "0x%lx\n", (unsigned long)mtd->flags);
+}
+static DEVICE_ATTR(flags, S_IRUGO, mtd_flags_show, NULL);
+
+static ssize_t mtd_size_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%llu\n",
+		(unsigned long long)mtd->size);
+}
+static DEVICE_ATTR(size, S_IRUGO, mtd_size_show, NULL);
+
+static ssize_t mtd_erasesize_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->erasesize);
+}
+static DEVICE_ATTR(erasesize, S_IRUGO, mtd_erasesize_show, NULL);
+
+static ssize_t mtd_writesize_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->writesize);
+}
+static DEVICE_ATTR(writesize, S_IRUGO, mtd_writesize_show, NULL);
+
+static ssize_t mtd_subpagesize_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+	unsigned int subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", subpagesize);
+}
+static DEVICE_ATTR(subpagesize, S_IRUGO, mtd_subpagesize_show, NULL);
+
+static ssize_t mtd_oobsize_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->oobsize);
+}
+static DEVICE_ATTR(oobsize, S_IRUGO, mtd_oobsize_show, NULL);
+
+static ssize_t mtd_oobavail_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", mtd->oobavail);
+}
+static DEVICE_ATTR(oobavail, S_IRUGO, mtd_oobavail_show, NULL);
+
+static ssize_t mtd_numeraseregions_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", mtd->numeraseregions);
+}
+static DEVICE_ATTR(numeraseregions, S_IRUGO, mtd_numeraseregions_show,
+	NULL);
+
+static ssize_t mtd_name_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", mtd->name);
+}
+static DEVICE_ATTR(name, S_IRUGO, mtd_name_show, NULL);
+
+static ssize_t mtd_ecc_strength_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", mtd->ecc_strength);
+}
+static DEVICE_ATTR(ecc_strength, S_IRUGO, mtd_ecc_strength_show, NULL);
+
+static ssize_t mtd_bitflip_threshold_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", mtd->bitflip_threshold);
+}
+
+static ssize_t mtd_bitflip_threshold_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t count)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+	unsigned int bitflip_threshold;
+	int retval;
+
+	retval = kstrtouint(buf, 0, &bitflip_threshold);
+	if (retval)
+		return retval;
+
+	mtd->bitflip_threshold = bitflip_threshold;
+	return count;
+}
+static DEVICE_ATTR(bitflip_threshold, S_IRUGO | S_IWUSR,
+		   mtd_bitflip_threshold_show,
+		   mtd_bitflip_threshold_store);
+
+static ssize_t mtd_ecc_step_size_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", mtd->ecc_step_size);
+
+}
+static DEVICE_ATTR(ecc_step_size, S_IRUGO, mtd_ecc_step_size_show, NULL);
+
+static ssize_t mtd_ecc_stats_corrected_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+	struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", ecc_stats->corrected);
+}
+static DEVICE_ATTR(corrected_bits, S_IRUGO,
+		   mtd_ecc_stats_corrected_show, NULL);
+
+static ssize_t mtd_ecc_stats_errors_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+	struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", ecc_stats->failed);
+}
+static DEVICE_ATTR(ecc_failures, S_IRUGO, mtd_ecc_stats_errors_show, NULL);
+
+static ssize_t mtd_badblocks_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+	struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", ecc_stats->badblocks);
+}
+static DEVICE_ATTR(bad_blocks, S_IRUGO, mtd_badblocks_show, NULL);
+
+static ssize_t mtd_bbtblocks_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+	struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", ecc_stats->bbtblocks);
+}
+static DEVICE_ATTR(bbt_blocks, S_IRUGO, mtd_bbtblocks_show, NULL);
+
+static struct attribute *mtd_attrs[] = {
+	&dev_attr_type.attr,
+	&dev_attr_flags.attr,
+	&dev_attr_size.attr,
+	&dev_attr_erasesize.attr,
+	&dev_attr_writesize.attr,
+	&dev_attr_subpagesize.attr,
+	&dev_attr_oobsize.attr,
+	&dev_attr_oobavail.attr,
+	&dev_attr_numeraseregions.attr,
+	&dev_attr_name.attr,
+	&dev_attr_ecc_strength.attr,
+	&dev_attr_ecc_step_size.attr,
+	&dev_attr_corrected_bits.attr,
+	&dev_attr_ecc_failures.attr,
+	&dev_attr_bad_blocks.attr,
+	&dev_attr_bbt_blocks.attr,
+	&dev_attr_bitflip_threshold.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(mtd);
+
+static const struct device_type mtd_devtype = {
+	.name		= "mtd",
+	.groups		= mtd_groups,
+	.release	= mtd_release,
+};
+
+static int mtd_partid_debug_show(struct seq_file *s, void *p)
+{
+	struct mtd_info *mtd = s->private;
+
+	seq_printf(s, "%s\n", mtd->dbg.partid);
+
+	return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(mtd_partid_debug);
+
+static int mtd_partname_debug_show(struct seq_file *s, void *p)
+{
+	struct mtd_info *mtd = s->private;
+
+	seq_printf(s, "%s\n", mtd->dbg.partname);
+
+	return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(mtd_partname_debug);
+
+static struct dentry *dfs_dir_mtd;
+
+static void mtd_debugfs_populate(struct mtd_info *mtd)
+{
+	struct device *dev = &mtd->dev;
+	struct dentry *root;
+
+	if (IS_ERR_OR_NULL(dfs_dir_mtd))
+		return;
+
+	root = debugfs_create_dir(dev_name(dev), dfs_dir_mtd);
+	mtd->dbg.dfs_dir = root;
+
+	if (mtd->dbg.partid)
+		debugfs_create_file("partid", 0400, root, mtd,
+				    &mtd_partid_debug_fops);
+
+	if (mtd->dbg.partname)
+		debugfs_create_file("partname", 0400, root, mtd,
+				    &mtd_partname_debug_fops);
+}
+
+#ifndef CONFIG_MMU
+unsigned mtd_mmap_capabilities(struct mtd_info *mtd)
+{
+	switch (mtd->type) {
+	case MTD_RAM:
+		return NOMMU_MAP_COPY | NOMMU_MAP_DIRECT | NOMMU_MAP_EXEC |
+			NOMMU_MAP_READ | NOMMU_MAP_WRITE;
+	case MTD_ROM:
+		return NOMMU_MAP_COPY | NOMMU_MAP_DIRECT | NOMMU_MAP_EXEC |
+			NOMMU_MAP_READ;
+	default:
+		return NOMMU_MAP_COPY;
+	}
+}
+EXPORT_SYMBOL_GPL(mtd_mmap_capabilities);
+#endif
+
+static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state,
+			       void *cmd)
+{
+	struct mtd_info *mtd;
+
+	mtd = container_of(n, struct mtd_info, reboot_notifier);
+	mtd->_reboot(mtd);
+
+	return NOTIFY_DONE;
+}
+
+/**
+ * mtd_wunit_to_pairing_info - get pairing information of a wunit
+ * @mtd: pointer to new MTD device info structure
+ * @wunit: write unit we are interested in
+ * @info: returned pairing information
+ *
+ * Retrieve pairing information associated to the wunit.
+ * This is mainly useful when dealing with MLC/TLC NANDs where pages can be
+ * paired together, and where programming a page may influence the page it is
+ * paired with.
+ * The notion of page is replaced by the term wunit (write-unit) to stay
+ * consistent with the ->writesize field.
+ *
+ * The @wunit argument can be extracted from an absolute offset using
+ * mtd_offset_to_wunit(). @info is filled with the pairing information attached
+ * to @wunit.
+ *
+ * From the pairing info the MTD user can find all the wunits paired with
+ * @wunit using the following loop:
+ *
+ * for (i = 0; i < mtd_pairing_groups(mtd); i++) {
+ *	info.pair = i;
+ *	mtd_pairing_info_to_wunit(mtd, &info);
+ *	...
+ * }
+ */
+int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
+			      struct mtd_pairing_info *info)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+	int npairs = mtd_wunit_per_eb(master) / mtd_pairing_groups(master);
+
+	if (wunit < 0 || wunit >= npairs)
+		return -EINVAL;
+
+	if (master->pairing && master->pairing->get_info)
+		return master->pairing->get_info(master, wunit, info);
+
+	info->group = 0;
+	info->pair = wunit;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtd_wunit_to_pairing_info);
+
+/**
+ * mtd_pairing_info_to_wunit - get wunit from pairing information
+ * @mtd: pointer to new MTD device info structure
+ * @info: pairing information struct
+ *
+ * Returns a positive number representing the wunit associated to the info
+ * struct, or a negative error code.
+ *
+ * This is the reverse of mtd_wunit_to_pairing_info(), and can help one to
+ * iterate over all wunits of a given pair (see mtd_wunit_to_pairing_info()
+ * doc).
+ *
+ * It can also be used to only program the first page of each pair (i.e.
+ * page attached to group 0), which allows one to use an MLC NAND in
+ * software-emulated SLC mode:
+ *
+ * info.group = 0;
+ * npairs = mtd_wunit_per_eb(mtd) / mtd_pairing_groups(mtd);
+ * for (info.pair = 0; info.pair < npairs; info.pair++) {
+ *	wunit = mtd_pairing_info_to_wunit(mtd, &info);
+ *	mtd_write(mtd, mtd_wunit_to_offset(mtd, blkoffs, wunit),
+ *		  mtd->writesize, &retlen, buf + (i * mtd->writesize));
+ * }
+ */
+int mtd_pairing_info_to_wunit(struct mtd_info *mtd,
+			      const struct mtd_pairing_info *info)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+	int ngroups = mtd_pairing_groups(master);
+	int npairs = mtd_wunit_per_eb(master) / ngroups;
+
+	if (!info || info->pair < 0 || info->pair >= npairs ||
+	    info->group < 0 || info->group >= ngroups)
+		return -EINVAL;
+
+	if (master->pairing && master->pairing->get_wunit)
+		return mtd->pairing->get_wunit(master, info);
+
+	return info->pair;
+}
+EXPORT_SYMBOL_GPL(mtd_pairing_info_to_wunit);
+
+/**
+ * mtd_pairing_groups - get the number of pairing groups
+ * @mtd: pointer to new MTD device info structure
+ *
+ * Returns the number of pairing groups.
+ *
+ * This number is usually equal to the number of bits exposed by a single
+ * cell, and can be used in conjunction with mtd_pairing_info_to_wunit()
+ * to iterate over all pages of a given pair.
+ */
+int mtd_pairing_groups(struct mtd_info *mtd)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	if (!master->pairing || !master->pairing->ngroups)
+		return 1;
+
+	return master->pairing->ngroups;
+}
+EXPORT_SYMBOL_GPL(mtd_pairing_groups);
+
+static int mtd_nvmem_reg_read(void *priv, unsigned int offset,
+			      void *val, size_t bytes)
+{
+	struct mtd_info *mtd = priv;
+	size_t retlen;
+	int err;
+
+	err = mtd_read(mtd, offset, bytes, &retlen, val);
+	if (err && err != -EUCLEAN)
+		return err;
+
+	return retlen == bytes ? 0 : -EIO;
+}
+
+static int mtd_nvmem_add(struct mtd_info *mtd)
+{
+	struct nvmem_config config = {};
+
+	config.id = -1;
+	config.dev = &mtd->dev;
+	config.name = dev_name(&mtd->dev);
+	config.owner = THIS_MODULE;
+	config.reg_read = mtd_nvmem_reg_read;
+	config.size = mtd->size;
+	config.word_size = 1;
+	config.stride = 1;
+	config.read_only = true;
+	config.root_only = true;
+	config.no_of_node = true;
+	config.priv = mtd;
+
+	mtd->nvmem = nvmem_register(&config);
+	if (IS_ERR(mtd->nvmem)) {
+		/* Just ignore if there is no NVMEM support in the kernel */
+		if (PTR_ERR(mtd->nvmem) == -EOPNOTSUPP) {
+			mtd->nvmem = NULL;
+		} else {
+			dev_err(&mtd->dev, "Failed to register NVMEM device\n");
+			return PTR_ERR(mtd->nvmem);
+		}
+	}
+
+	return 0;
+}
+
+/**
+ *	add_mtd_device - register an MTD device
+ *	@mtd: pointer to new MTD device info structure
+ *
+ *	Add a device to the list of MTD devices present in the system, and
+ *	notify each currently active MTD 'user' of its arrival. Returns
+ *	zero on success or non-zero on failure.
+ */
+
+int add_mtd_device(struct mtd_info *mtd)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+	struct mtd_notifier *not;
+	int i, error;
+
+	/*
+	 * May occur, for instance, on buggy drivers which call
+	 * mtd_device_parse_register() multiple times on the same master MTD,
+	 * especially with CONFIG_MTD_PARTITIONED_MASTER=y.
+	 */
+	if (WARN_ONCE(mtd->dev.type, "MTD already registered\n"))
+		return -EEXIST;
+
+	BUG_ON(mtd->writesize == 0);
+
+	/*
+	 * MTD drivers should implement ->_{write,read}() or
+	 * ->_{write,read}_oob(), but not both.
+	 */
+	if (WARN_ON((mtd->_write && mtd->_write_oob) ||
+		    (mtd->_read && mtd->_read_oob)))
+		return -EINVAL;
+
+	if (WARN_ON((!mtd->erasesize || !master->_erase) &&
+		    !(mtd->flags & MTD_NO_ERASE)))
+		return -EINVAL;
+
+	/*
+	 * MTD_SLC_ON_MLC_EMULATION can only be set on partitions, when the
+	 * master is an MLC NAND and has a proper pairing scheme defined.
+	 * We also reject masters that implement ->_writev() for now, because
+	 * NAND controller drivers don't implement this hook, and adding the
+	 * SLC -> MLC address/length conversion to this path is useless if we
+	 * don't have a user.
+	 */
+	if (mtd->flags & MTD_SLC_ON_MLC_EMULATION &&
+	    (!mtd_is_partition(mtd) || master->type != MTD_MLCNANDFLASH ||
+	     !master->pairing || master->_writev))
+		return -EINVAL;
+
+	mutex_lock(&mtd_table_mutex);
+
+	i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
+	if (i < 0) {
+		error = i;
+		goto fail_locked;
+	}
+
+	mtd->index = i;
+	mtd->usecount = 0;
+
+	/* default value if not set by driver */
+	if (mtd->bitflip_threshold == 0)
+		mtd->bitflip_threshold = mtd->ecc_strength;
+
+	if (mtd->flags & MTD_SLC_ON_MLC_EMULATION) {
+		int ngroups = mtd_pairing_groups(master);
+
+		mtd->erasesize /= ngroups;
+		mtd->size = (u64)mtd_div_by_eb(mtd->size, master) *
+			    mtd->erasesize;
+	}
+
+	if (is_power_of_2(mtd->erasesize))
+		mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
+	else
+		mtd->erasesize_shift = 0;
+
+	if (is_power_of_2(mtd->writesize))
+		mtd->writesize_shift = ffs(mtd->writesize) - 1;
+	else
+		mtd->writesize_shift = 0;
+
+	mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
+	mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
+
+	/* Some chips always power up locked. Unlock them now */
+	if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
+		error = mtd_unlock(mtd, 0, mtd->size);
+		if (error && error != -EOPNOTSUPP)
+			printk(KERN_WARNING
+			       "%s: unlock failed, writes may not work\n",
+			       mtd->name);
+		/* Ignore unlock failures? */
+		error = 0;
+	}
+
+	/* Caller should have set dev.parent to match the
+	 * physical device, if appropriate.
+	 */
+	mtd->dev.type = &mtd_devtype;
+	mtd->dev.class = &mtd_class;
+	mtd->dev.devt = MTD_DEVT(i);
+	dev_set_name(&mtd->dev, "mtd%d", i);
+	dev_set_drvdata(&mtd->dev, mtd);
+	of_node_get(mtd_get_of_node(mtd));
+	error = device_register(&mtd->dev);
+	if (error)
+		goto fail_added;
+
+	/* Add the nvmem provider */
+	error = mtd_nvmem_add(mtd);
+	if (error)
+		goto fail_nvmem_add;
+
+	mtd_debugfs_populate(mtd);
+
+	device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
+		      "mtd%dro", i);
+
+	pr_debug("mtd: Giving out device %d to %s\n", i, mtd->name);
+	/* No need to get a refcount on the module containing
+	   the notifier, since we hold the mtd_table_mutex */
+	list_for_each_entry(not, &mtd_notifiers, list)
+		not->add(mtd);
+
+	mutex_unlock(&mtd_table_mutex);
+	/* We _know_ we aren't being removed, because
+	   our caller is still holding us here. So none
+	   of this try_ nonsense, and no bitching about it
+	   either. :) */
+	__module_get(THIS_MODULE);
+	return 0;
+
+fail_nvmem_add:
+	device_unregister(&mtd->dev);
+fail_added:
+	of_node_put(mtd_get_of_node(mtd));
+	idr_remove(&mtd_idr, i);
+fail_locked:
+	mutex_unlock(&mtd_table_mutex);
+	return error;
+}
+
+/**
+ *	del_mtd_device - unregister an MTD device
+ *	@mtd: pointer to MTD device info structure
+ *
+ *	Remove a device from the list of MTD devices present in the system,
+ *	and notify each currently active MTD 'user' of its departure.
+ *	Returns zero on success or 1 on failure, which currently will happen
+ *	if the requested device does not appear to be present in the list.
+ */
+
+int del_mtd_device(struct mtd_info *mtd)
+{
+	int ret;
+	struct mtd_notifier *not;
+
+	mutex_lock(&mtd_table_mutex);
+
+	if (idr_find(&mtd_idr, mtd->index) != mtd) {
+		ret = -ENODEV;
+		goto out_error;
+	}
+
+	/* No need to get a refcount on the module containing
+		the notifier, since we hold the mtd_table_mutex */
+	list_for_each_entry(not, &mtd_notifiers, list)
+		not->remove(mtd);
+
+	if (mtd->usecount) {
+		printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n",
+		       mtd->index, mtd->name, mtd->usecount);
+		ret = -EBUSY;
+	} else {
+		debugfs_remove_recursive(mtd->dbg.dfs_dir);
+
+		/* Try to remove the NVMEM provider */
+		if (mtd->nvmem)
+			nvmem_unregister(mtd->nvmem);
+
+		device_unregister(&mtd->dev);
+
+		idr_remove(&mtd_idr, mtd->index);
+		of_node_put(mtd_get_of_node(mtd));
+
+		module_put(THIS_MODULE);
+		ret = 0;
+	}
+
+out_error:
+	mutex_unlock(&mtd_table_mutex);
+	return ret;
+}
+
+/*
+ * Set a few defaults based on the parent devices, if not provided by the
+ * driver
+ */
+static void mtd_set_dev_defaults(struct mtd_info *mtd)
+{
+	if (mtd->dev.parent) {
+		if (!mtd->owner && mtd->dev.parent->driver)
+			mtd->owner = mtd->dev.parent->driver->owner;
+		if (!mtd->name)
+			mtd->name = dev_name(mtd->dev.parent);
+	} else {
+		pr_debug("mtd device won't show a device symlink in sysfs\n");
+	}
+
+	INIT_LIST_HEAD(&mtd->partitions);
+	mutex_init(&mtd->master.partitions_lock);
+}
+
+/**
+ * mtd_device_parse_register - parse partitions and register an MTD device.
+ *
+ * @mtd: the MTD device to register
+ * @types: the list of MTD partition probes to try, see
+ *         'parse_mtd_partitions()' for more information
+ * @parser_data: MTD partition parser-specific data
+ * @parts: fallback partition information to register, if parsing fails;
+ *         only valid if %nr_parts > %0
+ * @nr_parts: the number of partitions in parts, if zero then the full
+ *            MTD device is registered if no partition info is found
+ *
+ * This function aggregates MTD partitions parsing (done by
+ * 'parse_mtd_partitions()') and MTD device and partitions registering. It
+ * basically follows the most common pattern found in many MTD drivers:
+ *
+ * * If the MTD_PARTITIONED_MASTER option is set, then the device as a whole is
+ *   registered first.
+ * * Then It tries to probe partitions on MTD device @mtd using parsers
+ *   specified in @types (if @types is %NULL, then the default list of parsers
+ *   is used, see 'parse_mtd_partitions()' for more information). If none are
+ *   found this functions tries to fallback to information specified in
+ *   @parts/@nr_parts.
+ * * If no partitions were found this function just registers the MTD device
+ *   @mtd and exits.
+ *
+ * Returns zero in case of success and a negative error code in case of failure.
+ */
+int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
+			      struct mtd_part_parser_data *parser_data,
+			      const struct mtd_partition *parts,
+			      int nr_parts)
+{
+	int ret;
+
+	mtd_set_dev_defaults(mtd);
+
+	if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
+		ret = add_mtd_device(mtd);
+		if (ret)
+			return ret;
+	}
+
+	/* Prefer parsed partitions over driver-provided fallback */
+	ret = parse_mtd_partitions(mtd, types, parser_data);
+	if (ret == -EPROBE_DEFER)
+		goto out;
+
+	if (ret > 0)
+		ret = 0;
+	else if (nr_parts)
+		ret = add_mtd_partitions(mtd, parts, nr_parts);
+	else if (!device_is_registered(&mtd->dev))
+		ret = add_mtd_device(mtd);
+	else
+		ret = 0;
+
+	if (ret)
+		goto out;
+
+	/*
+	 * FIXME: some drivers unfortunately call this function more than once.
+	 * So we have to check if we've already assigned the reboot notifier.
+	 *
+	 * Generally, we can make multiple calls work for most cases, but it
+	 * does cause problems with parse_mtd_partitions() above (e.g.,
+	 * cmdlineparts will register partitions more than once).
+	 */
+	WARN_ONCE(mtd->_reboot && mtd->reboot_notifier.notifier_call,
+		  "MTD already registered\n");
+	if (mtd->_reboot && !mtd->reboot_notifier.notifier_call) {
+		mtd->reboot_notifier.notifier_call = mtd_reboot_notifier;
+		register_reboot_notifier(&mtd->reboot_notifier);
+	}
+
+out:
+	if (ret && device_is_registered(&mtd->dev))
+		del_mtd_device(mtd);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mtd_device_parse_register);
+
+/**
+ * mtd_device_unregister - unregister an existing MTD device.
+ *
+ * @master: the MTD device to unregister.  This will unregister both the master
+ *          and any partitions if registered.
+ */
+int mtd_device_unregister(struct mtd_info *master)
+{
+	int err;
+
+	if (master->_reboot)
+		unregister_reboot_notifier(&master->reboot_notifier);
+
+	err = del_mtd_partitions(master);
+	if (err)
+		return err;
+
+	if (!device_is_registered(&master->dev))
+		return 0;
+
+	return del_mtd_device(master);
+}
+EXPORT_SYMBOL_GPL(mtd_device_unregister);
+
+/**
+ *	register_mtd_user - register a 'user' of MTD devices.
+ *	@new: pointer to notifier info structure
+ *
+ *	Registers a pair of callbacks function to be called upon addition
+ *	or removal of MTD devices. Causes the 'add' callback to be immediately
+ *	invoked for each MTD device currently present in the system.
+ */
+void register_mtd_user (struct mtd_notifier *new)
+{
+	struct mtd_info *mtd;
+
+	mutex_lock(&mtd_table_mutex);
+
+	list_add(&new->list, &mtd_notifiers);
+
+	__module_get(THIS_MODULE);
+
+	mtd_for_each_device(mtd)
+		new->add(mtd);
+
+	mutex_unlock(&mtd_table_mutex);
+}
+EXPORT_SYMBOL_GPL(register_mtd_user);
+
+/**
+ *	unregister_mtd_user - unregister a 'user' of MTD devices.
+ *	@old: pointer to notifier info structure
+ *
+ *	Removes a callback function pair from the list of 'users' to be
+ *	notified upon addition or removal of MTD devices. Causes the
+ *	'remove' callback to be immediately invoked for each MTD device
+ *	currently present in the system.
+ */
+int unregister_mtd_user (struct mtd_notifier *old)
+{
+	struct mtd_info *mtd;
+
+	mutex_lock(&mtd_table_mutex);
+
+	module_put(THIS_MODULE);
+
+	mtd_for_each_device(mtd)
+		old->remove(mtd);
+
+	list_del(&old->list);
+	mutex_unlock(&mtd_table_mutex);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(unregister_mtd_user);
+
+/**
+ *	get_mtd_device - obtain a validated handle for an MTD device
+ *	@mtd: last known address of the required MTD device
+ *	@num: internal device number of the required MTD device
+ *
+ *	Given a number and NULL address, return the num'th entry in the device
+ *	table, if any.	Given an address and num == -1, search the device table
+ *	for a device with that address and return if it's still present. Given
+ *	both, return the num'th driver only if its address matches. Return
+ *	error code if not.
+ */
+struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
+{
+	struct mtd_info *ret = NULL, *other;
+	int err = -ENODEV;
+
+	mutex_lock(&mtd_table_mutex);
+
+	if (num == -1) {
+		mtd_for_each_device(other) {
+			if (other == mtd) {
+				ret = mtd;
+				break;
+			}
+		}
+	} else if (num >= 0) {
+		ret = idr_find(&mtd_idr, num);
+		if (mtd && mtd != ret)
+			ret = NULL;
+	}
+
+	if (!ret) {
+		ret = ERR_PTR(err);
+		goto out;
+	}
+
+	err = __get_mtd_device(ret);
+	if (err)
+		ret = ERR_PTR(err);
+out:
+	mutex_unlock(&mtd_table_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(get_mtd_device);
+
+
+int __get_mtd_device(struct mtd_info *mtd)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+	int err;
+
+	if (!try_module_get(master->owner))
+		return -ENODEV;
+
+	if (master->_get_device) {
+		err = master->_get_device(mtd);
+
+		if (err) {
+			module_put(master->owner);
+			return err;
+		}
+	}
+
+	master->usecount++;
+
+	while (mtd->parent) {
+		mtd->usecount++;
+		mtd = mtd->parent;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(__get_mtd_device);
+
+/**
+ *	get_mtd_device_nm - obtain a validated handle for an MTD device by
+ *	device name
+ *	@name: MTD device name to open
+ *
+ * 	This function returns MTD device description structure in case of
+ * 	success and an error code in case of failure.
+ */
+struct mtd_info *get_mtd_device_nm(const char *name)
+{
+	int err = -ENODEV;
+	struct mtd_info *mtd = NULL, *other;
+
+	mutex_lock(&mtd_table_mutex);
+
+	mtd_for_each_device(other) {
+		if (!strcmp(name, other->name)) {
+			mtd = other;
+			break;
+		}
+	}
+
+	if (!mtd)
+		goto out_unlock;
+
+	err = __get_mtd_device(mtd);
+	if (err)
+		goto out_unlock;
+
+	mutex_unlock(&mtd_table_mutex);
+	return mtd;
+
+out_unlock:
+	mutex_unlock(&mtd_table_mutex);
+	return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(get_mtd_device_nm);
+
+void put_mtd_device(struct mtd_info *mtd)
+{
+	mutex_lock(&mtd_table_mutex);
+	__put_mtd_device(mtd);
+	mutex_unlock(&mtd_table_mutex);
+
+}
+EXPORT_SYMBOL_GPL(put_mtd_device);
+
+void __put_mtd_device(struct mtd_info *mtd)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	while (mtd->parent) {
+		--mtd->usecount;
+		BUG_ON(mtd->usecount < 0);
+		mtd = mtd->parent;
+	}
+
+	master->usecount--;
+
+	if (master->_put_device)
+		master->_put_device(master);
+
+	module_put(master->owner);
+}
+EXPORT_SYMBOL_GPL(__put_mtd_device);
+
+/*
+ * Erase is an synchronous operation. Device drivers are epected to return a
+ * negative error code if the operation failed and update instr->fail_addr
+ * to point the portion that was not properly erased.
+ */
+int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+	u64 mst_ofs = mtd_get_master_ofs(mtd, 0);
+	struct erase_info adjinstr;
+	int ret;
+
+	instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
+	adjinstr = *instr;
+
+	if (!mtd->erasesize || !master->_erase)
+		return -ENOTSUPP;
+
+	if (instr->addr >= mtd->size || instr->len > mtd->size - instr->addr)
+		return -EINVAL;
+	if (!(mtd->flags & MTD_WRITEABLE))
+		return -EROFS;
+
+	if (!instr->len)
+		return 0;
+
+	ledtrig_mtd_activity();
+
+	if (mtd->flags & MTD_SLC_ON_MLC_EMULATION) {
+		adjinstr.addr = (loff_t)mtd_div_by_eb(instr->addr, mtd) *
+				master->erasesize;
+		adjinstr.len = ((u64)mtd_div_by_eb(instr->addr + instr->len, mtd) *
+				master->erasesize) -
+			       adjinstr.addr;
+	}
+
+	adjinstr.addr += mst_ofs;
+
+	ret = master->_erase(master, &adjinstr);
+
+	if (adjinstr.fail_addr != MTD_FAIL_ADDR_UNKNOWN) {
+		instr->fail_addr = adjinstr.fail_addr - mst_ofs;
+		if (mtd->flags & MTD_SLC_ON_MLC_EMULATION) {
+			instr->fail_addr = mtd_div_by_eb(instr->fail_addr,
+							 master);
+			instr->fail_addr *= mtd->erasesize;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mtd_erase);
+
+/*
+ * This stuff for eXecute-In-Place. phys is optional and may be set to NULL.
+ */
+int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+	      void **virt, resource_size_t *phys)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	*retlen = 0;
+	*virt = NULL;
+	if (phys)
+		*phys = 0;
+	if (!master->_point)
+		return -EOPNOTSUPP;
+	if (from < 0 || from >= mtd->size || len > mtd->size - from)
+		return -EINVAL;
+	if (!len)
+		return 0;
+
+	from = mtd_get_master_ofs(mtd, from);
+	return master->_point(master, from, len, retlen, virt, phys);
+}
+EXPORT_SYMBOL_GPL(mtd_point);
+
+/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
+int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	if (!master->_unpoint)
+		return -EOPNOTSUPP;
+	if (from < 0 || from >= mtd->size || len > mtd->size - from)
+		return -EINVAL;
+	if (!len)
+		return 0;
+	return master->_unpoint(master, mtd_get_master_ofs(mtd, from), len);
+}
+EXPORT_SYMBOL_GPL(mtd_unpoint);
+
+/*
+ * Allow NOMMU mmap() to directly map the device (if not NULL)
+ * - return the address to which the offset maps
+ * - return -ENOSYS to indicate refusal to do the mapping
+ */
+unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len,
+				    unsigned long offset, unsigned long flags)
+{
+	size_t retlen;
+	void *virt;
+	int ret;
+
+	ret = mtd_point(mtd, offset, len, &retlen, &virt, NULL);
+	if (ret)
+		return ret;
+	if (retlen != len) {
+		mtd_unpoint(mtd, offset, retlen);
+		return -ENOSYS;
+	}
+	return (unsigned long)virt;
+}
+EXPORT_SYMBOL_GPL(mtd_get_unmapped_area);
+
+static void mtd_update_ecc_stats(struct mtd_info *mtd, struct mtd_info *master,
+				 const struct mtd_ecc_stats *old_stats)
+{
+	struct mtd_ecc_stats diff;
+
+	if (master == mtd)
+		return;
+
+	diff = master->ecc_stats;
+	diff.failed -= old_stats->failed;
+	diff.corrected -= old_stats->corrected;
+
+	while (mtd->parent) {
+		mtd->ecc_stats.failed += diff.failed;
+		mtd->ecc_stats.corrected += diff.corrected;
+		mtd = mtd->parent;
+	}
+}
+
+int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+	     u_char *buf)
+{
+	struct mtd_oob_ops ops = {
+		.len = len,
+		.datbuf = buf,
+	};
+	int ret;
+
+	ret = mtd_read_oob(mtd, from, &ops);
+	*retlen = ops.retlen;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mtd_read);
+
+int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+	      const u_char *buf)
+{
+	struct mtd_oob_ops ops = {
+		.len = len,
+		.datbuf = (u8 *)buf,
+	};
+	int ret;
+
+	ret = mtd_write_oob(mtd, to, &ops);
+	*retlen = ops.retlen;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mtd_write);
+
+/*
+ * In blackbox flight recorder like scenarios we want to make successful writes
+ * in interrupt context. panic_write() is only intended to be called when its
+ * known the kernel is about to panic and we need the write to succeed. Since
+ * the kernel is not going to be running for much longer, this function can
+ * break locks and delay to ensure the write succeeds (but not sleep).
+ */
+int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+		    const u_char *buf)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	*retlen = 0;
+	if (!master->_panic_write)
+		return -EOPNOTSUPP;
+	if (to < 0 || to >= mtd->size || len > mtd->size - to)
+		return -EINVAL;
+	if (!(mtd->flags & MTD_WRITEABLE))
+		return -EROFS;
+	if (!len)
+		return 0;
+	if (!master->oops_panic_write)
+		master->oops_panic_write = true;
+
+	return master->_panic_write(master, mtd_get_master_ofs(mtd, to), len,
+				    retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_panic_write);
+
+static int mtd_check_oob_ops(struct mtd_info *mtd, loff_t offs,
+			     struct mtd_oob_ops *ops)
+{
+	/*
+	 * Some users are setting ->datbuf or ->oobbuf to NULL, but are leaving
+	 * ->len or ->ooblen uninitialized. Force ->len and ->ooblen to 0 in
+	 *  this case.
+	 */
+	if (!ops->datbuf)
+		ops->len = 0;
+
+	if (!ops->oobbuf)
+		ops->ooblen = 0;
+
+	if (offs < 0 || offs + ops->len > mtd->size)
+		return -EINVAL;
+
+	if (ops->ooblen) {
+		size_t maxooblen;
+
+		if (ops->ooboffs >= mtd_oobavail(mtd, ops))
+			return -EINVAL;
+
+		maxooblen = ((size_t)(mtd_div_by_ws(mtd->size, mtd) -
+				      mtd_div_by_ws(offs, mtd)) *
+			     mtd_oobavail(mtd, ops)) - ops->ooboffs;
+		if (ops->ooblen > maxooblen)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mtd_read_oob_std(struct mtd_info *mtd, loff_t from,
+			    struct mtd_oob_ops *ops)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+	int ret;
+
+	from = mtd_get_master_ofs(mtd, from);
+	if (master->_read_oob)
+		ret = master->_read_oob(master, from, ops);
+	else
+		ret = master->_read(master, from, ops->len, &ops->retlen,
+				    ops->datbuf);
+
+	return ret;
+}
+
+static int mtd_write_oob_std(struct mtd_info *mtd, loff_t to,
+			     struct mtd_oob_ops *ops)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+	int ret;
+
+	to = mtd_get_master_ofs(mtd, to);
+	if (master->_write_oob)
+		ret = master->_write_oob(master, to, ops);
+	else
+		ret = master->_write(master, to, ops->len, &ops->retlen,
+				     ops->datbuf);
+
+	return ret;
+}
+
+static int mtd_io_emulated_slc(struct mtd_info *mtd, loff_t start, bool read,
+			       struct mtd_oob_ops *ops)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+	int ngroups = mtd_pairing_groups(master);
+	int npairs = mtd_wunit_per_eb(master) / ngroups;
+	struct mtd_oob_ops adjops = *ops;
+	unsigned int wunit, oobavail;
+	struct mtd_pairing_info info;
+	int max_bitflips = 0;
+	u32 ebofs, pageofs;
+	loff_t base, pos;
+
+	ebofs = mtd_mod_by_eb(start, mtd);
+	base = (loff_t)mtd_div_by_eb(start, mtd) * master->erasesize;
+	info.group = 0;
+	info.pair = mtd_div_by_ws(ebofs, mtd);
+	pageofs = mtd_mod_by_ws(ebofs, mtd);
+	oobavail = mtd_oobavail(mtd, ops);
+
+	while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) {
+		int ret;
+
+		if (info.pair >= npairs) {
+			info.pair = 0;
+			base += master->erasesize;
+		}
+
+		wunit = mtd_pairing_info_to_wunit(master, &info);
+		pos = mtd_wunit_to_offset(mtd, base, wunit);
+
+		adjops.len = ops->len - ops->retlen;
+		if (adjops.len > mtd->writesize - pageofs)
+			adjops.len = mtd->writesize - pageofs;
+
+		adjops.ooblen = ops->ooblen - ops->oobretlen;
+		if (adjops.ooblen > oobavail - adjops.ooboffs)
+			adjops.ooblen = oobavail - adjops.ooboffs;
+
+		if (read) {
+			ret = mtd_read_oob_std(mtd, pos + pageofs, &adjops);
+			if (ret > 0)
+				max_bitflips = max(max_bitflips, ret);
+		} else {
+			ret = mtd_write_oob_std(mtd, pos + pageofs, &adjops);
+		}
+
+		if (ret < 0)
+			return ret;
+
+		max_bitflips = max(max_bitflips, ret);
+		ops->retlen += adjops.retlen;
+		ops->oobretlen += adjops.oobretlen;
+		adjops.datbuf += adjops.retlen;
+		adjops.oobbuf += adjops.oobretlen;
+		adjops.ooboffs = 0;
+		pageofs = 0;
+		info.pair++;
+	}
+
+	return max_bitflips;
+}
+
+int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+	struct mtd_ecc_stats old_stats = master->ecc_stats;
+	int ret_code;
+
+	ops->retlen = ops->oobretlen = 0;
+
+	ret_code = mtd_check_oob_ops(mtd, from, ops);
+	if (ret_code)
+		return ret_code;
+
+	ledtrig_mtd_activity();
+
+	/* Check the validity of a potential fallback on mtd->_read */
+	if (!master->_read_oob && (!master->_read || ops->oobbuf))
+		return -EOPNOTSUPP;
+
+	if (mtd->flags & MTD_SLC_ON_MLC_EMULATION)
+		ret_code = mtd_io_emulated_slc(mtd, from, true, ops);
+	else
+		ret_code = mtd_read_oob_std(mtd, from, ops);
+
+	mtd_update_ecc_stats(mtd, master, &old_stats);
+
+	/*
+	 * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
+	 * similar to mtd->_read(), returning a non-negative integer
+	 * representing max bitflips. In other cases, mtd->_read_oob() may
+	 * return -EUCLEAN. In all cases, perform similar logic to mtd_read().
+	 */
+	if (unlikely(ret_code < 0))
+		return ret_code;
+	if (mtd->ecc_strength == 0)
+		return 0;	/* device lacks ecc */
+	//printk("ecc strength = %d.\n",mtd->ecc_strength);
+	//printk("bitflip_threshold = %d.\n",mtd->bitflip_threshold);
+	if (mtd->bitflip_threshold == 0)
+		mtd->bitflip_threshold = mtd->ecc_strength;
+	return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
+}
+EXPORT_SYMBOL_GPL(mtd_read_oob);
+
+int mtd_write_oob(struct mtd_info *mtd, loff_t to,
+				struct mtd_oob_ops *ops)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+	int ret;
+
+	ops->retlen = ops->oobretlen = 0;
+
+	if (!(mtd->flags & MTD_WRITEABLE))
+		return -EROFS;
+
+	ret = mtd_check_oob_ops(mtd, to, ops);
+	if (ret)
+		return ret;
+
+	ledtrig_mtd_activity();
+
+	/* Check the validity of a potential fallback on mtd->_write */
+	if (!master->_write_oob && (!master->_write || ops->oobbuf))
+		return -EOPNOTSUPP;
+
+	if (mtd->flags & MTD_SLC_ON_MLC_EMULATION)
+		return mtd_io_emulated_slc(mtd, to, false, ops);
+
+	return mtd_write_oob_std(mtd, to, ops);
+}
+EXPORT_SYMBOL_GPL(mtd_write_oob);
+
+/**
+ * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section
+ * @mtd: MTD device structure
+ * @section: ECC section. Depending on the layout you may have all the ECC
+ *	     bytes stored in a single contiguous section, or one section
+ *	     per ECC chunk (and sometime several sections for a single ECC
+ *	     ECC chunk)
+ * @oobecc: OOB region struct filled with the appropriate ECC position
+ *	    information
+ *
+ * This function returns ECC section information in the OOB area. If you want
+ * to get all the ECC bytes information, then you should call
+ * mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
+		      struct mtd_oob_region *oobecc)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	memset(oobecc, 0, sizeof(*oobecc));
+
+	if (!master || section < 0)
+		return -EINVAL;
+
+	if (!master->ooblayout || !master->ooblayout->ecc)
+		return -ENOTSUPP;
+
+	return master->ooblayout->ecc(master, section, oobecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc);
+
+/**
+ * mtd_ooblayout_free - Get the OOB region definition of a specific free
+ *			section
+ * @mtd: MTD device structure
+ * @section: Free section you are interested in. Depending on the layout
+ *	     you may have all the free bytes stored in a single contiguous
+ *	     section, or one section per ECC chunk plus an extra section
+ *	     for the remaining bytes (or other funky layout).
+ * @oobfree: OOB region struct filled with the appropriate free position
+ *	     information
+ *
+ * This function returns free bytes position in the OOB area. If you want
+ * to get all the free bytes information, then you should call
+ * mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_free(struct mtd_info *mtd, int section,
+		       struct mtd_oob_region *oobfree)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	memset(oobfree, 0, sizeof(*oobfree));
+
+	if (!master || section < 0)
+		return -EINVAL;
+
+	if (!master->ooblayout || !master->ooblayout->free)
+		return -ENOTSUPP;
+
+	return master->ooblayout->free(master, section, oobfree);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_free);
+
+/**
+ * mtd_ooblayout_find_region - Find the region attached to a specific byte
+ * @mtd: mtd info structure
+ * @byte: the byte we are searching for
+ * @sectionp: pointer where the section id will be stored
+ * @oobregion: used to retrieve the ECC position
+ * @iter: iterator function. Should be either mtd_ooblayout_free or
+ *	  mtd_ooblayout_ecc depending on the region type you're searching for
+ *
+ * This function returns the section id and oobregion information of a
+ * specific byte. For example, say you want to know where the 4th ECC byte is
+ * stored, you'll use:
+ *
+ * mtd_ooblayout_find_region(mtd, 3, &section, &oobregion, mtd_ooblayout_ecc);
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte,
+				int *sectionp, struct mtd_oob_region *oobregion,
+				int (*iter)(struct mtd_info *,
+					    int section,
+					    struct mtd_oob_region *oobregion))
+{
+	int pos = 0, ret, section = 0;
+
+	memset(oobregion, 0, sizeof(*oobregion));
+
+	while (1) {
+		ret = iter(mtd, section, oobregion);
+		if (ret)
+			return ret;
+
+		if (pos + oobregion->length > byte)
+			break;
+
+		pos += oobregion->length;
+		section++;
+	}
+
+	/*
+	 * Adjust region info to make it start at the beginning at the
+	 * 'start' ECC byte.
+	 */
+	oobregion->offset += byte - pos;
+	oobregion->length -= byte - pos;
+	*sectionp = section;
+
+	return 0;
+}
+
+/**
+ * mtd_ooblayout_find_eccregion - Find the ECC region attached to a specific
+ *				  ECC byte
+ * @mtd: mtd info structure
+ * @eccbyte: the byte we are searching for
+ * @sectionp: pointer where the section id will be stored
+ * @oobregion: OOB region information
+ *
+ * Works like mtd_ooblayout_find_region() except it searches for a specific ECC
+ * byte.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
+				 int *section,
+				 struct mtd_oob_region *oobregion)
+{
+	return mtd_ooblayout_find_region(mtd, eccbyte, section, oobregion,
+					 mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_find_eccregion);
+
+/**
+ * mtd_ooblayout_get_bytes - Extract OOB bytes from the oob buffer
+ * @mtd: mtd info structure
+ * @buf: destination buffer to store OOB bytes
+ * @oobbuf: OOB buffer
+ * @start: first byte to retrieve
+ * @nbytes: number of bytes to retrieve
+ * @iter: section iterator
+ *
+ * Extract bytes attached to a specific category (ECC or free)
+ * from the OOB buffer and copy them into buf.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf,
+				const u8 *oobbuf, int start, int nbytes,
+				int (*iter)(struct mtd_info *,
+					    int section,
+					    struct mtd_oob_region *oobregion))
+{
+	struct mtd_oob_region oobregion;
+	int section, ret;
+
+	ret = mtd_ooblayout_find_region(mtd, start, &section,
+					&oobregion, iter);
+
+	while (!ret) {
+		int cnt;
+
+		cnt = min_t(int, nbytes, oobregion.length);
+		memcpy(buf, oobbuf + oobregion.offset, cnt);
+		buf += cnt;
+		nbytes -= cnt;
+
+		if (!nbytes)
+			break;
+
+		ret = iter(mtd, ++section, &oobregion);
+	}
+
+	return ret;
+}
+
+/**
+ * mtd_ooblayout_set_bytes - put OOB bytes into the oob buffer
+ * @mtd: mtd info structure
+ * @buf: source buffer to get OOB bytes from
+ * @oobbuf: OOB buffer
+ * @start: first OOB byte to set
+ * @nbytes: number of OOB bytes to set
+ * @iter: section iterator
+ *
+ * Fill the OOB buffer with data provided in buf. The category (ECC or free)
+ * is selected by passing the appropriate iterator.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf,
+				u8 *oobbuf, int start, int nbytes,
+				int (*iter)(struct mtd_info *,
+					    int section,
+					    struct mtd_oob_region *oobregion))
+{
+	struct mtd_oob_region oobregion;
+	int section, ret;
+
+	ret = mtd_ooblayout_find_region(mtd, start, &section,
+					&oobregion, iter);
+
+	while (!ret) {
+		int cnt;
+
+		cnt = min_t(int, nbytes, oobregion.length);
+		memcpy(oobbuf + oobregion.offset, buf, cnt);
+		buf += cnt;
+		nbytes -= cnt;
+
+		if (!nbytes)
+			break;
+
+		ret = iter(mtd, ++section, &oobregion);
+	}
+
+	return ret;
+}
+
+/**
+ * mtd_ooblayout_count_bytes - count the number of bytes in a OOB category
+ * @mtd: mtd info structure
+ * @iter: category iterator
+ *
+ * Count the number of bytes in a given category.
+ *
+ * Returns a positive value on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_count_bytes(struct mtd_info *mtd,
+				int (*iter)(struct mtd_info *,
+					    int section,
+					    struct mtd_oob_region *oobregion))
+{
+	struct mtd_oob_region oobregion;
+	int section = 0, ret, nbytes = 0;
+
+	while (1) {
+		ret = iter(mtd, section++, &oobregion);
+		if (ret) {
+			if (ret == -ERANGE)
+				ret = nbytes;
+			break;
+		}
+
+		nbytes += oobregion.length;
+	}
+
+	return ret;
+}
+
+/**
+ * mtd_ooblayout_get_eccbytes - extract ECC bytes from the oob buffer
+ * @mtd: mtd info structure
+ * @eccbuf: destination buffer to store ECC bytes
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to retrieve
+ * @nbytes: number of ECC bytes to retrieve
+ *
+ * Works like mtd_ooblayout_get_bytes(), except it acts on ECC bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf,
+			       const u8 *oobbuf, int start, int nbytes)
+{
+	return mtd_ooblayout_get_bytes(mtd, eccbuf, oobbuf, start, nbytes,
+				       mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_get_eccbytes);
+
+/**
+ * mtd_ooblayout_set_eccbytes - set ECC bytes into the oob buffer
+ * @mtd: mtd info structure
+ * @eccbuf: source buffer to get ECC bytes from
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to set
+ * @nbytes: number of ECC bytes to set
+ *
+ * Works like mtd_ooblayout_set_bytes(), except it acts on ECC bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf,
+			       u8 *oobbuf, int start, int nbytes)
+{
+	return mtd_ooblayout_set_bytes(mtd, eccbuf, oobbuf, start, nbytes,
+				       mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_set_eccbytes);
+
+/**
+ * mtd_ooblayout_get_databytes - extract data bytes from the oob buffer
+ * @mtd: mtd info structure
+ * @databuf: destination buffer to store ECC bytes
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to retrieve
+ * @nbytes: number of ECC bytes to retrieve
+ *
+ * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
+				const u8 *oobbuf, int start, int nbytes)
+{
+	return mtd_ooblayout_get_bytes(mtd, databuf, oobbuf, start, nbytes,
+				       mtd_ooblayout_free);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes);
+
+/**
+ * mtd_ooblayout_set_databytes - set data bytes into the oob buffer
+ * @mtd: mtd info structure
+ * @databuf: source buffer to get data bytes from
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to set
+ * @nbytes: number of ECC bytes to set
+ *
+ * Works like mtd_ooblayout_set_bytes(), except it acts on free bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
+				u8 *oobbuf, int start, int nbytes)
+{
+	return mtd_ooblayout_set_bytes(mtd, databuf, oobbuf, start, nbytes,
+				       mtd_ooblayout_free);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_set_databytes);
+
+/**
+ * mtd_ooblayout_count_freebytes - count the number of free bytes in OOB
+ * @mtd: mtd info structure
+ *
+ * Works like mtd_ooblayout_count_bytes(), except it count free bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_count_freebytes(struct mtd_info *mtd)
+{
+	return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_free);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes);
+
+/**
+ * mtd_ooblayout_count_eccbytes - count the number of ECC bytes in OOB
+ * @mtd: mtd info structure
+ *
+ * Works like mtd_ooblayout_count_bytes(), except it count ECC bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd)
+{
+	return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes);
+
+/*
+ * Method to access the protection register area, present in some flash
+ * devices. The user data is one time programmable but the factory data is read
+ * only.
+ */
+int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
+			   struct otp_info *buf)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	if (!master->_get_fact_prot_info)
+		return -EOPNOTSUPP;
+	if (!len)
+		return 0;
+	return master->_get_fact_prot_info(master, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info);
+
+int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
+			   size_t *retlen, u_char *buf)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	*retlen = 0;
+	if (!master->_read_fact_prot_reg)
+		return -EOPNOTSUPP;
+	if (!len)
+		return 0;
+	return master->_read_fact_prot_reg(master, from, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg);
+
+int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
+			   struct otp_info *buf)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	if (!master->_get_user_prot_info)
+		return -EOPNOTSUPP;
+	if (!len)
+		return 0;
+	return master->_get_user_prot_info(master, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_get_user_prot_info);
+
+int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
+			   size_t *retlen, u_char *buf)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	*retlen = 0;
+	if (!master->_read_user_prot_reg)
+		return -EOPNOTSUPP;
+	if (!len)
+		return 0;
+	return master->_read_user_prot_reg(master, from, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg);
+
+int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
+			    size_t *retlen, u_char *buf)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+	int ret;
+
+	*retlen = 0;
+	if (!master->_write_user_prot_reg)
+		return -EOPNOTSUPP;
+	if (!len)
+		return 0;
+	ret = master->_write_user_prot_reg(master, to, len, retlen, buf);
+	if (ret)
+		return ret;
+
+	/*
+	 * If no data could be written at all, we are out of memory and
+	 * must return -ENOSPC.
+	 */
+	return (*retlen) ? 0 : -ENOSPC;
+}
+EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg);
+
+int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	if (!master->_lock_user_prot_reg)
+		return -EOPNOTSUPP;
+	if (!len)
+		return 0;
+	return master->_lock_user_prot_reg(master, from, len);
+}
+EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg);
+
+/* Chip-supported device locking */
+int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	if (!master->_lock)
+		return -EOPNOTSUPP;
+	if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs)
+		return -EINVAL;
+	if (!len)
+		return 0;
+
+	if (mtd->flags & MTD_SLC_ON_MLC_EMULATION) {
+		ofs = (loff_t)mtd_div_by_eb(ofs, mtd) * master->erasesize;
+		len = (u64)mtd_div_by_eb(len, mtd) * master->erasesize;
+	}
+
+	return master->_lock(master, mtd_get_master_ofs(mtd, ofs), len);
+}
+EXPORT_SYMBOL_GPL(mtd_lock);
+
+int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	if (!master->_unlock)
+		return -EOPNOTSUPP;
+	if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs)
+		return -EINVAL;
+	if (!len)
+		return 0;
+
+	if (mtd->flags & MTD_SLC_ON_MLC_EMULATION) {
+		ofs = (loff_t)mtd_div_by_eb(ofs, mtd) * master->erasesize;
+		len = (u64)mtd_div_by_eb(len, mtd) * master->erasesize;
+	}
+
+	return master->_unlock(master, mtd_get_master_ofs(mtd, ofs), len);
+}
+EXPORT_SYMBOL_GPL(mtd_unlock);
+
+int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	if (!master->_is_locked)
+		return -EOPNOTSUPP;
+	if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs)
+		return -EINVAL;
+	if (!len)
+		return 0;
+
+	if (mtd->flags & MTD_SLC_ON_MLC_EMULATION) {
+		ofs = (loff_t)mtd_div_by_eb(ofs, mtd) * master->erasesize;
+		len = (u64)mtd_div_by_eb(len, mtd) * master->erasesize;
+	}
+
+	return master->_is_locked(master, mtd_get_master_ofs(mtd, ofs), len);
+}
+EXPORT_SYMBOL_GPL(mtd_is_locked);
+
+int mtd_block_isreserved(struct mtd_info *mtd, loff_t ofs)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	if (ofs < 0 || ofs >= mtd->size)
+		return -EINVAL;
+	if (!master->_block_isreserved)
+		return 0;
+
+	if (mtd->flags & MTD_SLC_ON_MLC_EMULATION)
+		ofs = (loff_t)mtd_div_by_eb(ofs, mtd) * master->erasesize;
+
+	return master->_block_isreserved(master, mtd_get_master_ofs(mtd, ofs));
+}
+EXPORT_SYMBOL_GPL(mtd_block_isreserved);
+
+int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	if (ofs < 0 || ofs >= mtd->size)
+		return -EINVAL;
+	if (!master->_block_isbad)
+		return 0;
+
+	if (mtd->flags & MTD_SLC_ON_MLC_EMULATION)
+		ofs = (loff_t)mtd_div_by_eb(ofs, mtd) * master->erasesize;
+
+	return master->_block_isbad(master, mtd_get_master_ofs(mtd, ofs));
+}
+EXPORT_SYMBOL_GPL(mtd_block_isbad);
+
+int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+	int ret;
+
+	if (!master->_block_markbad)
+		return -EOPNOTSUPP;
+	if (ofs < 0 || ofs >= mtd->size)
+		return -EINVAL;
+	if (!(mtd->flags & MTD_WRITEABLE))
+		return -EROFS;
+
+	if (mtd->flags & MTD_SLC_ON_MLC_EMULATION)
+		ofs = (loff_t)mtd_div_by_eb(ofs, mtd) * master->erasesize;
+
+	ret = master->_block_markbad(master, mtd_get_master_ofs(mtd, ofs));
+	if (ret)
+		return ret;
+
+	while (mtd->parent) {
+		mtd->ecc_stats.badblocks++;
+		mtd = mtd->parent;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtd_block_markbad);
+
+/*
+ * default_mtd_writev - the default writev method
+ * @mtd: mtd device description object pointer
+ * @vecs: the vectors to write
+ * @count: count of vectors in @vecs
+ * @to: the MTD device offset to write to
+ * @retlen: on exit contains the count of bytes written to the MTD device.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
+			      unsigned long count, loff_t to, size_t *retlen)
+{
+	unsigned long i;
+	size_t totlen = 0, thislen;
+	int ret = 0;
+
+	for (i = 0; i < count; i++) {
+		if (!vecs[i].iov_len)
+			continue;
+		ret = mtd_write(mtd, to, vecs[i].iov_len, &thislen,
+				vecs[i].iov_base);
+		totlen += thislen;
+		if (ret || thislen != vecs[i].iov_len)
+			break;
+		to += vecs[i].iov_len;
+	}
+	*retlen = totlen;
+	return ret;
+}
+
+/*
+ * mtd_writev - the vector-based MTD write method
+ * @mtd: mtd device description object pointer
+ * @vecs: the vectors to write
+ * @count: count of vectors in @vecs
+ * @to: the MTD device offset to write to
+ * @retlen: on exit contains the count of bytes written to the MTD device.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
+	       unsigned long count, loff_t to, size_t *retlen)
+{
+	struct mtd_info *master = mtd_get_master(mtd);
+
+	*retlen = 0;
+	if (!(mtd->flags & MTD_WRITEABLE))
+		return -EROFS;
+
+	if (!master->_writev)
+		return default_mtd_writev(mtd, vecs, count, to, retlen);
+
+	return master->_writev(master, vecs, count,
+			       mtd_get_master_ofs(mtd, to), retlen);
+}
+EXPORT_SYMBOL_GPL(mtd_writev);
+
+/**
+ * mtd_kmalloc_up_to - allocate a contiguous buffer up to the specified size
+ * @mtd: mtd device description object pointer
+ * @size: a pointer to the ideal or maximum size of the allocation, points
+ *        to the actual allocation size on success.
+ *
+ * This routine attempts to allocate a contiguous kernel buffer up to
+ * the specified size, backing off the size of the request exponentially
+ * until the request succeeds or until the allocation size falls below
+ * the system page size. This attempts to make sure it does not adversely
+ * impact system performance, so when allocating more than one page, we
+ * ask the memory allocator to avoid re-trying, swapping, writing back
+ * or performing I/O.
+ *
+ * Note, this function also makes sure that the allocated buffer is aligned to
+ * the MTD device's min. I/O unit, i.e. the "mtd->writesize" value.
+ *
+ * This is called, for example by mtd_{read,write} and jffs2_scan_medium,
+ * to handle smaller (i.e. degraded) buffer allocations under low- or
+ * fragmented-memory situations where such reduced allocations, from a
+ * requested ideal, are allowed.
+ *
+ * Returns a pointer to the allocated buffer on success; otherwise, NULL.
+ */
+void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size)
+{
+	gfp_t flags = __GFP_NOWARN | __GFP_DIRECT_RECLAIM | __GFP_NORETRY;
+	size_t min_alloc = max_t(size_t, mtd->writesize, PAGE_SIZE);
+	void *kbuf;
+
+	*size = min_t(size_t, *size, KMALLOC_MAX_SIZE);
+
+	while (*size > min_alloc) {
+		kbuf = kmalloc(*size, flags);
+		if (kbuf)
+			return kbuf;
+
+		*size >>= 1;
+		*size = ALIGN(*size, mtd->writesize);
+	}
+
+	/*
+	 * For the last resort allocation allow 'kmalloc()' to do all sorts of
+	 * things (write-back, dropping caches, etc) by using GFP_KERNEL.
+	 */
+	return kmalloc(*size, GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(mtd_kmalloc_up_to);
+
+#ifdef CONFIG_PROC_FS
+
+/*====================================================================*/
+/* Support for /proc/mtd */
+
+static int mtd_proc_show(struct seq_file *m, void *v)
+{
+	struct mtd_info *mtd;
+
+	seq_puts(m, "dev:    size   erasesize  name\n");
+	mutex_lock(&mtd_table_mutex);
+	mtd_for_each_device(mtd) {
+		seq_printf(m, "mtd%d: %8.8llx %8.8x \"%s\"\n",
+			   mtd->index, (unsigned long long)mtd->size,
+			   mtd->erasesize, mtd->name);
+	}
+	mutex_unlock(&mtd_table_mutex);
+	return 0;
+}
+#endif /* CONFIG_PROC_FS */
+
+/*====================================================================*/
+/* Init code */
+
+static struct backing_dev_info * __init mtd_bdi_init(char *name)
+{
+	struct backing_dev_info *bdi;
+	int ret;
+
+	bdi = bdi_alloc(NUMA_NO_NODE);
+	if (!bdi)
+		return ERR_PTR(-ENOMEM);
+	bdi->ra_pages = 0;
+	bdi->io_pages = 0;
+
+	/*
+	 * We put '-0' suffix to the name to get the same name format as we
+	 * used to get. Since this is called only once, we get a unique name. 
+	 */
+	ret = bdi_register(bdi, "%.28s-0", name);
+	if (ret)
+		bdi_put(bdi);
+
+	return ret ? ERR_PTR(ret) : bdi;
+}
+
+static struct proc_dir_entry *proc_mtd;
+
+static int __init init_mtd(void)
+{
+	int ret;
+
+	ret = class_register(&mtd_class);
+	if (ret)
+		goto err_reg;
+
+	mtd_bdi = mtd_bdi_init("mtd");
+	if (IS_ERR(mtd_bdi)) {
+		ret = PTR_ERR(mtd_bdi);
+		goto err_bdi;
+	}
+
+	proc_mtd = proc_create_single("mtd", 0, NULL, mtd_proc_show);
+
+	ret = init_mtdchar();
+	if (ret)
+		goto out_procfs;
+
+	dfs_dir_mtd = debugfs_create_dir("mtd", NULL);
+
+	return 0;
+
+out_procfs:
+	if (proc_mtd)
+		remove_proc_entry("mtd", NULL);
+	bdi_put(mtd_bdi);
+err_bdi:
+	class_unregister(&mtd_class);
+err_reg:
+	pr_err("Error registering mtd class or bdi: %d\n", ret);
+	return ret;
+}
+
+static void __exit cleanup_mtd(void)
+{
+	debugfs_remove_recursive(dfs_dir_mtd);
+	cleanup_mtdchar();
+	if (proc_mtd)
+		remove_proc_entry("mtd", NULL);
+	class_unregister(&mtd_class);
+	bdi_put(mtd_bdi);
+	idr_destroy(&mtd_idr);
+}
+
+module_init(init_mtd);
+module_exit(cleanup_mtd);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("Core MTD registration and access routines");
diff --git a/upstream/linux-5.10/drivers/net/ethernet/zte/zx29_gmac.c b/upstream/linux-5.10/drivers/net/ethernet/zte/zx29_gmac.c
new file mode 100755
index 0000000..668d9d9
--- /dev/null
+++ b/upstream/linux-5.10/drivers/net/ethernet/zte/zx29_gmac.c
@@ -0,0 +1,2063 @@
+/*

+ * Ethernet driver for zte zx2975xx gmac on chip network device

+ * (c)2008 http://www.zte.com.cn

+ * Authors:	zhang dongdong <zhang.dongdong16@zte.com.cn>

+ *

+ * 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.

+ */

+#include <linux/kernel.h>

+#include <linux/module.h>

+#include <linux/interrupt.h>

+#include <linux/types.h>

+#include <linux/delay.h>

+#include <linux/init.h>

+#include <linux/spinlock.h>

+#include <linux/netdevice.h>

+#include <linux/etherdevice.h>

+#include <linux/phy.h>

+#include <linux/platform_device.h>

+#include <linux/gmac/gmac.h>

+#include <linux/of.h>

+#include <linux/pinctrl/consumer.h>

+#include <linux/gpio.h>

+#include <linux/of_gpio.h>

+#include <linux/device.h>

+#include "zx29_gmac.h"

+

+#define gmac_printk(_format, _args...)		do{printk(KERN_INFO"gmac," _format "\n",##_args);}while(0)

+

+static u8 zx29_gmac_addr[MAC_ADDR_LENTH] = {0xec,0x1d,0x7f,0xb0,0x2f,0x32};

+static struct tasklet_struct *g_gmac_tasklet = NULL;

+/*struct zx29_gmac_dev	*g_gmac_dev = NULL; */ /* no use possible */

+extern void gmac_event_notify(GMAC_NOTIFY_EVENT notify_type, void* puf);

+extern int  gmac_event_init(const char *name);

+static void gmac_hw_deinit(struct net_device *dev);

+//extern void v7_dma_map_area(const void *, size_t, int);

+//extern unsigned long virt_to_phys_ap(unsigned long virt);

+extern void dma_map(const void *addr, size_t len, int flags);

+extern unsigned long virt_to_phys_ap_new(unsigned long virt_addr);

+extern void kobj_gmac_del(struct kobject *kobject);

+

+

+void dump_pkt_trace(unsigned char *data,int len)

+{

+	int i;

+	len = len > 128?128:len;

+    printk("********************\n");

+	for(i=0;i<len;i++){

+		printk("%.2x ",data[i]);

+		if((i&0xf) == 0xf)

+			printk("\n");

+	}

+	printk("\n");

+    printk("********************\n");

+}

+

+static u32 zx29_gmac_get_link(struct net_device *dev)

+{

+	struct zx29_gmac_dev* prv = (struct zx29_gmac_dev*)netdev_priv(dev);

+

+	return prv->link.isup;

+}

+

+

+static void gmac_start(void* io)

+{

+	volatile unsigned *gmac = (unsigned*)io;

+

+    mac_int_enable();

+    dma_enable();

+    mac_enable();

+}

+

+static inline int mod_sub(int left, int right, int mod)

+{

+    return (mod - right + left) % mod;

+}

+

+static struct bd_tx *get_txed_bd(struct net_device *ndev)

+{

+	struct bd_tx *d;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	int n = priv->txed_bd;

+

+	d = (struct bd_tx *)priv->dma_tx_vir;

+

+	if (n == priv->tx_bd_offset) 

+		return 0;

+

+	if (d[n].TDES0 & DMA_OWNER)

+		return 0;

+

+	if (d[n].skb == NULL)

+		return 0;

+

+	priv->txed_bd++;

+	priv->txed_bd %= GMAC_TX_BD_NUM;

+

+	return &d[n];

+}

+

+static inline struct bd_tx *get_tx_bd(struct net_device *ndev)

+{

+	struct zx29_gmac_dev* priv 	= (struct zx29_gmac_dev*)netdev_priv(ndev);

+    int   n						= priv->tx_bd_offset;

+    struct bd_tx *d				= (struct bd_tx*)priv->dma_tx_vir;

+	

+    if (mod_sub(priv->tx_bd_offset, priv->txed_bd, GMAC_TX_BD_NUM) > GMAC_TX_BD_NUM - 2)

+		return 0;

+		

+    if (d[n].TDES0 & DMA_OWNER) {

+        return 0;

+    } else {

+        return &d[n];

+    }

+}

+

+static struct bd_rx *get_rx_bd(struct net_device *dev)

+{

+    struct zx29_gmac_dev* prv 	= (struct zx29_gmac_dev*)netdev_priv(dev);

+    int   n						= prv->rx_bd_offset;

+    struct bd_rx *d				= (struct bd_rx*)prv->dma_rx_vir;

+

+    if(d[n].RDES0 & DMA_OWNER) 

+    {

+        return 0;

+    }

+    else	

+    {

+        return &d[n];

+    }

+}

+

+static void gmac_trig_transmit(void *io)

+{

+	volatile unsigned *gmac = (unsigned *)io;

+	register unsigned status = ((MAC(0x1014) >> 20) & 0x07);

+	switch (status) {

+		case 0:

+			dma_enable();

+			break;

+		case 6:

+			dma_continue_tx();

+			break;

+		case 1:

+		case 2:

+		case 3:

+		case 4:

+		case 5:

+		case 7:

+		default:

+			break;

+						

+	}

+}

+

+static void gmac_trig_receive(void *io)

+{

+	volatile unsigned *gmac = (unsigned *)io;

+	register unsigned status = ((MAC(0x1014) >> 17) & 0x07);

+	switch (status) {

+		case 0:

+			dma_enable();

+			break;

+		case 4:

+			dma_continue_rx();

+			break;

+		default:

+			break;				

+	}

+}

+

+static inline void gmac_update_mac(struct net_device *ndev)

+{

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+	unsigned char *mac = (unsigned char*)ndev->dev_addr;

+

+	MAC(0x0044)	= mac[0] | mac[1] << 8 | mac[2] << 16 | mac[3] << 24;

+    MAC(0x0040) = mac[4] | mac[5] << 8;

+}

+

+static int zx29mii_read(struct mii_bus *bus, int phy_addr, int regnum)

+{

+    unsigned long flags;

+	struct zx29_gmac_dev *prv = (struct zx29_gmac_dev *)bus->priv;

+	volatile unsigned *gmac = (unsigned *)prv->base_addr;

+

+	unsigned val	= ( 1 << 0 					|		// busy 位

+					    0 << 1 					|		// R/W操作指示位

+					    (PHY_CLOCK << 2)		|		// 时钟位

+					    (regnum & 0x1F) << 6	|		// 寄存器

+					    (phy_addr & 0x1F) << 11);		// 物理芯片

+

+	spin_lock_irqsave(&prv->lock,flags);

+	while(mac_mii_is_busy());

+	MAC(0x0010) 	= val;

+	spin_unlock_irqrestore(&prv->lock,flags);

+

+	while(mac_mii_is_busy());

+

+	return (MAC(0x0014) & 0xFFFF);

+}

+

+static int zx29mii_write(struct mii_bus *bus, int phy_addr, int regnum, u16 value)

+{

+	struct zx29_gmac_dev *prv = (struct zx29_gmac_dev *)bus->priv;

+	volatile unsigned *gmac = (unsigned *)prv->base_addr;

+

+	unsigned val = ( 1 << 0 					|		// busy 位

+					 1 << 1 					|		// R/W操作指示位

+					 (PHY_CLOCK << 2)			|		// 时钟位

+					 (regnum & 0x1F) << 6	    |		// 寄存器

+					 (phy_addr & 0x1F) << 11);		    // 物理芯片

+

+	spin_lock_irq(&prv->lock);

+	while(mac_mii_is_busy());

+	MAC(0x0014) = value;

+	MAC(0x0010) = val;

+

+	spin_unlock_irq(&prv->lock);

+	

+	while(mac_mii_is_busy());

+

+	return 0;

+}

+

+static int zx29mii_reset(struct mii_bus *bus)

+{

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)bus->priv;

+	volatile unsigned *gmac = (unsigned *)priv->base_addr;

+

+	gmac_start((void *)priv->base_addr);

+	while (mac_mii_is_busy());

+	return 0;

+}

+

+static inline void zx29_gmac_set_macaddr(struct net_device *ndev)

+{

+    int i = 0;

+#if MAC_ADDR_SET

+    for (i = 0; i < MAC_ADDR_LENTH; i++)

+		ndev->dev_addr[i] = zx29_gmac_addr[i];

+

+	if (!is_valid_ether_addr(ndev->dev_addr))

+		random_ether_addr(ndev->dev_addr);

+#else

+	random_ether_addr(ndev->dev_addr);

+#endif

+}

+

+static void zx29_gmac_tx(struct net_device *ndev)

+{

+	register unsigned status;

+	struct net_device_stats s = ndev->stats;

+	struct bd_tx *tx = get_txed_bd(ndev);

+

+	

+	while (tx) {

+		status = tx->TDES0;

+

+		if (tx->TDES0 & ERR_TX_ES) {

+			s.tx_errors++;

+			if(status & ERR_TX_LC)		s.tx_carrier_errors++;

+            if(status & ERR_TX_NC)		s.tx_carrier_errors++;

+            if(status & ERR_TX_EC)		s.tx_window_errors++;

+            if(status & ERR_TX_LATECOL)	s.tx_window_errors++;

+            if(status & ERR_TX_UF)		s.tx_aborted_errors++;

+            if(status & ERR_TX_ED)		s.tx_aborted_errors++;

+            if(status & ERR_TX_JT)		s.tx_fifo_errors++;

+            if(status & ERR_TX_FF)		s.tx_fifo_errors++;

+

+			printk("%s, status=0x%x, err_cnt=%ld\n", __FUNCTION__,status, s.tx_errors);

+		}

+		dev_kfree_skb_any(tx->skb);

+		tx->skb = NULL;

+		tx = get_txed_bd(ndev);

+		

+	}

+    

+	if (netif_queue_stopped(ndev))

+		netif_wake_queue(ndev);	

+}

+

+static int zx29_gmac_rx(struct net_device *ndev)

+{

+	struct bd_rx 	*rx;

+	struct sk_buff	*skb;

+	struct sk_buff	*skb_new;

+	unsigned		len;

+	int   exhausted = 0;

+	

+	struct zx29_gmac_dev* priv = (struct zx29_gmac_dev*)netdev_priv(ndev);

+

+    rx  = get_rx_bd(ndev);

+

+    if(unlikely(!rx))	goto rcv_done;

+

+	while (rx) {

+		if ((rx->RDES0 & ERR_RX_ES) || (rx->RDES0 & ERR_RX_LE)) {

+			ndev->stats.rx_errors++;

+            if(rx->RDES0 & ERR_RX_LE)	ndev->stats.rx_length_errors++;

+            if(rx->RDES0 & ERR_RX_OE)	ndev->stats.rx_over_errors++;

+            if(rx->RDES0 & ERR_RX_IPC)	ndev->stats.rx_frame_errors++;

+            if(rx->RDES0 & ERR_RX_LC)	ndev->stats.rx_fifo_errors++;

+            if(rx->RDES0 & ERR_RX_CE)	ndev->stats.rx_crc_errors++;

+		} else {

+            		

+			len = ((rx->RDES0 >> 16) & 0x3FFF) - 4;

+            if(len  > (ETH_FRAME_LEN+8)) {

+                ndev->stats.rx_dropped++;

+                goto rx_bd_reset;

+            }

+

+            skb_new = netdev_alloc_skb(ndev, GMAC_FRAME_LEN + NET_IP_ALIGN);

+            if (unlikely(!skb_new)) {

+                ndev->stats.rx_dropped++;

+				exhausted++;

+            } else {

+				exhausted = 0;

+            	ndev->stats.rx_packets++;

+				ndev->stats.rx_bytes += len;

+				

+				dma_sync_single_for_cpu(&ndev->dev, rx->dma_buf, GMAC_FRAME_LEN, DMA_FROM_DEVICE);

+                skb				= rx->skb;

+                skb_put(skb, len); /*in fact , use for?*/

+				

+//				dump_pkt_trace(skb->data, len);

+                skb->protocol 		= eth_type_trans(skb, ndev);

+                netif_rx(skb);

+

+				skb_reserve(skb_new, NET_IP_ALIGN);

+//				rx->dma_buf = virt_to_phys_ap((unsigned long)skb_new->data);

+				rx->dma_buf = virt_to_phys_ap_new((unsigned long)skb_new->data);

+				if(rx->dma_buf == NULL)

+				rx->dma_buf = __pa((unsigned)skb_new->data);

+				rx->skb = skb_new;

+				wmb();

+				dma_sync_single_for_device(&ndev->dev, rx->dma_buf, GMAC_FRAME_LEN, DMA_TO_DEVICE);

+			}

+		}

+rx_bd_reset:

+		rx->RDES0 = rx->RDES0 | DMA_OWNER;

+		priv->rx_bd_offset++;

+		priv->rx_bd_offset %= GMAC_RX_BD_NUM;

+		wmb();

+

+		if (exhausted >= 10)

+			break;

+		gmac_trig_receive((void*)ndev->base_addr);

+		rx = get_rx_bd(ndev);

+	}

+

+rcv_done:

+    

+	gmac_trig_receive((void*)ndev->base_addr);

+

+	return (exhausted > 10);

+}

+

+

+#ifndef GMAC_NO_INT

+static irqreturn_t zx29_gmac_interrupt(int irq, void *dev_id)

+{

+	struct net_device *ndev = (struct net_device *)dev_id;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	volatile unsigned * gmac = (unsigned *)ndev->base_addr;

+

+	priv->int_event = MAC(0x1014);

+	MAC(0x1014) = priv->int_event;

+

+	mac_int_disable();

+	tasklet_schedule(&priv->tasklet);

+

+	return IRQ_HANDLED;

+}

+

+void zx29_gmac_tasklet(unsigned long dev_id)

+{

+	struct net_device *ndev = (struct net_device *)dev_id;

+	struct zx29_gmac_dev *prv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+	unsigned int events = prv->int_event;

+

+	do {

+		if (events & INT_ST_TX)

+			zx29_gmac_tx(ndev);

+

+		if (events & INT_ST_RX)

+			zx29_gmac_rx(ndev);

+

+		events = MAC(0x1014);

+		MAC(0x1014) = events;

+	} while (events & (INT_ST_TX | INT_ST_RX));

+

+	mac_int_enable();

+}

+

+#else 

+void zx29_gmac_tasklet(unsigned long dev_id)

+{

+	struct net_device *ndev = (struct net_device *)dev_id;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+	unsigned events = priv->int_event;

+	

+	do {

+		if (events & INT_ST_TX)

+			zx29_gmac_tx(ndev);

+		

+		if (events & INT_ST_RX) {

+			if (zx29_gmac_rx(ndev))

+				break;

+		}

+		events = MAC(0x1014);

+	    MAC(0x1014) = events;

+	} while (events & (INT_ST_RX | INT_ST_TX));

+}

+

+enum hrtimer_restart gmac_timer_callback(struct hrtimer *timer)

+{

+	unsigned long delay_in_us = GTIMER_INTERVAL;

+	ktime_t gmac_schdule_time = ktime_set(0, delay_in_us * 1000);

+	

+	hrtimer_forward_now(timer, gmac_schdule_time);

+	tasklet_schedule(g_gmac_tasklet);

+	return HRTIMER_RESTART;

+}

+#endif

+

+static inline void zx29_gmac_linkisup(struct net_device *dev, int isup)

+{

+	struct zx29_gmac_dev *priv = netdev_priv(dev);

+	struct phy_device *phydev = priv->phydev;

+

+	priv->link.duplex = phydev->duplex;

+	priv->link.giga = (phydev->speed == 100);

+	if (priv->link.speed != phydev->speed)

+			priv->link.speed = phydev->speed;

+

+

+	priv->link.isup = isup;

+	if (isup)

+		netif_carrier_on(dev);

+	phy_print_status(phydev);

+}

+

+static void zx29_gmac_adjust_link(struct net_device *dev)

+{

+	struct zx29_gmac_dev *priv = netdev_priv(dev);

+	struct phy_device *phydev = priv->phydev;

+	volatile unsigned *gmac = (unsigned *)dev->base_addr;

+

+	if (priv->link.isup &&

+		(!phydev->link ||

+		(priv->link.speed != phydev->speed) ||

+		(priv->link.duplex != phydev->duplex))) {

+			priv->link.isup = 0;

+			netif_tx_disable(dev);

+		    if (!phydev->link) {

+				netif_carrier_off(dev);

+				phy_print_status(phydev);

+			}

+		}

+

+		if (!priv->link.isup && phydev->link) {

+			if (priv->link.duplex != phydev->duplex) {

+				if (phydev->duplex)

+					mac_set_full_duplex_mode();

+				else

+					mac_set_half_duplex_mode();

+			}

+

+		if (priv->link.giga != (phydev->speed == 100)) {

+			if (phydev->speed == 100)

+				mac_set_speed_100m_mode();

+			else

+				mac_set_speed_10m_mode();

+		}

+			netif_wake_queue(dev);

+            zx29_gmac_linkisup(dev, 1);

+		}

+		

+}

+

+static inline int zx29_gmac_phy_start(struct net_device *dev)

+{

+	struct zx29_gmac_dev *priv = netdev_priv(dev);

+	struct phy_device *p = NULL;

+	struct mdio_device *mdio_dev = NULL;

+	int ret = 0;

+

+	//zw.wang Without phy, gmac's gpio output power is removed on 20240328 start

+	int i = 0;

+	for(i = 0;i <= 5;i++)

+	{

+		if (priv->nports == 1) {

+			p = phy_find_first(priv->mii.bus);

+		} else if (priv->rmii_port < PHY_MAX_ADDR) {

+			mdio_dev = priv->mii.bus->mdio_map[priv->rmii_port];

+			p = container_of(mdio_dev, struct phy_device, mdio);

+		}

+

+		if (!p) {

+			if(i == 5){

+				gpio_direction_output(priv->gpio_power[0], 0);

+#ifdef CONFIG_MDIO_C45

+				gpio_direction_output(priv->gpio_power[1], 0);

+#endif

+			}

+			else

+				continue;

+			printk("%s: no PHY found\n", dev->name);

+			return -ENODEV;

+		}

+		else

+			break;

+	}

+	//zw.wang Without phy, gmac's gpio output power is removed on 20240328 end

+

+	ret = phy_connect_direct(dev, p, zx29_gmac_adjust_link, PHY_INTERFACE_MODE_RMII);  /*  phy_start_machine */

+	  /* supported and advertising */

+	priv->phydev = p;

+	return 0;

+}

+

+

+static int gmac_init_rx_bd(struct net_device *ndev, struct zx29_gmac_dev *priv)

+{

+	struct sk_buff *skb = NULL;

+	struct bd_rx *rx = (struct bd_rx *)priv->dma_rx_vir;

+	int i = 0;

+    

+	priv->rx_bd_offset = 0;

+    

+	for (i = 0; i < GMAC_RX_BD_NUM; i++) {

+		skb = netdev_alloc_skb(ndev, GMAC_FRAME_LEN + NET_IP_ALIGN);

+		if (unlikely(!skb)) {

+			gmac_hw_deinit(ndev);

+			return -1;

+		}

+

+		skb_reserve(skb, NET_IP_ALIGN);

+

+		rx[i].RDES0 |= DMA_OWNER;  /* when to change? */

+		rx[i].RDES1 = 0;

+		rx[i].RDES1 = GMAC_FRAME_LEN | 1 << 14;

+		rx[i].dma_buf = __pa((unsigned)skb->data);  /* __pa ? */

+		rx[i].next = priv->dma_rx_phy + ((i + 1) << 5); /* why phy? */

+		rx[i].skb = skb;

+#if 0

+		if(i%4 != 0)

+		{

+		   rx[i].RDES1	|= 0x80000000;

+		}

+#endif

+		dma_sync_single_for_device(&ndev->dev, rx[i].dma_buf, GMAC_FRAME_LEN, DMA_TO_DEVICE);

+		

+	}

+	rx[GMAC_RX_BD_NUM - 1].next = priv->dma_rx_phy;

+	rx[GMAC_RX_BD_NUM - 1].RDES1 = GMAC_FRAME_LEN | 1 << 14 | 1 << 15;

+

+	return 0;

+}

+

+static void gmac_init_tx_bd(struct zx29_gmac_dev *priv)

+{

+	struct bd_tx *tx = (struct bd_tx *)priv->dma_tx_vir;

+	int i = 0;

+	priv->tx_bd_offset = 0;

+	priv->txed_bd = 0;

+

+	for (i = 0; i < GMAC_TX_BD_NUM; i++) {

+		tx[i].TDES0 = (1 << 20 | 1 << 30);

+		tx[i].TDES1 = GMAC_FRAME_LEN;

+		tx[i].next = priv->dma_tx_phy + ((i + 1) << 5);

+	}

+

+	tx[GMAC_TX_BD_NUM - 1].next = priv->dma_tx_phy;

+	tx[GMAC_TX_BD_NUM - 1].TDES0 = 1 << 20 | 1 << 21 | 1 << 30;

+	

+}

+

+static void gmac_stop(void *io)

+{

+	volatile unsigned *gmac = (unsigned *)io;

+

+	dma_disable();

+	mac_disable();

+	mac_int_disable();

+

+	dma_clear_tx_fifo();

+	dma_wait_tx_fifo_cleared();	

+}

+

+static void gmac_set_speed_duplex(struct net_device *ndev, int speed, int duplex)

+{

+	unsigned val;

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+

+	val = MAC(0x0000) | 1 << 11 | 1 << 14;

+	if (SPEED_10 == speed)

+		val &= ~(1 << 14);

+	if (DUPLEX_HALF == duplex) {

+		val &= (~(1 << 11));

+		val |= (1 << 16);

+	}

+	MAC(0x0000) = val;

+}

+

+static void mac_init(struct net_device *ndev)

+{

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+	unsigned int i = 0, j = 0, mac_rst = 0;

+	unsigned long long mac_time_start = 0;

+	unsigned long long mac_time_end = 0;

+

+	mac_provide_clock();

+#ifdef __DEAD_LOOP_POLL__

+	mac_reset();

+	mac_set_gmii_mode();

+	mac_wait_reset_finished();

+#else

+	mac_time_start = local_clock();

+	for (i = 0; i < MAC_RESET_NUM; i++) {

+		mac_reset();

+		mac_set_mii_mode();

+		for (j = 0; j < MAC_WAIT_TIME; j++) {

+//			printk(".");

+			if (!((MAC(0x1000)) & 1)) {

+				mac_time_end = local_clock();

+				printk("ok:time:%llu ns\n", mac_time_end - mac_time_start);

+				mac_rst = 1;

+				goto mac_reset_option;

+			}

+			udelay(100);

+		}

+	}

+    mac_time_end = local_clock();

+mac_reset_option:

+	if(!mac_rst)								

+		printk("gmac reset failed!time:%llu us\n", mac_time_end - mac_time_start);

+#endif

+	while(mac_mii_is_busy());

+}

+

+static void gmac_hw_deinit(struct net_device *ndev)

+{

+	int i;

+	struct bd_rx *rx_bd;

+	struct bd_tx *tx_bd;

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+

+	gmac_stop((void *)ndev->base_addr);

+

+	if (priv->dma_rx_phy) {

+		rx_bd = (struct bd_rx *)priv->dma_rx_vir;

+

+		for (i = 0; i < GMAC_RX_BD_NUM; i++) {

+			if (rx_bd[i].skb)

+				dev_kfree_skb_any(rx_bd[i].skb);

+		}

+

+		tx_bd = (struct bd_tx *)priv->dma_tx_vir;

+		

+		for (i = 0; i < GMAC_TX_BD_NUM; i++) {

+			if (tx_bd[i].skb)

+				dev_kfree_skb_any(tx_bd[i].skb);

+		}

+	}

+

+	dma_set_tx_buffer(0);   //设置首个BD的缓冲区为0;

+    dma_set_rx_buffer(0);

+

+    priv->rx_bd_offset	= 0;

+    priv->tx_bd_offset	= 0;

+    priv->txed_bd		= 0;

+    priv->dma_rx_phy	= 0;

+    priv->dma_rx_vir	= 0;

+    priv->dma_tx_phy	= 0;

+    priv->dma_tx_vir	= 0;

+		

+}

+

+static int gmac_hw_init(struct net_device *ndev)

+{

+	int ret = -1;

+	unsigned val;

+	

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+

+	if (priv->dma_rx_phy)

+		gmac_hw_deinit(ndev);

+

+	priv->dma_rx_vir = priv->dma_rx_vir_init;

+	priv->dma_rx_phy = priv->dma_rx_phy_init;

+    priv->dma_tx_vir	= priv->dma_rx_vir + GMAC_RX_BUF_LEN;

+    priv->dma_tx_phy	= priv->dma_rx_phy + GMAC_RX_BUF_LEN;   /* ifconfig up, clear fifo*/

+

+	memset(priv->dma_rx_vir, 0, GMAC_BUF_LEN);

+

+	ret = gmac_init_rx_bd(ndev, priv);

+	if (ret < 0) {

+		printk("hw_net_init,init_rx_bd fail\n");

+		return ret;

+	}

+	gmac_init_tx_bd(priv);

+

+	mac_init(ndev); 

+

+	dma_disable();

+	mac_disable();

+	mac_int_disable();

+

+	val = MAC(0x1000);

+	val &= ~(0x3F << 8);

+	val |= (0x10 << 8);

+	MAC(0x1000) = val;

+

+	dma_set_rx_buffer(priv->dma_rx_phy);

+	dma_set_tx_buffer(priv->dma_tx_phy);

+

+	mac_int_clear(0x0001FFFF);

+	while (mac_mii_is_busy());

+

+	gmac_set_speed_duplex(ndev, priv->phydev->speed, priv->phydev->duplex);

+

+	mac_rece_all_data();

+

+	gmac_start((void *)ndev->base_addr);

+	return 0;

+}

+

+static int zx29_gmac_open(struct net_device *ndev)

+{

+	struct zx29_gmac_dev *priv = netdev_priv(ndev);

+	unsigned long flags;

+	int ret;

+	int err = 0;

+#ifdef GMAC_NO_INT

+	unsigned long delay_in_us = GTIMER_INTERVAL;

+	ktime_t gmac_schdule_time;

+#endif

+	err = phy_read_status(priv->phydev);  /*interal, phy drv provide*/

+	if (err < 0)

+		return err;

+

+	spin_lock_irqsave(&priv->lock, flags);

+	priv->link.speed = 0;

+

+	zx29_gmac_linkisup(ndev, priv->phydev->link);

+		

+	ret = gmac_hw_init(ndev);

+	if(ret) {

+		spin_unlock_irqrestore(&priv->lock, flags);

+		return ret;

+	}

+

+	netif_carrier_on(ndev);

+	spin_unlock_irqrestore(&priv->lock, flags);

+	

+	phy_start(priv->phydev);

+		

+	netif_start_queue(ndev);

+	

+#ifdef GMAC_NO_INT

+	gmac_schdule_time = ktime_set(0, delay_in_us * 1000);

+	if (priv->timer)

+		hrtimer_start(priv->timer, gmac_schdule_time, HRTIMER_MODE_REL);

+#endif

+

+	priv->stopped = 0;

+

+	printk("TSP zx29 gmac net open\n");

+

+	return 0;

+}

+

+static int zx29_gmac_stop(struct net_device *ndev)

+{

+	unsigned long flags = 0;

+	int ret = 0;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+

+	if (!priv->stopped) {

+		spin_lock_irqsave(&priv->lock, flags);

+#ifdef GMAC_NO_INT

+		ret = hrtimer_cancel(priv->timer);

+		if (ret < 0) {

+			BUG_ON(1);

+			spin_unlock_irqrestore(&priv->lock, flags);

+			return ret;

+		}

+#endif

+

+		priv->stopped = 1;

+		netif_stop_queue(ndev);

+		netif_carrier_off(ndev);

+		phy_stop(priv->phydev);

+		gmac_hw_deinit(ndev);

+

+		memset(&ndev->stats, 0, sizeof(struct net_device_stats));

+		spin_unlock_irqrestore(&priv->lock, flags);

+		printk("TSP zx29 gmac net stop\n");

+		}

+	return 0;

+}

+

+

+static netdev_tx_t zx29_gmac_start_xmit(struct sk_buff *skb, struct net_device *ndev)

+{

+	unsigned long flags;

+	unsigned len;

+	struct sk_buff *skb_old;

+	struct bd_tx *tx;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	

+	if (0 == priv->link.isup) {

+		dev_kfree_skb_any(skb);

+		printk("TSP zx29 gmac xmit  phy not link\n");	 

+		return NETDEV_TX_OK;  /* ? */

+	}

+

+	spin_lock_irqsave(&priv->lock, flags);

+	if(priv->stopped)

+	{

+		spin_unlock_irqrestore(&priv->lock,flags);

+		dev_kfree_skb_any(skb);

+		

+		printk("zx_net_start_xmit when stopped\n");

+		

+		return NETDEV_TX_OK;

+	}

+

+	tx = get_tx_bd(ndev);

+

+	if (!tx) {

+		spin_unlock_irqrestore(&priv->lock,flags);

+		dev_kfree_skb_any(skb);

+		return NETDEV_TX_OK;

+	}

+

+	priv->tx_bd_offset++;

+	priv->tx_bd_offset %= GMAC_TX_BD_NUM;

+	spin_unlock_irqrestore(&priv->lock, flags);

+

+	if(skb->len > ETH_FRAME_LEN + 4)	/* why 4*/

+		printk("TSP zx29 gmac start xmit len too long\n");

+ 

+//	v7_dma_map_area(skb->data, skb->len, DMA_TO_DEVICE);

+	dma_map(skb->data, skb->len, DMA_TO_DEVICE);

+	if (NULL == skb)

+		BUG_ON(1);

+

+	len = MIN(skb->len, GMAC_FRAME_LEN - NET_IP_ALIGN);

+	

+	tx->TDES0 |= (0x07 << 28);

+//	tx->dma_buf = virt_to_phys_ap((unsigned long)skb->data);

+	tx->dma_buf = virt_to_phys_ap_new((unsigned long)skb->data);

+

+	if(tx->dma_buf == NULL)

+	tx->dma_buf = virt_to_phys((unsigned)skb->data);

+	tx->skb = skb;

+

+	tx->TDES1 = len;

+	tx->TDES0 |= DMA_OWNER;

+

+	wmb();

+	ndev->stats.tx_bytes 		+= len;

+	ndev->stats.tx_packets++;

+/*	ndev->trans_start			= jiffies; */

+

+    

+	gmac_trig_transmit((void*)ndev->base_addr);

+//    dump_pkt_trace(skb->data, len);

+//	printk("[%s]\n", __func__);

+

+	return NETDEV_TX_OK;

+}

+

+static void zx29_gmac_tx_timeout(struct net_device *ndev, unsigned int txqueue)

+{

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+	genphy_update_link(priv->phydev);

+#endif

+	priv->link.isup = priv->phydev->link;

+

+	if (0 == priv->link.isup) {

+		printk("TSP zx29 gmac net timeout phy not link\n");						// PHY 未连接

+		netif_stop_queue(ndev);

+		netif_carrier_off(ndev);

+	} else {

+		printk("TSP zx29 gmac net timeout phy linked\n"); 

+		gmac_trig_transmit(ndev);

+		gmac_trig_receive(ndev);

+

+		netif_carrier_on(ndev);

+		netif_wake_queue(ndev);

+/*		ndev->trans_start		= jiffies; */ /* modify */

+		ndev->stats.tx_errors++;

+		ndev->stats.tx_dropped++;

+	}

+}

+

+void __iomem *base_clk = NULL;

+void __iomem *base_phy_release = NULL;

+

+static int zx29_gmac_phy_disable(struct device *dev)

+{	

+	struct platform_device *pdev	= to_platform_device(dev);

+	struct net_device	*ndev		= platform_get_drvdata(pdev);

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	volatile unsigned int *gmac = NULL;

+	gmac = (unsigned *)ndev->base_addr;

+	

+	enum of_gpio_flags flags;

+    unsigned long flag;

+	int gpio = 0;

+	int ret = 0;

+	

+#ifndef CONFIG_BOOT_WITHOUT_LOCK 	

+	if (ndev && !priv->stopped) {

+		if (netif_running(ndev)) {

+			

+			spin_lock_irqsave(&priv->lock, flag);

+            netif_stop_queue(ndev);

+			netif_carrier_off(ndev);

+			priv->stopped = 1;

+

+#ifdef GMAC_NO_INT

+            hrtimer_cancel(priv->timer);

+#endif			

+			printk("[%s] netif_running\n", __func__);

+

+			gpio_direction_output(priv->gpio_power[0], 0);

+			

+			gmac_stop((void*)ndev->base_addr); 

+            spin_unlock_irqrestore(&priv->lock, flag);

+		

+//			netif_device_detach(ndev);

+		}

+        pm_relax(&pdev->dev);

+       // printk("[%s] sleep\n");

+    }

+#endif

+	//printk("[%s] exit\n", __func__);

+	return 0;

+}

+

+static int zx29_gmac_phy_enable(struct device *dev)

+{

+    struct platform_device *pdev	= to_platform_device(dev);

+	struct net_device *ndev 		= platform_get_drvdata(pdev);

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	volatile unsigned int *gmac = NULL;

+	void __iomem *base = NULL;

+	gmac = (unsigned *)ndev->base_addr;

+	enum of_gpio_flags flags;

+	int gpio = 0;

+	int ret = 0;

+	int status = 0;

+	int islink = 0;

+	unsigned int num= 0;	

+    unsigned long flag = 0;

+	

+#ifndef CONFIG_BOOT_WITHOUT_LOCK 

+	if(ndev && priv->stopped) {

+	    pm_stay_awake(&pdev->dev);

+		if( netif_running(ndev)) {			

+            printk("[%s] enter\n", __func__);

+			spin_lock_irqsave(&priv->lock, flag);

+			gpio_direction_output(priv->gpio_power[0], 1);

+

+			base = base_clk;

+			gmac_set_clk();

+

+			base = base_phy_release;

+			gmac_phy_release();

+

+		    mdelay(500); //icplus ping need

+

+            priv->phydev->drv->config_init(priv->phydev);

+			gmac_hw_init(ndev);

+

+			netif_carrier_on(ndev);						

+			netif_start_queue(ndev);

+	

+#ifdef GMAC_NO_INT	

+            hrtimer_start(priv->timer, ktime_set(0, GTIMER_INTERVAL * 1000), HRTIMER_MODE_REL);

+#endif 

+			priv->stopped = 0;

+            spin_unlock_irqrestore(&priv->lock, flag);

+            printk("[%s] enter\n", __func__);

+//			netif_device_attach(ndev);	  	

+		}

+    }

+#endif

+	return 0;

+}

+

+#define C45_READ 1

+#define C45_WRITE 0

+static int zx29_c22_2_c45(struct phy_device *phydev, int addr, u16 devad, u32 regnum, int rw, int write_val)

+{

+	int val = 0;

+	phy_lock_mdio_bus(phydev);

+	/* Write the desired MMD Devad */

+	__mdiobus_write(phydev->mdio.bus, addr, MII_MMD_CTRL, devad);

+	/* Write the desired MMD register address */

+	__mdiobus_write(phydev->mdio.bus, addr, MII_MMD_DATA, regnum);

+	/* Select the Function : DATA with no post increment */

+	__mdiobus_write(phydev->mdio.bus, addr, MII_MMD_CTRL,

+			devad | MII_MMD_CTRL_NOINCR);

+	/* Read the content of the MMD's selected register */

+	if (rw == C45_READ)

+		val = __mdiobus_read(phydev->mdio.bus, addr, MII_MMD_DATA);

+	else 

+		__mdiobus_write(phydev->mdio.bus, addr, MII_MMD_DATA, write_val);

+	phy_unlock_mdio_bus(phydev);

+

+	return val;

+}

+

+static int zx29_c45_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)

+{

+	struct mii_ioctl_data *mii_data = if_mii(ifr);

+	u16 val = mii_data->val_in;

+	bool change_autoneg = false;

+	int prtad, devad, reg_num;

+

+	switch (cmd) {

+	case SIOCGMIIREG:

+		prtad = mdio_phy_id_prtad(mii_data->phy_id);//phy id

+		devad = mdio_phy_id_devad(mii_data->phy_id);//dev id / mmd

+		reg_num = mii_data->reg_num;

+		mii_data->val_out = zx29_c22_2_c45(phydev, prtad, devad, reg_num, C45_READ, 0);	

+		return 0;

+

+	case SIOCSMIIREG:

+		prtad = mdio_phy_id_prtad(mii_data->phy_id);

+		devad = mdio_phy_id_devad(mii_data->phy_id);

+        reg_num = mii_data->reg_num;

+			

+		if (prtad == phydev->mdio.addr) {

+			switch (devad) {

+			case MII_BMCR:

+				if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) {

+					if (phydev->autoneg == AUTONEG_ENABLE)

+						change_autoneg = true;

+					phydev->autoneg = AUTONEG_DISABLE;

+					if (val & BMCR_FULLDPLX)

+						phydev->duplex = DUPLEX_FULL;

+					else

+						phydev->duplex = DUPLEX_HALF;

+					if (val & BMCR_SPEED1000)

+						phydev->speed = SPEED_1000;

+					else if (val & BMCR_SPEED100)

+						phydev->speed = SPEED_100;

+					else phydev->speed = SPEED_10;

+				}

+				else {

+					if (phydev->autoneg == AUTONEG_DISABLE)

+						change_autoneg = true;

+					phydev->autoneg = AUTONEG_ENABLE;

+				}

+				break;

+			case MII_ADVERTISE:

+				mii_adv_mod_linkmode_adv_t(phydev->advertising,

+							   val);

+				change_autoneg = true;

+				break;

+			case MII_CTRL1000:

+				mii_ctrl1000_mod_linkmode_adv_t(phydev->advertising,

+							        val);

+				change_autoneg = true;

+				break;

+			default:

+				/* do nothing */

+				break;

+			}

+		}

+

+		zx29_c22_2_c45(phydev, prtad, devad, reg_num, C45_WRITE, val);

+

+		if (prtad == phydev->mdio.addr &&

+		    devad == MII_BMCR &&

+		    val & BMCR_RESET)

+			return phy_init_hw(phydev);

+

+		if (change_autoneg)

+			return phy_start_aneg(phydev);

+

+		return 0;

+	default:

+		return -EOPNOTSUPP;

+	}

+}

+

+static int zx29_gmac_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)

+{

+	struct zx29_gmac_dev *priv = netdev_priv(ndev);

+	struct mii_ioctl_data *mii_data = if_mii(ifr);

+	int is_c45 = mdio_phy_id_is_c45(mii_data->phy_id);

+	

+	if (!(netif_running(ndev)))

+		return -EINVAL;

+	if (!priv->phydev)

+		return -EINVAL;

+	if (cmd == SIOCDISABLEPHY)

+		return zx29_gmac_phy_disable(ndev->dev.parent);

+

+	if (cmd == SIOCENABLEPHY)

+		return zx29_gmac_phy_enable(ndev->dev.parent);

+

+	if (is_c45)

+		return zx29_c45_mii_ioctl(priv->phydev, ifr, cmd);

+	

+	return phy_mii_ioctl(priv->phydev, ifr, cmd);

+}

+

+static int eth_change_mtu(struct net_device *ndev, int new_mtu)

+{

+	if (new_mtu < 68 || new_mtu > ETH_DATA_LEN)

+		return -EINVAL;

+	ndev->mtu = new_mtu;

+	return 0;

+}

+

+static int zx29_gmac_set_mac_address(struct net_device *ndev, void *p)

+{

+	int ret = eth_mac_addr(ndev, p);

+	if (!ret) {

+		gmac_update_mac(ndev);	

+		printk(" zx29 gmac set mac addr ok\n");

+	}

+	return ret;

+}

+

+

+static int zx29_gmac_suspend(struct device *dev)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+    unsigned long flag;

+

+    if(ndev) {

+    	if(netif_running(ndev)) {

+//    		netif_device_detach(ndev);

+//    		gmac_stop((void*)ndev->base_addr);

+#ifdef CONFIG_BOOT_WITHOUT_LOCK 

+        phy_stop(priv->phydev);

+		spin_lock_irqsave(&priv->lock, flag);

+		netif_stop_queue(ndev);

+		netif_carrier_off(ndev);

+		priv->stopped = 1;

+

+#ifdef GMAC_NO_INT

+		hrtimer_cancel(priv->timer);

+#endif

+		

+		printk("[%s] netif_running\n", __func__);

+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+		gpio_direction_output(priv->gpio_power[0], 0);

+		gmac_stop((void*)ndev->base_addr);

+#else

+		gmac_stop((void*)ndev->base_addr); 

+		gpio_direction_output(priv->gpio_power[0], 0);

+#endif

+		spin_unlock_irqrestore(&priv->lock, flag);

+

+#endif

+    	}

+    }

+#ifndef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+    pinctrl_pm_select_sleep_state(&pdev->dev);

+#endif

+    return 0;

+}

+

+static int zx29_gmac_resume(struct device *dev)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device *ndev 		= platform_get_drvdata(pdev);

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+

+	volatile unsigned int *gmac = NULL;

+	void __iomem *base = NULL;

+	gmac = (unsigned *)ndev->base_addr;

+    unsigned long flag = 0;

+	

+#ifndef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+    pinctrl_pm_select_default_state(&pdev->dev);

+#endif

+    if(ndev) {

+    	if(netif_running(ndev)) {

+//        	gmac_start((void*)ndev->base_addr);

+//        	netif_device_attach(ndev);

+//            zx29_gmac_phy_enable(dev);

+#ifdef CONFIG_BOOT_WITHOUT_LOCK 

+		printk("[%s] enter\n", __func__);

+		spin_lock_irqsave(&priv->lock, flag);

+		gpio_direction_output(priv->gpio_power[0], 1);

+

+		base = base_clk;

+		gmac_set_clk();

+

+		base = base_phy_release;

+		gmac_phy_release();

+

+		mdelay(500); //icplus ping need

+

+		priv->phydev->drv->config_init(priv->phydev);

+		gmac_hw_init(ndev);

+

+		netif_carrier_on(ndev); 	

+		

+        phy_start(priv->phydev);

+		netif_start_queue(ndev);

+

+#ifdef GMAC_NO_INT

+		hrtimer_start(priv->timer, ktime_set(0, GTIMER_INTERVAL * 1000), HRTIMER_MODE_REL);

+#endif

+

+		priv->stopped = 0;

+		spin_unlock_irqrestore(&priv->lock, flag);

+		printk("[%s] enter\n", __func__);

+#endif

+

+    	}

+    }

+    return 0;

+}

+

+static const struct ethtool_ops zx29_gmac_ethtool_ops = {

+	.get_link = zx29_gmac_get_link,

+	.get_link_ksettings     = phy_ethtool_get_link_ksettings,

+	.set_link_ksettings     = phy_ethtool_set_link_ksettings,

+	/* other func */

+};

+

+static const struct net_device_ops zx29_gmac_netdev_ops = {

+	.ndo_open = zx29_gmac_open,

+	.ndo_stop = zx29_gmac_stop,

+	.ndo_start_xmit = zx29_gmac_start_xmit,

+	.ndo_tx_timeout = zx29_gmac_tx_timeout,

+	.ndo_do_ioctl = zx29_gmac_ioctl,

+	.ndo_change_mtu = eth_change_mtu,

+	.ndo_validate_addr = eth_validate_addr,

+	.ndo_set_mac_address = zx29_gmac_set_mac_address,

+};

+

+

+

+ssize_t show_fun(struct device *dev, struct device_attribute *attr, char *buf)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    int status = 0;

+    volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+    printk("MAC(1000) :0x%x\n", MAC(0x1000));

+    printk("MAC(1004) :0x%x\n", MAC(0x1004));

+    printk("MAC(1008) :0x%x\n", MAC(0x1008));

+    printk("MAC(100c) :0x%x\n", MAC(0x100c));

+    printk("MAC(1010) :0x%x\n", MAC(0x1010));

+    printk("MAC(1014) int status:0x%x\n", MAC(0x1014));

+    printk("MAC(1018) :0x%x\n", MAC(0x1018));

+    printk("MAC(101c) :0x%x\n", MAC(0x101c));

+    printk("MAC(0000) :0x%x\n", MAC(0x0000));

+    printk("MAC(0004) :0x%x\n", MAC(0x0004));

+    printk("MAC(0010) :0x%x\n", MAC(0x0010));

+

+    status = mdiobus_read(priv->phydev->mdio.bus, 21, 1);

+    printk("phy status:0x%x\n", status);

+    status = mdiobus_read(priv->phydev->mdio.bus, 0, 1);

+    printk("phy status port0:0x%x\n", status);

+    status = mdiobus_read(priv->phydev->mdio.bus, 1, 1);

+    printk("phy status port1:0x%x\n", status);

+    status = mdiobus_read(priv->phydev->mdio.bus, 2, 1);

+    printk("phy status port2:0x%x\n", status);

+    status = mdiobus_read(priv->phydev->mdio.bus, 3, 1);

+    printk("phy status port3:0x%x\n", status);

+    status = mdiobus_read(priv->phydev->mdio.bus, 4, 1);

+    printk("phy status port4:0x%x\n", status);

+

+    status = mdiobus_read(priv->phydev->mdio.bus, 21, 20);

+    status |= 0x4;

+    mdiobus_write(priv->phydev->mdio.bus, 21, 20, status);

+

+    status = mdiobus_read(priv->phydev->mdio.bus, 21, 21);

+    printk("phy status loop port:0x%x\n", status);

+

+

+    return 0;

+}

+

+ssize_t store_fun(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    printk("[%s]", __func__);    

+    return 1;

+}

+

+

+ssize_t mdio_show(struct device *dev, struct device_attribute *attr, char *buf)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	int mmd = 0;

+	int reg = 0;

+	

+    mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+    mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+    mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+	printk("phyaddr:0x%x,devad:0x%x,reg:0x%x,val=0x%x\n", 

+		priv->phydev->mdio.addr,

+		mmd,

+		reg,

+		mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e));

+

+    return 0;

+}

+

+ssize_t mdio_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+    int ret = 0;

+	int mmd = 0;

+	int reg = 0;  

+	int rd_wt = 0;/* rd:0, wt:1 */

+	int val = 0;

+	char *kern_buf = NULL;

+

+	printk(KERN_INFO "%s input str=%s,nbytes=%d \n", __func__, buf, count);

+

+	ret = sscanf(buf, "%x,%x,%x,%x", &rd_wt, &mmd, &reg, &val);

+	if (ret < 4) {

+		printk(KERN_INFO "gmac: failed to read user buf, ret=%d, input 0x%x,0x%x,0x%x,0x%x\n",

+				ret, rd_wt, mmd, reg, val);

+		return count;

+	}

+

+	if (rd_wt !=0 && rd_wt !=1) {

+		printk("please input with format: rd_wt,devad,reg,val\n"

+			   "0:rd, 1:wt,  if rd, val default input 0\n");

+		return ret ? ret : count;

+	}

+

+	if (rd_wt == 0) {

+	    mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		printk("phyaddr:0x%x,devad:0x%x,reg:0x%x,val=0x%x\n", 

+			priv->phydev->mdio.addr,

+			mmd,

+			reg,

+			mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e));

+	}

+

+	if (rd_wt == 1) {

+	    mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+        mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, val);

+

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		printk("phyaddr:0x%x,devad:0x%x,reg:0x%x,val=0x%x\n", 

+			priv->phydev->mdio.addr,

+			mmd,

+			reg,

+			mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e));

+	}

+

+    return count;

+}

+

+

+ssize_t free_mdio_show(struct device *dev, struct device_attribute *attr, char *buf)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	int mmd = 0;

+	int reg = 0;

+	

+    mdiobus_write(priv->phydev->mdio.bus, 8, 0x0d, mmd);

+    mdiobus_write(priv->phydev->mdio.bus, 8, 0x0e, reg);

+    mdiobus_write(priv->phydev->mdio.bus, 8, 0x0d, 0x4000 | mmd);

+	printk("phyaddr:0x%x,devad:0x%x,reg:0x%x,val=0x%x\n", 

+		priv->phydev->mdio.addr,

+		mmd,

+		reg,

+		mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e));

+

+    return 0;

+}

+

+ssize_t free_mdio_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+    int ret = 0;

+	int mmd = 0;

+	int reg = 0;  

+	int rd_wt = 0;/* rd:0, wt:1 */

+	int val = 0;

+	int phy_addr = 8;

+	char *kern_buf = NULL;

+

+	printk(KERN_INFO "%s input str=%s,nbytes=%d \n", __func__, buf, count);

+

+	ret = sscanf(buf, "%x,%x,%x,%x,%x", &rd_wt, &phy_addr, &mmd, &reg, &val);

+	if (ret < 4) {

+		printk(KERN_INFO "gmac: failed to read user buf, ret=%d, input 0x%x,0x%x,0x%x,0x%x,0x%x\n",

+				ret, rd_wt, phy_addr, mmd, reg, val);

+		return count;

+	}

+

+	if (rd_wt !=0 && rd_wt !=1) {

+		printk("please input with format: rd_wt,phy_addr,devad,reg,val\n"

+			   "0:rd, 1:wt,  if rd, val default input 0\n");

+		return ret ? ret : count;

+	}

+

+	if (rd_wt == 0) {

+	    mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0d, 0x4000 | mmd);

+		printk("phyaddr:0x%x,devad:0x%x,reg:0x%x,val=0x%x\n", 

+			phy_addr,

+			mmd,

+			reg,

+			mdiobus_read(priv->phydev->mdio.bus, phy_addr, 0x0e));

+	}

+

+	if (rd_wt == 1) {

+	    mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0d, 0x4000 | mmd);

+        mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0e, val);

+

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0d, 0x4000 | mmd);

+		printk("phyaddr:0x%x,devad:0x%x,reg:0x%x,val=0x%x\n", 

+			phy_addr,

+			mmd,

+			reg,

+			mdiobus_read(priv->phydev->mdio.bus, phy_addr, 0x0e));

+	}

+

+    return count;

+}

+

+extern int debug_on;

+ssize_t debug_on_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+

+	int val = 0;

+	int ret;

+	ret = sscanf(buf, "%d", &val);

+	if (ret < 1) {

+		printk(KERN_INFO "gmac: failed to read user buf, ret=%d, input %d\n",

+				ret,val);

+		return count;

+	}

+	debug_on = val;	

+    return count;

+}

+

+ssize_t debug_on_show(struct device *dev, struct device_attribute *attr, char *buf)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	

+	if (debug_on) 

+		memcpy(buf, "on", 3);

+	else	

+		memcpy(buf, "off", 4);

+    return 0;

+}

+

+/*jb.qi add for gamc power down on 20231116 start*/

+

+extern int gmac_power = 1;

+int gmac_power_flag = 0;

+

+ssize_t gmac_power_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

+{

+    int val = 0;

+    int ret;

+    ret = sscanf(buf, "%d", &val);

+    if(ret < 1)

+    {

+        printk(KERN_INFO "gmac: failed ti read user buf, ret=%d, input %d\n", ret,val);

+        return count;

+    }

+    gmac_power = val;

+    gpio_direction_output(gmac_power_flag, val);

+    return count;

+}

+

+ssize_t gmac_power_show(struct device *dev, struct device_attribute *attr, char *buf)

+{

+    if(gmac_power)

+        memcpy(buf, "on",3);

+    else

+        memcpy(buf, "off", 4);

+

+    printk("gmac_power %s\n", buf);

+    return 0;

+

+}

+/*jb.qi add for gamc power down on 20231116 end */

+

+/*zw.wang add for switching the primary/secondary mode of gmac on 20240118 start*/

+static int mode_type = -1;

+static int enter_only_one = 0;

+

+ssize_t gmac_master_or_slave_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

+{

+	int mmd = 0;

+	int reg = 0;

+	int val = 0;

+	int ret;

+	struct platform_device *pdev	= to_platform_device(dev);

+	if(!pdev){

+		printk(KERN_ERR "%s : %s pdev : %x \n", __func__,  __LINE__, pdev);

+		return -1;

+	}

+	struct net_device 	*ndev		= platform_get_drvdata(pdev);

+	if(!ndev){

+		printk(KERN_ERR "%s : %s ndev : %x \n", __func__,  __LINE__, ndev);

+		return -1;

+	}

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	if(!priv){

+		printk(KERN_ERR "%s : %s priv : %x \n", __func__,  __LINE__, priv);

+		return -1;

+	}

+

+	///read mode_type

+	ret = sscanf(buf, "%d", &mode_type);

+	if (ret < 1) {

+		printk(KERN_ERR "Please enter the number 0-3 to enable the corresponding mode \n"

+				"Enter values in the non-0-3 range to get pattern description \n");

+		return count;

+	}

+

+	///Judgment model

+	if (mode_type < 0 || mode_type > 3) {

+		printk(KERN_DEBUG "Please enter the number range 0-3\n"

+				"0: Set the slave mode \n"

+				"1: Set the main mode \n"

+				"2: indicates setting SQI value view mode \n"

+				"3: Set the VCT value view mode \n"

+				"After the mode is set, the corresponding value can be obtained\n");

+		return ret ? ret : count;

+	}

+

+	///Set the Ethernet slave mode

+	if (mode_type == 0) {

+		mmd = 0x1;

+		reg = 0x834;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		val = mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e);

+

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, val & (~BIT(14)));

+	}

+	///Set the Ethernet master mode

+	else if (mode_type == 1) {

+		mmd = 0x1;

+		reg = 0x834;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		val = mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e);

+

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, val | BIT(14));

+	}

+	return count;

+}

+

+ssize_t gmac_master_or_slave_show(struct device *dev, struct device_attribute *attr, char *buf)

+{

+	int mmd = 0;

+	int reg = 0;

+	int val = 0;

+	int len = 0;

+	int ret;

+	struct platform_device *pdev	= to_platform_device(dev);

+	if(!pdev){

+		printk(KERN_ERR "%s : %s pdev : %x \n", __func__,  __LINE__, pdev);

+		return -1;

+	}

+	struct net_device 	*ndev		= platform_get_drvdata(pdev);

+	if(!ndev){

+		printk(KERN_ERR "%s : %s ndev : %x \n", __func__,  __LINE__, ndev);

+		return -1;

+	}

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	if(!priv){

+		printk(KERN_ERR "%s : %s priv : %x \n", __func__,  __LINE__, priv);

+		return -1;

+	}

+

+	///Reentrant prevention

+	if(enter_only_one == 1)

+	{

+		return 0;

+	}

+	enter_only_one = 1;

+

+	///Read the network master/slave

+	if (mode_type == 0 || mode_type == 1) {

+		mmd = 0x1;

+		reg = 0x834;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		val = mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e) & BIT(14);

+		if(val)

+			memcpy(buf, "Master\n",7);

+		else

+			memcpy(buf, "Slave\n", 6);

+

+		printk(KERN_DEBUG "mode_type %d - gmac_master_or_slave is %s\n", mode_type, buf);

+

+	}

+	///Obtain the cable quality SQI value

+	else if(mode_type == 2){

+		mmd = 0x1;

+		reg = 0x8B10;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		val = mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e);

+		sprintf(buf, "0x%x\n", val);

+		sprintf(buf, "SQI : 0x%x\n", val);

+		printk(KERN_DEBUG "mode_type %d - SQI is 0x%x", mode_type, val);

+

+	}

+	///Obtain short circuit, open circuit and normal connection of VCT

+	else if(mode_type == 3){

+		///--TDR Enable

+		mmd = 0x1;

+		reg = 0x8B00;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, BIT(14) | BIT(12));

+		msleep(10);

+		///--Read VCT

+		mmd = 0x1;

+		reg = 0x8B02;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		val = mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e);

+		printk(KERN_DEBUG "Open status: %s - Short status: %s\n",

+		 (val & BIT(1)) ? "Open" : "Normal",  (val & BIT(0)) ? "Short" : "Normal");

+		sprintf(buf, "Open status: %s\nShort status: %s\n",

+		 (val & BIT(1)) ? "Open" : "Normal",  (val & BIT(0)) ? "Short" : "Normal");

+		reg = 0x8B01;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		val = mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e);

+		sprintf(buf, "%sDistance status: 0x%x\n", buf, val);

+		printk(KERN_DEBUG "mode_type %d - Distance status is 0x%x\n", mode_type, val);

+

+		///--TDR Disable

+		mmd = 0x1;

+		reg = 0x8B00;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, 0);

+

+	}

+	///Get model help information

+	else{

+		sprintf(buf, "Please enter the number range 0-3\n"

+				"0: Set the slave mode \n"

+				"1: Set the main mode \n"

+				"2: indicates setting SQI value view mode \n"

+				"3: Set the VCT value view mode \n"

+				"After the mode is set, the corresponding value can be obtained\n");

+		printk(KERN_DEBUG "Please enter the number range 0-3\n"

+				"0: Set the slave mode \n"

+				"1: Set the main mode \n"

+				"2: indicates setting SQI value view mode \n"

+				"3: Set the VCT value view mode \n"

+				"After the mode is set, the corresponding value can be obtained\n");

+	}

+	enter_only_one = 0;

+	return strlen(buf);

+

+}

+

+/*zw.wang add for switching the primary/secondary mode of gmac on 20240118 end */

+

+static DEVICE_ATTR(gmac_test, 0664, show_fun, store_fun);

+static DEVICE_ATTR(mdio_test, 0664, mdio_show, mdio_store);

+static DEVICE_ATTR(free_mdio, 0664, free_mdio_show, free_mdio_store);

+static DEVICE_ATTR(debug_on, 0664, debug_on_show, debug_on_store);

+static DEVICE_ATTR(gmac_power, 0664, gmac_power_show, gmac_power_store);//jb.qi add for gamc power down on 20231116

+static DEVICE_ATTR(gmac_master_or_slave, 0664, gmac_master_or_slave_show, gmac_master_or_slave_store);//zw.wang add for switching the primary/secondary mode of gmac on 20240118

+

+static int zx29_gmac_probe(struct platform_device *pdev)

+{

+    struct zx29_gmac_dev *prv = NULL;

+	struct net_device *ndev = alloc_etherdev(sizeof(struct zx29_gmac_dev));

+	volatile unsigned int *gmac = NULL;

+	struct device_node *np = pdev->dev.of_node;

+	int ret = -1;

+	unsigned long i;

+	struct mii_bus *mb;

+	struct resource *iomem;

+    void __iomem *base = NULL;

+	struct pinctrl		  *pctrl;

+	struct pinctrl_state  *state0;

+	enum of_gpio_flags flags;

+	int gpio = 0;

+	char board_name[128] = {"init_failed"};

+

+	printk("[%s] #########zx29_gmac_probe begin.\n", __func__);

+	if (!ndev)

+		return -ENOMEM;

+

+    device_create_file(&pdev->dev, &dev_attr_gmac_test);

+	device_create_file(&pdev->dev, &dev_attr_mdio_test);

+	device_create_file(&pdev->dev, &dev_attr_free_mdio);

+	device_create_file(&pdev->dev, &dev_attr_debug_on);

+    device_create_file(&pdev->dev, &dev_attr_gmac_power);//jb.qi add for gamc power down on 20231116

+	device_create_file(&pdev->dev, &dev_attr_gmac_master_or_slave);//zw.wang add for switching the primary/secondary mode of gmac on 20240118

+

+	prv = netdev_priv(ndev);

+	memset(prv, 0, sizeof(*prv));

+	prv->stopped = 1;

+	

+    pctrl = devm_pinctrl_get(&pdev->dev);

+	if (IS_ERR(pctrl)) {

+		dev_warn(&pdev->dev, "Failed to get test pins");

+		pctrl = NULL;

+		goto errirq;

+	}

+

+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+	state0 = pinctrl_lookup_state(pctrl, "state0");

+#else

+	state0 = pinctrl_lookup_state(pctrl, "default");

+#endif

+	if (IS_ERR(state0)) {

+		dev_err(&pdev->dev, "TEST: missing state0\n");

+		goto pinctrl_init_end;

+	}	

+

+	if (pinctrl_select_state(pctrl, state0) < 0) {

+		dev_err(&pdev->dev, "setting state0 failed\n");

+		goto pinctrl_init_end;

+	}

+

+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+	prv->gpio_power[2] = of_get_gpio_flags(pdev->dev.of_node, 2, &flags);

+	ret = gpio_request(prv->gpio_power[2], "phy_power"); /* gpio 51 */

+	gpio_direction_output(prv->gpio_power[2], 1);

+	mdelay(15);

+#endif

+

+    prv->gpio_power[0] = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);

+	ret = gpio_request(prv->gpio_power[0], "gmac_power"); /* gpio 83/124 */

+	gpio_direction_output(prv->gpio_power[0], 1);

+	mdelay(15);

+#ifdef CONFIG_MDIO_C45 //zw.wang Customer chooses phy c22/c45 issues on 20240301

+	prv->gpio_power[1] = of_get_gpio_flags(pdev->dev.of_node, 1, &flags);

+	ret = gpio_request(prv->gpio_power[1], "phy_rst"); /* gpio 63 */

+	gpio_direction_output(prv->gpio_power[1], 0);

+	mdelay(10);

+	gpio_direction_output(prv->gpio_power[1], 1);

+	mdelay(15);

+#endif    

+

+	SET_NETDEV_DEV(ndev, &pdev->dev); //if not, will panic

+	

+	base = devm_platform_ioremap_resource(pdev, 0);

+	gmac_power_flag = prv->gpio_power[0];//jb.qi add for gamc power down on 20231116

+	ndev->base_addr = base;/*iomem->start;*/

+	if (!ndev->base_addr)

+		return -ENXIO;

+

+#ifndef GMAC_NO_INT

+    ndev->irq = platform_get_irq(pdev, 0);

+#endif

+	ndev->netdev_ops = &zx29_gmac_netdev_ops;

+	ndev->ethtool_ops = &zx29_gmac_ethtool_ops;

+

+	gmac = (unsigned *)ndev->base_addr;

+

+	dma_disable();

+	mac_disable();

+	mac_int_disable();

+	

+	spin_lock_init(&prv->lock);

+

+	

+/*	wake_lock_init(&prv->wake_lock, WAKE_LOCK_SUSPEND, "gmac_pm"); //what replace?

+    wake_lock(&prv->wake_lock); */ 

+	device_init_wakeup(&pdev->dev, true);

+

+

+	zx29_gmac_set_macaddr(ndev);

+

+#ifndef GMAC_NO_INT

+	ret = request_irq(ndev->irq, zx29_gmac_interrupt, 0, ndev->name, ndev);

+	if (ret) {

+		printk(KERN_ERR "irq request failed: %d\n", ndev->irq);

+		goto errirq;

+	}

+#endif

+

+	ret = register_netdev(ndev);

+	if (ret) {

+		printk(KERN_ERR "error registering device %s\n",

+			ndev->name);

+		goto errdev;

+	}

+

+	of_property_read_u32(np, "port-nums", &prv->nports); 

+	of_property_read_u32(np, "rmii-ports", &prv->rmii_port);

+	prv->base_addr = ndev->base_addr;

+

+	prv->netdev = ndev;

+

+	mb = mdiobus_alloc();

+	if (!mb) {

+		printk(KERN_ERR "error allocating mii bus\n");

+		goto errmii;

+	}

+	mb->name = "zx29_gmac_mii";

+	mb->read = zx29mii_read;

+	mb->write = zx29mii_write;

+	mb->reset = zx29mii_reset;

+	mb->priv = prv;

+	snprintf(mb->id, MII_BUS_ID_SIZE, "%s-%x", "zx29_gmac", 0);

+	of_property_read_u32(np, "port-mask", &mb->phy_mask);

+/*	mb->irq = &prv->mii.irq[0]; */

+	for (i = 0; i < PHY_MAX_ADDR; i++) {

+		int n = platform_get_irq(pdev, i + 1);  /* devtrrr modify */

+		 if (n < 0)

+		 	n = PHY_POLL;

+		 prv->mii.irq[i] = n;

+		 mb->irq[i] = n;

+	}

+    

+    base = devm_platform_ioremap_resource(pdev, 2);

+	gmac_set_clk();	

+	base_clk = base;

+

+	base = devm_platform_ioremap_resource(pdev, 1);

+	gmac_phy_release();	

+	base_phy_release = base;

+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+	mdelay(500);

+#else

+	mdelay(10); //zw.wang@20240306 modify. Here, the jl3103 is set as an example, and other phy peripherals need to be optimized according to different reset stability times

+#endif

+

+	ret = mdiobus_register(mb);

+	if (ret < 0) {

+        printk("[%s] mdiobus register failed!\n", __func__);

+		goto errmdioregister;

+	}

+		

+	prv->mii.bus = mb;

+	ret = zx29_gmac_phy_start(ndev);

+	if (ret)

+		goto errphystart;

+

+	if (!(prv->phydev->phy_id == 0x00000000 || prv->phydev->phy_id == 0xffffffff)) {

+#ifndef CONFIG_BOOT_WITHOUT_LOCK 

+		pm_stay_awake(&pdev->dev);

+#endif		

+        strcpy(board_name, "cpe");

+	

+		printk("[%s] phy id = 0x%x \n", __func__, prv->phydev->phy_id);

+		printk("set gmac wakelock!\n");

+	} else {	

+		strcpy(board_name, "mdl");

+	    netif_device_detach(ndev);

+    }

+    

+	platform_set_drvdata(pdev, ndev);

+

+	tasklet_init(&prv->tasklet, zx29_gmac_tasklet, (unsigned long)ndev);

+	g_gmac_tasklet = &prv->tasklet;

+

+    

+	prv->dma_rx_vir = dma_alloc_coherent(ndev->dev.parent, GMAC_BUF_LEN, &prv->dma_rx_phy, GFP_KERNEL);

+	if (!prv->dma_rx_vir) {             // null, ndev->dev.parent difference?

+		BUG_ON(1);

+		goto errphystart;

+	}

+    	

+	prv->dma_rx_phy_init = prv->dma_rx_phy;

+	prv->dma_rx_vir_init = prv->dma_rx_vir;

+	prv->dma_tx_phy = prv->dma_rx_phy + GMAC_RX_BUF_LEN;

+	prv->dma_tx_vir = prv->dma_rx_vir + GMAC_RX_BUF_LEN;

+

+#ifdef GMAC_NO_INT

+	sema_init(&prv->sem, 0);

+

+	prv->timer = kzalloc(sizeof(struct hrtimer), GFP_KERNEL);

+	if (!prv->timer) {

+		BUG_ON(1);

+		goto errmalloc;

+	}

+	hrtimer_init(prv->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);

+

+	prv->timer->function = gmac_timer_callback;

+#endif

+

+	gmac_event_init(board_name);

+/*	g_gmac_dev = prv; */ /* no use possible*/

+	

+    printk("[%s] probe end\n", __func__);

+	return 0;

+errmalloc:

+	dma_free_coherent(ndev->dev.parent, GMAC_BUF_LEN, &prv->dma_rx_vir, prv->dma_rx_phy);

+	

+	

+errphystart:

+    mdiobus_unregister(mb);

+	

+

+errmdioregister:

+    mdiobus_free(mb);

+	

+errmii:

+	unregister_netdev(ndev);

+

+errdev:

+#ifndef GMAC_NO_INT

+	free_irq(ndev->irq, ndev);

+#endif

+

+pinctrl_init_end:

+

+errirq:

+	free_netdev(ndev);

+

+	printk("#########zx29_gmac_probe fail.\n");

+	return ret;

+}

+

+static int zx29_gmac_remove(struct platform_device *pdev)

+{

+    struct net_device *ndev = platform_get_drvdata(pdev);

+	volatile unsigned *gmac = NULL;

+	if (ndev) {

+		struct zx29_gmac_dev *priv = netdev_priv(ndev);

+

+//	    gpio_direction_output(priv->gpio_power[0], 1);

+//	    msleep(500);

+		unregister_netdev(ndev);

+

+		phy_disconnect(priv->phydev);

+			

+	    kobj_gmac_del(NULL);

+		

+		mdiobus_unregister(priv->mii.bus);

+		mdiobus_free(priv->mii.bus);

+#ifndef GMAC_NO_INT

+		free_irq(ndev->irq, ndev);

+#endif

+		tasklet_disable(&priv->tasklet);

+		tasklet_kill(&priv->tasklet);

+

+		if (priv->dma_rx_vir)

+			dma_free_coherent(ndev->dev.parent, GMAC_BUF_LEN, priv->dma_rx_vir, priv->dma_rx_phy);

+

+		pm_relax(&pdev->dev);

+		free_netdev(ndev);

+	    platform_set_drvdata(pdev, NULL);

+

+#ifdef CONFIG_MDIO_C45 //zw.wang Customer chooses phy c22/c45 issues on 20240301

+		gpio_free(priv->gpio_power[1]);

+#endif

+		gpio_direction_output(priv->gpio_power[0], 0);

+        gpio_free(priv->gpio_power[0]);

+

+		device_remove_file(&pdev->dev, &dev_attr_gmac_test);

+	    device_remove_file(&pdev->dev, &dev_attr_mdio_test);

+	    device_remove_file(&pdev->dev, &dev_attr_free_mdio);

+	    device_remove_file(&pdev->dev, &dev_attr_debug_on);

+        device_remove_file(&pdev->dev, &dev_attr_gmac_power);//jb.qi add for gamc power down on 20231116

+		device_remove_file(&pdev->dev, &dev_attr_gmac_master_or_slave);//zw.wang add for switching the primary/secondary mode of gmac on 20240118

+	}

+	return 0;

+}

+

+static struct dev_pm_ops zx29_gmac_pm_ops = {

+	.suspend = zx29_gmac_suspend,

+	.resume  = zx29_gmac_resume,

+};

+

+static const struct of_device_id gmac_match_table[] = {

+    {.compatible = "zte, zx29_gmac",},

+};

+

+static struct platform_driver zx29_gmac_driver = {

+	.probe  = zx29_gmac_probe,

+	.remove = zx29_gmac_remove,

+	.driver = {

+		.name  = "zx29_gmac",

+		.owner = THIS_MODULE,

+		.pm    = &zx29_gmac_pm_ops,

+		.of_match_table = gmac_match_table,

+	},

+};

+

+static int __init zx29_gmac_init(void)

+{

+	return platform_driver_register(&zx29_gmac_driver);

+}

+

+static void __exit zx29_gmac_exit(void)

+{

+    printk("[%s] start exit!\n", __func__);

+	platform_driver_unregister(&zx29_gmac_driver);

+}

+

+module_init(zx29_gmac_init);

+module_exit(zx29_gmac_exit);

+

+MODULE_LICENSE("GPL");

+MODULE_DESCRIPTION("ZX29 on chip Ethernet driver");

+MODULE_AUTHOR("zhu jianlinag <zhu.jianliang@zte.com.cn>");

diff --git a/upstream/linux-5.10/drivers/net/ethernet/zte/zx29_gmac_event.c b/upstream/linux-5.10/drivers/net/ethernet/zte/zx29_gmac_event.c
new file mode 100755
index 0000000..750580b
--- /dev/null
+++ b/upstream/linux-5.10/drivers/net/ethernet/zte/zx29_gmac_event.c
@@ -0,0 +1,298 @@
+/*

+ * Ethernet driver for zte zx2975xx gmac on chip network device

+ * (c)2008 http://www.zte.com.cn

+ * Authors:	zhang dongdong <zhang.dongdong16@zte.com.cn>

+ *

+ * 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.

+ */

+#include <linux/kernel.h>

+#include <linux/module.h>

+#include <linux/interrupt.h>

+#include <linux/types.h>

+#include <linux/delay.h>

+#include <linux/init.h>

+#include <linux/spinlock.h>

+#include <linux/netdevice.h>

+#include <linux/etherdevice.h>

+#include <linux/phy.h>

+#include <linux/platform_device.h>

+#include <linux/gmac/gmac.h>

+#include "zx29_gmac.h"

+

+extern void v7_dma_map_area(const void *, size_t, int);

+extern unsigned long virt_to_phys_ap(unsigned long virt);

+

+void dma_map(const void * addr, size_t len, int flags)

+{

+    v7_dma_map_area(addr, len, flags);

+}

+EXPORT_SYMBOL(dma_map);

+

+unsigned long virt_to_phys_ap_new(unsigned long virt_addr)

+{

+    return virt_to_phys_ap(virt_addr);

+}

+EXPORT_SYMBOL(virt_to_phys_ap_new);

+

+int debug_on = 0;

+EXPORT_SYMBOL(debug_on);

+

+struct kset *kset_gmac;

+struct kobject *gmackobj = NULL;

+struct kobject *typekobj = NULL;

+char type[8] = { 0 };

+u32 zx29_gmac_plug_state[3] = {0}; /* 0:phy 1:sw_wan 2:sw_lan */

+

+static struct attribute gmac_phy_plug_attr = {

+	.name = "eth_phy_state",

+	.mode = S_IRWXUGO,

+};

+

+static struct attribute gmac_sw_wan_plug_attr = {

+	.name = "eth_sw_wan_state",

+	.mode = S_IRWXUGO,

+};

+

+static struct attribute gmac_sw_lan_plug_attr = {

+	.name = "eth_sw_lan_state",

+	.mode = S_IRWXUGO,

+};

+

+static struct attribute board_type = {

+	.name = "type",

+	.mode = S_IRWXUGO,

+};

+

+static struct attribute *gmac_status_attrs[] = {

+	&gmac_phy_plug_attr,

+	&gmac_sw_wan_plug_attr,

+	&gmac_sw_lan_plug_attr,

+    &board_type,

+	NULL,

+};

+

+ssize_t kobj_gmac_show(struct kobject *kobject,struct attribute *attr,char *buf)

+{

+    unsigned  link =0;

+

+	if(!strcmp(attr->name,"eth_phy_state")) {

+		if(zx29_gmac_plug_state[0] == 0)

+			sprintf(buf, "%s","0");

+		else

+			sprintf(buf, "%s","1");

+	} else if(!strcmp(attr->name,"eth_sw_wan_state")) {

+		if(zx29_gmac_plug_state[1] == 0)

+			sprintf(buf, "%s","0");

+		else

+			sprintf(buf, "%s","1");

+	} else if(!strcmp(attr->name,"eth_sw_lan_state")) {

+		if(zx29_gmac_plug_state[2] == 0)

+			sprintf(buf, "%s","0");

+		else

+			sprintf(buf, "%s","1");

+	} else if (!strcmp(attr->name,"type")) {

+			sprintf(buf, "%s", type);

+	} else {

+		printk("invalidate attr name.\n");

+	}

+	

+	return strlen(buf);

+}

+

+ssize_t kobj_gmac_store(struct kobject *kobject, struct attribute *attr, const char *buf, size_t size)

+{

+	unsigned int value = 0;

+	value = simple_strtoul(buf, NULL, 4);

+	printk("attrname: %s.\n", attr->name);

+	if (!strcmp(attr->name, "eth_phy_state")) {

+		zx29_gmac_plug_state[0] = value;

+	} else if (!strcmp(attr->name,"eth_sw_wan_state")) {

+		zx29_gmac_plug_state[1] = value;

+	} else if (!strcmp(attr->name,"eth_sw_lan_state")) {

+		zx29_gmac_plug_state[2] = value;

+	} else {

+		printk("invalidate attr name.\n");

+	}

+	return size;

+}

+

+static struct sysfs_ops obj_gmac_sysops = {

+	.show = kobj_gmac_show,

+	.store = kobj_gmac_store,

+};

+

+static void kobj_gmac_release(struct kobject *kobject)

+{

+	printk("[gmac kobj_test: release!]\n");

+}

+

+static void kobj_type_release(struct kobject *kobject)

+{

+	printk("[type kobj_test: release!]\n");

+}

+

+

+void kobj_gmac_del(struct kobject *kobject)

+{

+	kset_unregister(kset_gmac);

+	

+    kobject_uevent(typekobj, KOBJ_REMOVE);

+	kobject_del(typekobj);

+	kobject_put(typekobj);

+	kfree(typekobj);

+	

+	kobject_uevent(gmackobj, KOBJ_REMOVE);

+	kobject_del(gmackobj);

+	kobject_put(gmackobj);

+

+	kfree(gmackobj);

+	

+	printk("[gmac kobj_test: delete!]\n");

+}

+EXPORT_SYMBOL(kobj_gmac_del);

+

+static struct kobj_type gmacktype =

+{       .release = kobj_gmac_release,

+        .sysfs_ops = &obj_gmac_sysops,

+        .default_attrs = gmac_status_attrs,

+};

+

+static struct kobj_type typektype =

+{       .release = kobj_type_release,

+//        .sysfs_ops = &obj_gmac_sysops,

+//        .default_attrs = gmac_status_attrs,

+};

+

+

+static int kset_filter(struct kset *kset,struct kobject *kobj)

+{

+        printk("kset Filter: kobj %s.\n",kobj->name);

+        return 1;

+}

+

+static const char *kset_name(struct kset *kset,struct kobject *kobj)

+{    

+        static char buf[20];

+        printk("Name:  kobj %s.\n",kobj->name);

+        sprintf(buf,"%s","gmac");

+        return buf;

+}

+

+static int kset_uevent(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env)

+{

+        int i = 0;

+        printk("uevent: kobj %s.\n",kobj->name);

+        while (i < env->envp_idx) {

+        	printk("%s.\n",env->envp[i]);

+        	i++;

+		}

+		

+        return 0;

+}

+

+static struct kset_uevent_ops gmac_uevent_ops =

+{

+        .filter = kset_filter,

+        .name = kset_name,

+        .uevent = kset_uevent,

+};

+

+void gmac_event_notify(GMAC_NOTIFY_EVENT notify_type, void *puf)

+{

+	int rtv = -1;

+	enum kobject_action action = KOBJ_UNBIND;

+	char *envp_phy_ext[] = {"GMACEVENT=gmac_eth_phy",NULL};

+	char *envp_sw_wan_ext[] = {"GMACEVENT=gmac_eth_sw_wan",NULL};

+	char *envp_sw_lan_ext[] = {"GMACEVENT=gmac_eth_sw_lan",NULL};

+

+	switch (notify_type) {

+		case GMAC_ETH_PHY_PLUGIN:

+			printk("gmac eth phy plugin \n");

+			action = KOBJ_ADD;

+			zx29_gmac_plug_state[0] = 1;

+			if (gmackobj)

+				rtv = kobject_uevent_env(gmackobj, action, envp_phy_ext);

+			break;

+			

+		case GMAC_ETH_PHY_PLUGOUT:

+			printk("gmac eth phy plugout \n");

+			action = KOBJ_REMOVE;

+			zx29_gmac_plug_state[0] = 0;

+			if(gmackobj)

+				rtv = kobject_uevent_env(gmackobj, action,envp_phy_ext);

+			break;

+			

+		case GMAC_ETH_SW_WAN_PLUGIN:

+			printk("gmac eth switch wan plugin \n");

+			action = KOBJ_ADD;

+			zx29_gmac_plug_state[1] = 1;

+			if(gmackobj)

+				rtv = kobject_uevent_env(gmackobj, action,envp_sw_wan_ext);

+			break;

+			

+		case GMAC_ETH_SW_WAN_PLUGOUT:

+			printk("gmac eth switch wan plugout \n");

+			action = KOBJ_REMOVE;

+			zx29_gmac_plug_state[1] = 0;

+			if(gmackobj)

+				rtv = kobject_uevent_env(gmackobj, action,envp_sw_wan_ext);

+			break;

+			

+		case GMAC_ETH_SW_LAN_PLUGIN:

+			printk("gmac eth switch lan plugin \n");

+			action = KOBJ_ADD;

+			zx29_gmac_plug_state[2] = 1;

+			if(gmackobj)

+				rtv = kobject_uevent_env(gmackobj, action,envp_sw_lan_ext);

+			break;

+			

+		case GMAC_ETH_SW_LAN_PLUGOUT:

+			printk("gmac eth switch lan plugout \n");

+			action = KOBJ_REMOVE;

+			zx29_gmac_plug_state[2] = 0;

+			if(gmackobj)

+				rtv = kobject_uevent_env(gmackobj, action,envp_sw_lan_ext);

+			break;

+

+		default:

+			printk(KERN_WARNING "UNKWON GMAC EVENT \n");

+			break;

+	}

+

+	printk(KERN_WARNING "rtv:%d \n",rtv);

+}

+

+EXPORT_SYMBOL(gmac_event_notify);

+

+int gmac_event_init(const char *name)

+{

+	int ret = 0;

+	/* 创建并注册 kset_p */   

+	gmackobj = kzalloc(sizeof(*gmackobj),GFP_KERNEL);

+	if(!gmackobj){

+		printk(KERN_WARNING "mallock gmackobj failed \n");

+		return 0;

+	}

+	kset_gmac = kset_create_and_add("gmac", &gmac_uevent_ops, NULL); 

+	kobject_init(gmackobj, &gmacktype);

+	kobject_add(gmackobj,&kset_gmac->kobj,"%s","gmacconfig");  

+	gmackobj->kset = kset_gmac;

+

+	typekobj = kzalloc(sizeof(*typekobj),GFP_KERNEL);

+	if(!typekobj){

+		printk(KERN_WARNING "mallock gmackobj failed \n");

+		return 0;

+	}

+//	kset_gmac = kset_create_and_add("gmac", &gmac_uevent_ops, NULL); 

+	kobject_init(typekobj, &typektype);

+	kobject_add(typekobj,&kset_gmac->kobj,"%s",name);  

+	typekobj->kset = kset_gmac;

+

+	strcpy(type, name);

+	

+	return ret;

+}

+EXPORT_SYMBOL(gmac_event_init);

diff --git a/upstream/linux-5.10/drivers/net/phy/phy_device.c b/upstream/linux-5.10/drivers/net/phy/phy_device.c
new file mode 100755
index 0000000..d9b53ba
--- /dev/null
+++ b/upstream/linux-5.10/drivers/net/phy/phy_device.c
@@ -0,0 +1,3113 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Framework for finding and configuring PHYs.
+ * Also contains generic PHY driver
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitmap.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mdio.h>
+#include <linux/mii.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/phy_led_triggers.h>
+#include <linux/property.h>
+#include <linux/sfp.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/unistd.h>
+
+MODULE_DESCRIPTION("PHY library");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_basic_features);
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_t1_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_basic_t1_features);
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_gbit_features);
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_fibre_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_gbit_fibre_features);
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_all_ports_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_gbit_all_ports_features);
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_10gbit_features);
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_fec_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_10gbit_fec_features);
+
+const int phy_basic_ports_array[3] = {
+	ETHTOOL_LINK_MODE_Autoneg_BIT,
+	ETHTOOL_LINK_MODE_TP_BIT,
+	ETHTOOL_LINK_MODE_MII_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_basic_ports_array);
+
+const int phy_fibre_port_array[1] = {
+	ETHTOOL_LINK_MODE_FIBRE_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_fibre_port_array);
+
+const int phy_all_ports_features_array[7] = {
+	ETHTOOL_LINK_MODE_Autoneg_BIT,
+	ETHTOOL_LINK_MODE_TP_BIT,
+	ETHTOOL_LINK_MODE_MII_BIT,
+	ETHTOOL_LINK_MODE_FIBRE_BIT,
+	ETHTOOL_LINK_MODE_AUI_BIT,
+	ETHTOOL_LINK_MODE_BNC_BIT,
+	ETHTOOL_LINK_MODE_Backplane_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_all_ports_features_array);
+
+const int phy_10_100_features_array[4] = {
+	ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+	ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+	ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+	ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_10_100_features_array);
+
+const int phy_basic_t1_features_array[2] = {
+	ETHTOOL_LINK_MODE_TP_BIT,
+	ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_basic_t1_features_array);
+
+const int phy_gbit_features_array[2] = {
+	ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+	ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_gbit_features_array);
+
+const int phy_10gbit_features_array[1] = {
+	ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_10gbit_features_array);
+
+static const int phy_10gbit_fec_features_array[1] = {
+	ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
+};
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_10gbit_full_features);
+
+static const int phy_10gbit_full_features_array[] = {
+	ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+	ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+	ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+	ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+};
+
+//status = phy_read_cl(phydev, MII_BMSR);
+static int phy_read_cl(struct phy_device *phydev, u32 regnum)
+{
+	int val = 0;
+
+	mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, 0x0d, 1);
+	mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, 0x0e, regnum);
+	mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, 0x0d, 0x4000 | 1);
+	val = mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, 0x0e);
+
+	return val;
+}
+
+static void features_init(void)
+{
+	/* 10/100 half/full*/
+	linkmode_set_bit_array(phy_basic_ports_array,
+			       ARRAY_SIZE(phy_basic_ports_array),
+			       phy_basic_features);
+	linkmode_set_bit_array(phy_10_100_features_array,
+			       ARRAY_SIZE(phy_10_100_features_array),
+			       phy_basic_features);
+
+	/* 100 full, TP */
+	linkmode_set_bit_array(phy_basic_t1_features_array,
+			       ARRAY_SIZE(phy_basic_t1_features_array),
+			       phy_basic_t1_features);
+
+	/* 10/100 half/full + 1000 half/full */
+	linkmode_set_bit_array(phy_basic_ports_array,
+			       ARRAY_SIZE(phy_basic_ports_array),
+			       phy_gbit_features);
+	linkmode_set_bit_array(phy_10_100_features_array,
+			       ARRAY_SIZE(phy_10_100_features_array),
+			       phy_gbit_features);
+	linkmode_set_bit_array(phy_gbit_features_array,
+			       ARRAY_SIZE(phy_gbit_features_array),
+			       phy_gbit_features);
+
+	/* 10/100 half/full + 1000 half/full + fibre*/
+	linkmode_set_bit_array(phy_basic_ports_array,
+			       ARRAY_SIZE(phy_basic_ports_array),
+			       phy_gbit_fibre_features);
+	linkmode_set_bit_array(phy_10_100_features_array,
+			       ARRAY_SIZE(phy_10_100_features_array),
+			       phy_gbit_fibre_features);
+	linkmode_set_bit_array(phy_gbit_features_array,
+			       ARRAY_SIZE(phy_gbit_features_array),
+			       phy_gbit_fibre_features);
+	linkmode_set_bit_array(phy_fibre_port_array,
+			       ARRAY_SIZE(phy_fibre_port_array),
+			       phy_gbit_fibre_features);
+
+	/* 10/100 half/full + 1000 half/full + TP/MII/FIBRE/AUI/BNC/Backplane*/
+	linkmode_set_bit_array(phy_all_ports_features_array,
+			       ARRAY_SIZE(phy_all_ports_features_array),
+			       phy_gbit_all_ports_features);
+	linkmode_set_bit_array(phy_10_100_features_array,
+			       ARRAY_SIZE(phy_10_100_features_array),
+			       phy_gbit_all_ports_features);
+	linkmode_set_bit_array(phy_gbit_features_array,
+			       ARRAY_SIZE(phy_gbit_features_array),
+			       phy_gbit_all_ports_features);
+
+	/* 10/100 half/full + 1000 half/full + 10G full*/
+	linkmode_set_bit_array(phy_all_ports_features_array,
+			       ARRAY_SIZE(phy_all_ports_features_array),
+			       phy_10gbit_features);
+	linkmode_set_bit_array(phy_10_100_features_array,
+			       ARRAY_SIZE(phy_10_100_features_array),
+			       phy_10gbit_features);
+	linkmode_set_bit_array(phy_gbit_features_array,
+			       ARRAY_SIZE(phy_gbit_features_array),
+			       phy_10gbit_features);
+	linkmode_set_bit_array(phy_10gbit_features_array,
+			       ARRAY_SIZE(phy_10gbit_features_array),
+			       phy_10gbit_features);
+
+	/* 10/100/1000/10G full */
+	linkmode_set_bit_array(phy_all_ports_features_array,
+			       ARRAY_SIZE(phy_all_ports_features_array),
+			       phy_10gbit_full_features);
+	linkmode_set_bit_array(phy_10gbit_full_features_array,
+			       ARRAY_SIZE(phy_10gbit_full_features_array),
+			       phy_10gbit_full_features);
+	/* 10G FEC only */
+	linkmode_set_bit_array(phy_10gbit_fec_features_array,
+			       ARRAY_SIZE(phy_10gbit_fec_features_array),
+			       phy_10gbit_fec_features);
+}
+
+void phy_device_free(struct phy_device *phydev)
+{
+	put_device(&phydev->mdio.dev);
+}
+EXPORT_SYMBOL(phy_device_free);
+
+static void phy_mdio_device_free(struct mdio_device *mdiodev)
+{
+	struct phy_device *phydev;
+
+	phydev = container_of(mdiodev, struct phy_device, mdio);
+	phy_device_free(phydev);
+}
+
+static void phy_device_release(struct device *dev)
+{
+	kfree(to_phy_device(dev));
+}
+
+static void phy_mdio_device_remove(struct mdio_device *mdiodev)
+{
+	struct phy_device *phydev;
+
+	phydev = container_of(mdiodev, struct phy_device, mdio);
+	phy_device_remove(phydev);
+}
+
+static struct phy_driver genphy_driver;
+
+static LIST_HEAD(phy_fixup_list);
+static DEFINE_MUTEX(phy_fixup_lock);
+
+static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
+{
+	struct device_driver *drv = phydev->mdio.dev.driver;
+	struct phy_driver *phydrv = to_phy_driver(drv);
+	struct net_device *netdev = phydev->attached_dev;
+
+	if (!drv || !phydrv->suspend)
+		return false;
+
+	/* PHY not attached? May suspend if the PHY has not already been
+	 * suspended as part of a prior call to phy_disconnect() ->
+	 * phy_detach() -> phy_suspend() because the parent netdev might be the
+	 * MDIO bus driver and clock gated at this point.
+	 */
+	if (!netdev)
+		goto out;
+
+	if (netdev->wol_enabled)
+		return false;
+
+	/* As long as not all affected network drivers support the
+	 * wol_enabled flag, let's check for hints that WoL is enabled.
+	 * Don't suspend PHY if the attached netdev parent may wake up.
+	 * The parent may point to a PCI device, as in tg3 driver.
+	 */
+	if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))
+		return false;
+
+	/* Also don't suspend PHY if the netdev itself may wakeup. This
+	 * is the case for devices w/o underlaying pwr. mgmt. aware bus,
+	 * e.g. SoC devices.
+	 */
+	if (device_may_wakeup(&netdev->dev))
+		return false;
+
+out:
+	return !phydev->suspended;
+}
+
+static __maybe_unused int mdio_bus_phy_suspend(struct device *dev)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+
+	/* We must stop the state machine manually, otherwise it stops out of
+	 * control, possibly with the phydev->lock held. Upon resume, netdev
+	 * may call phy routines that try to grab the same lock, and that may
+	 * lead to a deadlock.
+	 */
+	if (phydev->attached_dev && phydev->adjust_link)
+		phy_stop_machine(phydev);
+
+	if (!mdio_bus_phy_may_suspend(phydev))
+		return 0;
+
+	phydev->suspended_by_mdio_bus = 1;
+
+	return phy_suspend(phydev);
+}
+
+static __maybe_unused int mdio_bus_phy_resume(struct device *dev)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	int ret;
+
+	if (!phydev->suspended_by_mdio_bus)
+		goto no_resume;
+
+	phydev->suspended_by_mdio_bus = 0;
+
+	ret = phy_init_hw(phydev);
+	if (ret < 0)
+		return ret;
+
+	ret = phy_resume(phydev);
+	if (ret < 0)
+		return ret;
+no_resume:
+	if (phydev->attached_dev && phydev->adjust_link)
+		phy_start_machine(phydev);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mdio_bus_phy_pm_ops, mdio_bus_phy_suspend,
+			 mdio_bus_phy_resume);
+
+/**
+ * phy_register_fixup - creates a new phy_fixup and adds it to the list
+ * @bus_id: A string which matches phydev->mdio.dev.bus_id (or PHY_ANY_ID)
+ * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY)
+ *	It can also be PHY_ANY_UID
+ * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before
+ *	comparison
+ * @run: The actual code to be run when a matching PHY is found
+ */
+int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask,
+		       int (*run)(struct phy_device *))
+{
+	struct phy_fixup *fixup = kzalloc(sizeof(*fixup), GFP_KERNEL);
+
+	if (!fixup)
+		return -ENOMEM;
+
+	strlcpy(fixup->bus_id, bus_id, sizeof(fixup->bus_id));
+	fixup->phy_uid = phy_uid;
+	fixup->phy_uid_mask = phy_uid_mask;
+	fixup->run = run;
+
+	mutex_lock(&phy_fixup_lock);
+	list_add_tail(&fixup->list, &phy_fixup_list);
+	mutex_unlock(&phy_fixup_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_register_fixup);
+
+/* Registers a fixup to be run on any PHY with the UID in phy_uid */
+int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
+			       int (*run)(struct phy_device *))
+{
+	return phy_register_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask, run);
+}
+EXPORT_SYMBOL(phy_register_fixup_for_uid);
+
+/* Registers a fixup to be run on the PHY with id string bus_id */
+int phy_register_fixup_for_id(const char *bus_id,
+			      int (*run)(struct phy_device *))
+{
+	return phy_register_fixup(bus_id, PHY_ANY_UID, 0xffffffff, run);
+}
+EXPORT_SYMBOL(phy_register_fixup_for_id);
+
+/**
+ * phy_unregister_fixup - remove a phy_fixup from the list
+ * @bus_id: A string matches fixup->bus_id (or PHY_ANY_ID) in phy_fixup_list
+ * @phy_uid: A phy id matches fixup->phy_id (or PHY_ANY_UID) in phy_fixup_list
+ * @phy_uid_mask: Applied to phy_uid and fixup->phy_uid before comparison
+ */
+int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask)
+{
+	struct list_head *pos, *n;
+	struct phy_fixup *fixup;
+	int ret;
+
+	ret = -ENODEV;
+
+	mutex_lock(&phy_fixup_lock);
+	list_for_each_safe(pos, n, &phy_fixup_list) {
+		fixup = list_entry(pos, struct phy_fixup, list);
+
+		if ((!strcmp(fixup->bus_id, bus_id)) &&
+		    ((fixup->phy_uid & phy_uid_mask) ==
+		     (phy_uid & phy_uid_mask))) {
+			list_del(&fixup->list);
+			kfree(fixup);
+			ret = 0;
+			break;
+		}
+	}
+	mutex_unlock(&phy_fixup_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(phy_unregister_fixup);
+
+/* Unregisters a fixup of any PHY with the UID in phy_uid */
+int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask)
+{
+	return phy_unregister_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask);
+}
+EXPORT_SYMBOL(phy_unregister_fixup_for_uid);
+
+/* Unregisters a fixup of the PHY with id string bus_id */
+int phy_unregister_fixup_for_id(const char *bus_id)
+{
+	return phy_unregister_fixup(bus_id, PHY_ANY_UID, 0xffffffff);
+}
+EXPORT_SYMBOL(phy_unregister_fixup_for_id);
+
+/* Returns 1 if fixup matches phydev in bus_id and phy_uid.
+ * Fixups can be set to match any in one or more fields.
+ */
+static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup)
+{
+	if (strcmp(fixup->bus_id, phydev_name(phydev)) != 0)
+		if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0)
+			return 0;
+
+	if ((fixup->phy_uid & fixup->phy_uid_mask) !=
+	    (phydev->phy_id & fixup->phy_uid_mask))
+		if (fixup->phy_uid != PHY_ANY_UID)
+			return 0;
+
+	return 1;
+}
+
+/* Runs any matching fixups for this phydev */
+static int phy_scan_fixups(struct phy_device *phydev)
+{
+	struct phy_fixup *fixup;
+
+	mutex_lock(&phy_fixup_lock);
+	list_for_each_entry(fixup, &phy_fixup_list, list) {
+		if (phy_needs_fixup(phydev, fixup)) {
+			int err = fixup->run(phydev);
+
+			if (err < 0) {
+				mutex_unlock(&phy_fixup_lock);
+				return err;
+			}
+			phydev->has_fixups = true;
+		}
+	}
+	mutex_unlock(&phy_fixup_lock);
+
+	return 0;
+}
+
+static int phy_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	struct phy_driver *phydrv = to_phy_driver(drv);
+	const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
+	int i;
+
+	if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
+		return 0;
+
+	if (phydrv->match_phy_device)
+		return phydrv->match_phy_device(phydev);
+
+	if (phydev->is_c45) {
+		for (i = 1; i < num_ids; i++) {
+			if (phydev->c45_ids.device_ids[i] == 0xffffffff)
+				continue;
+
+			if ((phydrv->phy_id & phydrv->phy_id_mask) ==
+			    (phydev->c45_ids.device_ids[i] &
+			     phydrv->phy_id_mask))
+				return 1;
+		}
+		return 0;
+	} else {
+		return (phydrv->phy_id & phydrv->phy_id_mask) ==
+			(phydev->phy_id & phydrv->phy_id_mask);
+	}
+}
+
+static ssize_t
+phy_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+
+	return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id);
+}
+static DEVICE_ATTR_RO(phy_id);
+
+static ssize_t
+phy_interface_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	const char *mode = NULL;
+
+	if (phy_is_internal(phydev))
+		mode = "internal";
+	else
+		mode = phy_modes(phydev->interface);
+
+	return sprintf(buf, "%s\n", mode);
+}
+static DEVICE_ATTR_RO(phy_interface);
+
+static ssize_t
+phy_has_fixups_show(struct device *dev, struct device_attribute *attr,
+		    char *buf)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+
+	return sprintf(buf, "%d\n", phydev->has_fixups);
+}
+static DEVICE_ATTR_RO(phy_has_fixups);
+
+static struct attribute *phy_dev_attrs[] = {
+	&dev_attr_phy_id.attr,
+	&dev_attr_phy_interface.attr,
+	&dev_attr_phy_has_fixups.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(phy_dev);
+
+static const struct device_type mdio_bus_phy_type = {
+	.name = "PHY",
+	.groups = phy_dev_groups,
+	.release = phy_device_release,
+	.pm = pm_ptr(&mdio_bus_phy_pm_ops),
+};
+
+static int phy_request_driver_module(struct phy_device *dev, u32 phy_id)
+{
+	int ret;
+
+	ret = request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
+			     MDIO_ID_ARGS(phy_id));
+	/* We only check for failures in executing the usermode binary,
+	 * not whether a PHY driver module exists for the PHY ID.
+	 * Accept -ENOENT because this may occur in case no initramfs exists,
+	 * then modprobe isn't available.
+	 */
+	if (IS_ENABLED(CONFIG_MODULES) && ret < 0 && ret != -ENOENT) {
+		phydev_err(dev, "error %d loading PHY driver module for ID 0x%08lx\n",
+			   ret, (unsigned long)phy_id);
+		return ret;
+	}
+
+	return 0;
+}
+
+struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
+				     bool is_c45,
+				     struct phy_c45_device_ids *c45_ids)
+{
+	struct phy_device *dev;
+	struct mdio_device *mdiodev;
+	int ret = 0;
+
+	/* We allocate the device, and initialize the default values */
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	mdiodev = &dev->mdio;
+	mdiodev->dev.parent = &bus->dev;
+	mdiodev->dev.bus = &mdio_bus_type;
+	mdiodev->dev.type = &mdio_bus_phy_type;
+	mdiodev->bus = bus;
+	mdiodev->bus_match = phy_bus_match;
+	mdiodev->addr = addr;
+	mdiodev->flags = MDIO_DEVICE_FLAG_PHY;
+	mdiodev->device_free = phy_mdio_device_free;
+	mdiodev->device_remove = phy_mdio_device_remove;
+
+	dev->speed = SPEED_UNKNOWN;
+	dev->duplex = DUPLEX_UNKNOWN;
+	dev->pause = 0;
+	dev->asym_pause = 0;
+	dev->link = 0;
+	dev->port = PORT_TP;
+	dev->interface = PHY_INTERFACE_MODE_GMII;
+
+	dev->autoneg = AUTONEG_ENABLE;
+
+	dev->is_c45 = is_c45;
+	dev->phy_id = phy_id;
+	if (c45_ids)
+		dev->c45_ids = *c45_ids;
+	dev->irq = bus->irq[addr];
+
+	dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
+	device_initialize(&mdiodev->dev);
+
+	dev->state = PHY_DOWN;
+
+	mutex_init(&dev->lock);
+	INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
+
+	/* Request the appropriate module unconditionally; don't
+	 * bother trying to do so only if it isn't already loaded,
+	 * because that gets complicated. A hotplug event would have
+	 * done an unconditional modprobe anyway.
+	 * We don't do normal hotplug because it won't work for MDIO
+	 * -- because it relies on the device staying around for long
+	 * enough for the driver to get loaded. With MDIO, the NIC
+	 * driver will get bored and give up as soon as it finds that
+	 * there's no driver _already_ loaded.
+	 */
+	if (is_c45 && c45_ids) {
+		const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
+		int i;
+
+		for (i = 1; i < num_ids; i++) {
+			if (c45_ids->device_ids[i] == 0xffffffff)
+				continue;
+
+			ret = phy_request_driver_module(dev,
+						c45_ids->device_ids[i]);
+			if (ret)
+				break;
+		}
+	} else {
+		ret = phy_request_driver_module(dev, phy_id);
+	}
+
+	if (ret) {
+		put_device(&mdiodev->dev);
+		dev = ERR_PTR(ret);
+	}
+
+	return dev;
+}
+EXPORT_SYMBOL(phy_device_create);
+
+/* phy_c45_probe_present - checks to see if a MMD is present in the package
+ * @bus: the target MII bus
+ * @prtad: PHY package address on the MII bus
+ * @devad: PHY device (MMD) address
+ *
+ * Read the MDIO_STAT2 register, and check whether a device is responding
+ * at this address.
+ *
+ * Returns: negative error number on bus access error, zero if no device
+ * is responding, or positive if a device is present.
+ */
+static int phy_c45_probe_present(struct mii_bus *bus, int prtad, int devad)
+{
+	int stat2;
+
+	stat2 = mdiobus_c45_read(bus, prtad, devad, MDIO_STAT2);
+	if (stat2 < 0)
+		return stat2;
+
+	return (stat2 & MDIO_STAT2_DEVPRST) == MDIO_STAT2_DEVPRST_VAL;
+}
+
+/* get_phy_c45_devs_in_pkg - reads a MMD's devices in package registers.
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
+ * @dev_addr: MMD address in the PHY.
+ * @devices_in_package: where to store the devices in package information.
+ *
+ * Description: reads devices in package registers of a MMD at @dev_addr
+ * from PHY at @addr on @bus.
+ *
+ * Returns: 0 on success, -EIO on failure.
+ */
+static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr,
+				   u32 *devices_in_package)
+{
+	int phy_reg;
+
+	phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS2);
+	if (phy_reg < 0)
+		return -EIO;
+	*devices_in_package = phy_reg << 16;
+
+	phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS1);
+	if (phy_reg < 0)
+		return -EIO;
+	*devices_in_package |= phy_reg;
+
+	return 0;
+}
+
+/**
+ * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs.
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
+ * @c45_ids: where to store the c45 ID information.
+ *
+ * Read the PHY "devices in package". If this appears to be valid, read
+ * the PHY identifiers for each device. Return the "devices in package"
+ * and identifiers in @c45_ids.
+ *
+ * Returns zero on success, %-EIO on bus access error, or %-ENODEV if
+ * the "devices in package" is invalid.
+ */
+static int get_phy_c45_ids(struct mii_bus *bus, int addr,
+			   struct phy_c45_device_ids *c45_ids)
+{
+	const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
+	u32 devs_in_pkg = 0;
+	int i, ret, phy_reg;
+
+	/* Find first non-zero Devices In package. Device zero is reserved
+	 * for 802.3 c45 complied PHYs, so don't probe it at first.
+	 */
+	for (i = 1; i < MDIO_MMD_NUM && (devs_in_pkg == 0 ||
+	     (devs_in_pkg & 0x1fffffff) == 0x1fffffff); i++) {
+		if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) {
+			/* Check that there is a device present at this
+			 * address before reading the devices-in-package
+			 * register to avoid reading garbage from the PHY.
+			 * Some PHYs (88x3310) vendor space is not IEEE802.3
+			 * compliant.
+			 */
+			ret = phy_c45_probe_present(bus, addr, i);
+			if (ret < 0)
+				return -EIO;
+
+			if (!ret)
+				continue;
+		}
+		phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, &devs_in_pkg);
+		if (phy_reg < 0)
+			return -EIO;
+	}
+
+	if ((devs_in_pkg & 0x1fffffff) == 0x1fffffff) {
+		/* If mostly Fs, there is no device there, then let's probe
+		 * MMD 0, as some 10G PHYs have zero Devices In package,
+		 * e.g. Cortina CS4315/CS4340 PHY.
+		 */
+		phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, &devs_in_pkg);
+		if (phy_reg < 0)
+			return -EIO;
+
+		/* no device there, let's get out of here */
+		if ((devs_in_pkg & 0x1fffffff) == 0x1fffffff)
+			return -ENODEV;
+	}
+
+	/* Now probe Device Identifiers for each device present. */
+	for (i = 1; i < num_ids; i++) {
+		if (!(devs_in_pkg & (1 << i)))
+			continue;
+
+		if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) {
+			/* Probe the "Device Present" bits for the vendor MMDs
+			 * to ignore these if they do not contain IEEE 802.3
+			 * registers.
+			 */
+			ret = phy_c45_probe_present(bus, addr, i);
+			if (ret < 0)
+				return ret;
+
+			if (!ret)
+				continue;
+		}
+
+		phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID1);
+		if (phy_reg < 0)
+			return -EIO;
+		c45_ids->device_ids[i] = phy_reg << 16;
+
+		phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID2);
+		if (phy_reg < 0)
+			return -EIO;
+		c45_ids->device_ids[i] |= phy_reg;
+	}
+
+	c45_ids->devices_in_package = devs_in_pkg;
+	/* Bit 0 doesn't represent a device, it indicates c22 regs presence */
+	c45_ids->mmds_present = devs_in_pkg & ~BIT(0);
+
+	return 0;
+}
+
+/**
+ * get_phy_c22_id - reads the specified addr for its clause 22 ID.
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
+ * @phy_id: where to store the ID retrieved.
+ *
+ * Read the 802.3 clause 22 PHY ID from the PHY at @addr on the @bus,
+ * placing it in @phy_id. Return zero on successful read and the ID is
+ * valid, %-EIO on bus access error, or %-ENODEV if no device responds
+ * or invalid ID.
+ */
+static int get_phy_c22_id(struct mii_bus *bus, int addr, u32 *phy_id)
+{
+	int phy_reg;
+
+	/* Grab the bits from PHYIR1, and put them in the upper half */
+//	phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);
+#ifdef CONFIG_MDIO_C45 //zw.wang Customer chooses phy c22/c45 issues on 20240301
+    mdiobus_write(bus, addr, 0x0d, 1);
+    mdiobus_write(bus, addr, 0x0e, 2);
+    mdiobus_write(bus, addr, 0x0d, 0x4000 | 1);
+    phy_reg = mdiobus_read(bus, addr, 0x0e);
+#else 
+	phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);
+#endif
+    if (phy_reg < 0) {
+		/* returning -ENODEV doesn't stop bus scanning */
+		return (phy_reg == -EIO || phy_reg == -ENODEV) ? -ENODEV : -EIO;
+	}
+
+	*phy_id = phy_reg << 16;
+
+	/* Grab the bits from PHYIR2, and put them in the lower half */
+//	phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);
+#ifdef CONFIG_MDIO_C45	
+    mdiobus_write(bus, addr, 0x0d, 1);
+    mdiobus_write(bus, addr, 0x0e, 3);
+    mdiobus_write(bus, addr, 0x0d, 0x4000 | 1);
+    phy_reg = mdiobus_read(bus, addr, 0x0e);
+#else 
+	phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);
+#endif
+	if (phy_reg < 0) {
+		/* returning -ENODEV doesn't stop bus scanning */
+		return (phy_reg == -EIO || phy_reg == -ENODEV) ? -ENODEV : -EIO;
+	}
+
+	*phy_id |= phy_reg;
+
+#ifdef CONFIG_MDIO_C45	
+    printk("[%s] read with c45 phy id:0x%x\n", __func__, *phy_id);
+#else
+    printk("[%s] read with c22 phy id:0x%x\n", __func__, *phy_id);
+#endif
+	/* If the phy_id is mostly Fs, there is no device there */
+	if ((*phy_id & 0x1fffffff) == 0x1fffffff)
+		return -ENODEV;
+
+	return 0;
+}
+
+/**
+ * get_phy_device - reads the specified PHY device and returns its @phy_device
+ *		    struct
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
+ * @is_c45: If true the PHY uses the 802.3 clause 45 protocol
+ *
+ * Probe for a PHY at @addr on @bus.
+ *
+ * When probing for a clause 22 PHY, then read the ID registers. If we find
+ * a valid ID, allocate and return a &struct phy_device.
+ *
+ * When probing for a clause 45 PHY, read the "devices in package" registers.
+ * If the "devices in package" appears valid, read the ID registers for each
+ * MMD, allocate and return a &struct phy_device.
+ *
+ * Returns an allocated &struct phy_device on success, %-ENODEV if there is
+ * no PHY present, or %-EIO on bus access error.
+ */
+struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
+{
+	struct phy_c45_device_ids c45_ids;
+	u32 phy_id = 0;
+	int r;
+
+	c45_ids.devices_in_package = 0;
+	c45_ids.mmds_present = 0;
+	memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids));
+
+	if (is_c45)
+		r = get_phy_c45_ids(bus, addr, &c45_ids);
+	else
+		r = get_phy_c22_id(bus, addr, &phy_id);
+
+	if (r)
+		return ERR_PTR(r);
+
+	return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);
+}
+EXPORT_SYMBOL(get_phy_device);
+
+/**
+ * phy_device_register - Register the phy device on the MDIO bus
+ * @phydev: phy_device structure to be added to the MDIO bus
+ */
+int phy_device_register(struct phy_device *phydev)
+{
+	int err;
+
+	err = mdiobus_register_device(&phydev->mdio);
+	if (err)
+		return err;
+
+	/* Deassert the reset signal */
+	phy_device_reset(phydev, 0);
+
+	/* Run all of the fixups for this PHY */
+	err = phy_scan_fixups(phydev);
+	if (err) {
+		phydev_err(phydev, "failed to initialize\n");
+		goto out;
+	}
+
+	err = device_add(&phydev->mdio.dev);
+	if (err) {
+		phydev_err(phydev, "failed to add\n");
+		goto out;
+	}
+
+	return 0;
+
+ out:
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
+
+	mdiobus_unregister_device(&phydev->mdio);
+	return err;
+}
+EXPORT_SYMBOL(phy_device_register);
+
+/**
+ * phy_device_remove - Remove a previously registered phy device from the MDIO bus
+ * @phydev: phy_device structure to remove
+ *
+ * This doesn't free the phy_device itself, it merely reverses the effects
+ * of phy_device_register(). Use phy_device_free() to free the device
+ * after calling this function.
+ */
+void phy_device_remove(struct phy_device *phydev)
+{
+	if (phydev->mii_ts)
+		unregister_mii_timestamper(phydev->mii_ts);
+
+	device_del(&phydev->mdio.dev);
+
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
+
+	mdiobus_unregister_device(&phydev->mdio);
+}
+EXPORT_SYMBOL(phy_device_remove);
+
+/**
+ * phy_find_first - finds the first PHY device on the bus
+ * @bus: the target MII bus
+ */
+struct phy_device *phy_find_first(struct mii_bus *bus)
+{
+	struct phy_device *phydev;
+	int addr;
+
+	for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
+		phydev = mdiobus_get_phy(bus, addr);
+		if (phydev) {
+			printk("[%s] addr:%d\n", __func__, addr);
+			return phydev;
+		}
+	}
+	return NULL;
+}
+EXPORT_SYMBOL(phy_find_first);
+
+static void phy_link_change(struct phy_device *phydev, bool up)
+{
+	struct net_device *netdev = phydev->attached_dev;
+
+	if (up)
+		netif_carrier_on(netdev);
+	else
+		netif_carrier_off(netdev);
+	phydev->adjust_link(netdev);
+	if (phydev->mii_ts && phydev->mii_ts->link_state)
+		phydev->mii_ts->link_state(phydev->mii_ts, phydev);
+}
+
+/**
+ * phy_prepare_link - prepares the PHY layer to monitor link status
+ * @phydev: target phy_device struct
+ * @handler: callback function for link status change notifications
+ *
+ * Description: Tells the PHY infrastructure to handle the
+ *   gory details on monitoring link status (whether through
+ *   polling or an interrupt), and to call back to the
+ *   connected device driver when the link status changes.
+ *   If you want to monitor your own link state, don't call
+ *   this function.
+ */
+static void phy_prepare_link(struct phy_device *phydev,
+			     void (*handler)(struct net_device *))
+{
+	phydev->adjust_link = handler;
+}
+
+/**
+ * phy_connect_direct - connect an ethernet device to a specific phy_device
+ * @dev: the network device to connect
+ * @phydev: the pointer to the phy device
+ * @handler: callback function for state change notifications
+ * @interface: PHY device's interface
+ */
+int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,
+		       void (*handler)(struct net_device *),
+		       phy_interface_t interface)
+{
+	int rc;
+
+	if (!dev)
+		return -EINVAL;
+
+	rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);
+	if (rc)
+		return rc;
+
+	phy_prepare_link(phydev, handler);
+	if (phy_interrupt_is_valid(phydev))
+		phy_request_interrupt(phydev);
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_connect_direct);
+
+/**
+ * phy_connect - connect an ethernet device to a PHY device
+ * @dev: the network device to connect
+ * @bus_id: the id string of the PHY device to connect
+ * @handler: callback function for state change notifications
+ * @interface: PHY device's interface
+ *
+ * Description: Convenience function for connecting ethernet
+ *   devices to PHY devices.  The default behavior is for
+ *   the PHY infrastructure to handle everything, and only notify
+ *   the connected driver when the link status changes.  If you
+ *   don't want, or can't use the provided functionality, you may
+ *   choose to call only the subset of functions which provide
+ *   the desired functionality.
+ */
+struct phy_device *phy_connect(struct net_device *dev, const char *bus_id,
+			       void (*handler)(struct net_device *),
+			       phy_interface_t interface)
+{
+	struct phy_device *phydev;
+	struct device *d;
+	int rc;
+
+	/* Search the list of PHY devices on the mdio bus for the
+	 * PHY with the requested name
+	 */
+	d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id);
+	if (!d) {
+		pr_err("PHY %s not found\n", bus_id);
+		return ERR_PTR(-ENODEV);
+	}
+	phydev = to_phy_device(d);
+
+	rc = phy_connect_direct(dev, phydev, handler, interface);
+	put_device(d);
+	if (rc)
+		return ERR_PTR(rc);
+
+	return phydev;
+}
+EXPORT_SYMBOL(phy_connect);
+
+/**
+ * phy_disconnect - disable interrupts, stop state machine, and detach a PHY
+ *		    device
+ * @phydev: target phy_device struct
+ */
+void phy_disconnect(struct phy_device *phydev)
+{
+	if (phy_is_started(phydev))
+		phy_stop(phydev);
+
+	if (phy_interrupt_is_valid(phydev))
+		phy_free_interrupt(phydev);
+
+	phydev->adjust_link = NULL;
+
+	phy_detach(phydev);
+}
+EXPORT_SYMBOL(phy_disconnect);
+
+/**
+ * phy_poll_reset - Safely wait until a PHY reset has properly completed
+ * @phydev: The PHY device to poll
+ *
+ * Description: According to IEEE 802.3, Section 2, Subsection 22.2.4.1.1, as
+ *   published in 2008, a PHY reset may take up to 0.5 seconds.  The MII BMCR
+ *   register must be polled until the BMCR_RESET bit clears.
+ *
+ *   Furthermore, any attempts to write to PHY registers may have no effect
+ *   or even generate MDIO bus errors until this is complete.
+ *
+ *   Some PHYs (such as the Marvell 88E1111) don't entirely conform to the
+ *   standard and do not fully reset after the BMCR_RESET bit is set, and may
+ *   even *REQUIRE* a soft-reset to properly restart autonegotiation.  In an
+ *   effort to support such broken PHYs, this function is separate from the
+ *   standard phy_init_hw() which will zero all the other bits in the BMCR
+ *   and reapply all driver-specific and board-specific fixups.
+ */
+static int phy_poll_reset(struct phy_device *phydev)
+{
+	/* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
+	int ret, val;
+
+	ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
+				    50000, 600000, true);
+	if (ret)
+		return ret;
+	/* Some chips (smsc911x) may still need up to another 1ms after the
+	 * BMCR_RESET bit is cleared before they are usable.
+	 */
+	msleep(1);
+	return 0;
+}
+
+int phy_init_hw(struct phy_device *phydev)
+{
+	int ret = 0;
+
+	/* Deassert the reset signal */
+	phy_device_reset(phydev, 0);
+
+	if (!phydev->drv)
+		return 0;
+
+	if (phydev->drv->soft_reset) {
+		ret = phydev->drv->soft_reset(phydev);
+		/* see comment in genphy_soft_reset for an explanation */
+		if (!ret)
+			phydev->suspended = 0;
+	}
+
+	if (ret < 0)
+		return ret;
+
+	ret = phy_scan_fixups(phydev);
+	if (ret < 0)
+		return ret;
+
+	if (phydev->drv->config_init) {
+		ret = phydev->drv->config_init(phydev);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (phydev->drv->config_intr) {
+		ret = phydev->drv->config_intr(phydev);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_init_hw);
+
+void phy_attached_info(struct phy_device *phydev)
+{
+	phy_attached_print(phydev, NULL);
+}
+EXPORT_SYMBOL(phy_attached_info);
+
+#define ATTACHED_FMT "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%s)"
+char *phy_attached_info_irq(struct phy_device *phydev)
+{
+	char *irq_str;
+	char irq_num[8];
+
+	switch(phydev->irq) {
+	case PHY_POLL:
+		irq_str = "POLL";
+		break;
+	case PHY_IGNORE_INTERRUPT:
+		irq_str = "IGNORE";
+		break;
+	default:
+		snprintf(irq_num, sizeof(irq_num), "%d", phydev->irq);
+		irq_str = irq_num;
+		break;
+	}
+
+	return kasprintf(GFP_KERNEL, "%s", irq_str);
+}
+EXPORT_SYMBOL(phy_attached_info_irq);
+
+void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)
+{
+	const char *drv_name = phydev->drv ? phydev->drv->name : "unbound";
+	char *irq_str = phy_attached_info_irq(phydev);
+
+	if (!fmt) {
+		phydev_info(phydev, ATTACHED_FMT "\n",
+			 drv_name, phydev_name(phydev),
+			 irq_str);
+	} else {
+		va_list ap;
+
+		phydev_info(phydev, ATTACHED_FMT,
+			 drv_name, phydev_name(phydev),
+			 irq_str);
+
+		va_start(ap, fmt);
+		vprintk(fmt, ap);
+		va_end(ap);
+	}
+	kfree(irq_str);
+}
+EXPORT_SYMBOL(phy_attached_print);
+
+static void phy_sysfs_create_links(struct phy_device *phydev)
+{
+	struct net_device *dev = phydev->attached_dev;
+	int err;
+
+	if (!dev)
+		return;
+
+	err = sysfs_create_link(&phydev->mdio.dev.kobj, &dev->dev.kobj,
+				"attached_dev");
+	if (err)
+		return;
+
+	err = sysfs_create_link_nowarn(&dev->dev.kobj,
+				       &phydev->mdio.dev.kobj,
+				       "phydev");
+	if (err) {
+		dev_err(&dev->dev, "could not add device link to %s err %d\n",
+			kobject_name(&phydev->mdio.dev.kobj),
+			err);
+		/* non-fatal - some net drivers can use one netdevice
+		 * with more then one phy
+		 */
+	}
+
+	phydev->sysfs_links = true;
+}
+
+static ssize_t
+phy_standalone_show(struct device *dev, struct device_attribute *attr,
+		    char *buf)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+
+	return sprintf(buf, "%d\n", !phydev->attached_dev);
+}
+static DEVICE_ATTR_RO(phy_standalone);
+
+/**
+ * phy_sfp_attach - attach the SFP bus to the PHY upstream network device
+ * @upstream: pointer to the phy device
+ * @bus: sfp bus representing cage being attached
+ *
+ * This is used to fill in the sfp_upstream_ops .attach member.
+ */
+void phy_sfp_attach(void *upstream, struct sfp_bus *bus)
+{
+	struct phy_device *phydev = upstream;
+
+	if (phydev->attached_dev)
+		phydev->attached_dev->sfp_bus = bus;
+	phydev->sfp_bus_attached = true;
+}
+EXPORT_SYMBOL(phy_sfp_attach);
+
+/**
+ * phy_sfp_detach - detach the SFP bus from the PHY upstream network device
+ * @upstream: pointer to the phy device
+ * @bus: sfp bus representing cage being attached
+ *
+ * This is used to fill in the sfp_upstream_ops .detach member.
+ */
+void phy_sfp_detach(void *upstream, struct sfp_bus *bus)
+{
+	struct phy_device *phydev = upstream;
+
+	if (phydev->attached_dev)
+		phydev->attached_dev->sfp_bus = NULL;
+	phydev->sfp_bus_attached = false;
+}
+EXPORT_SYMBOL(phy_sfp_detach);
+
+/**
+ * phy_sfp_probe - probe for a SFP cage attached to this PHY device
+ * @phydev: Pointer to phy_device
+ * @ops: SFP's upstream operations
+ */
+int phy_sfp_probe(struct phy_device *phydev,
+		  const struct sfp_upstream_ops *ops)
+{
+	struct sfp_bus *bus;
+	int ret = 0;
+
+	if (phydev->mdio.dev.fwnode) {
+		bus = sfp_bus_find_fwnode(phydev->mdio.dev.fwnode);
+		if (IS_ERR(bus))
+			return PTR_ERR(bus);
+
+		phydev->sfp_bus = bus;
+
+		ret = sfp_bus_add_upstream(bus, phydev, ops);
+		sfp_bus_put(bus);
+	}
+	return ret;
+}
+EXPORT_SYMBOL(phy_sfp_probe);
+
+/**
+ * phy_attach_direct - attach a network device to a given PHY device pointer
+ * @dev: network device to attach
+ * @phydev: Pointer to phy_device to attach
+ * @flags: PHY device's dev_flags
+ * @interface: PHY device's interface
+ *
+ * Description: Called by drivers to attach to a particular PHY
+ *     device. The phy_device is found, and properly hooked up
+ *     to the phy_driver.  If no driver is attached, then a
+ *     generic driver is used.  The phy_device is given a ptr to
+ *     the attaching device, and given a callback for link status
+ *     change.  The phy_device is returned to the attaching driver.
+ *     This function takes a reference on the phy device.
+ */
+int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
+		      u32 flags, phy_interface_t interface)
+{
+	struct mii_bus *bus = phydev->mdio.bus;
+	struct device *d = &phydev->mdio.dev;
+	struct module *ndev_owner = NULL;
+	bool using_genphy = false;
+	int err;
+
+	/* For Ethernet device drivers that register their own MDIO bus, we
+	 * will have bus->owner match ndev_mod, so we do not want to increment
+	 * our own module->refcnt here, otherwise we would not be able to
+	 * unload later on.
+	 */
+	if (dev)
+		ndev_owner = dev->dev.parent->driver->owner;
+	if (ndev_owner != bus->owner && !try_module_get(bus->owner)) {
+		phydev_err(phydev, "failed to get the bus module\n");
+		return -EIO;
+	}
+
+	get_device(d);
+
+	/* Assume that if there is no driver, that it doesn't
+	 * exist, and we should use the genphy driver.
+	 */
+	if (!d->driver) {
+		if (phydev->is_c45)
+			d->driver = &genphy_c45_driver.mdiodrv.driver;
+		else
+			d->driver = &genphy_driver.mdiodrv.driver;
+
+		using_genphy = true;
+	}
+
+	if (!try_module_get(d->driver->owner)) {
+		phydev_err(phydev, "failed to get the device driver module\n");
+		err = -EIO;
+		goto error_put_device;
+	}
+
+	if (using_genphy) {
+		err = d->driver->probe(d);
+		if (err >= 0)
+			err = device_bind_driver(d);
+
+		if (err)
+			goto error_module_put;
+	}
+
+	if (phydev->attached_dev) {
+		dev_err(&dev->dev, "PHY already attached\n");
+		err = -EBUSY;
+		goto error;
+	}
+
+	phydev->phy_link_change = phy_link_change;
+	if (dev) {
+		phydev->attached_dev = dev;
+		dev->phydev = phydev;
+
+		if (phydev->sfp_bus_attached)
+			dev->sfp_bus = phydev->sfp_bus;
+	}
+
+	/* Some Ethernet drivers try to connect to a PHY device before
+	 * calling register_netdevice() -> netdev_register_kobject() and
+	 * does the dev->dev.kobj initialization. Here we only check for
+	 * success which indicates that the network device kobject is
+	 * ready. Once we do that we still need to keep track of whether
+	 * links were successfully set up or not for phy_detach() to
+	 * remove them accordingly.
+	 */
+	phydev->sysfs_links = false;
+
+	phy_sysfs_create_links(phydev);
+
+	if (!phydev->attached_dev) {
+		err = sysfs_create_file(&phydev->mdio.dev.kobj,
+					&dev_attr_phy_standalone.attr);
+		if (err)
+			phydev_err(phydev, "error creating 'phy_standalone' sysfs entry\n");
+	}
+
+	phydev->dev_flags |= flags;
+
+	phydev->interface = interface;
+
+	phydev->state = PHY_READY;
+
+	/* Port is set to PORT_TP by default and the actual PHY driver will set
+	 * it to different value depending on the PHY configuration. If we have
+	 * the generic PHY driver we can't figure it out, thus set the old
+	 * legacy PORT_MII value.
+	 */
+	if (using_genphy)
+		phydev->port = PORT_MII;
+
+	/* Initial carrier state is off as the phy is about to be
+	 * (re)initialized.
+	 */
+	if (dev)
+		netif_carrier_off(phydev->attached_dev);
+
+	/* Do initial configuration here, now that
+	 * we have certain key parameters
+	 * (dev_flags and interface)
+	 */
+	err = phy_init_hw(phydev);
+	if (err)
+		goto error;
+
+	err = phy_disable_interrupts(phydev);
+	if (err)
+		return err;
+
+	phy_resume(phydev);
+	phy_led_triggers_register(phydev);
+
+	return err;
+
+error:
+	/* phy_detach() does all of the cleanup below */
+	phy_detach(phydev);
+	return err;
+
+error_module_put:
+	module_put(d->driver->owner);
+error_put_device:
+	put_device(d);
+	if (ndev_owner != bus->owner)
+		module_put(bus->owner);
+	return err;
+}
+EXPORT_SYMBOL(phy_attach_direct);
+
+/**
+ * phy_attach - attach a network device to a particular PHY device
+ * @dev: network device to attach
+ * @bus_id: Bus ID of PHY device to attach
+ * @interface: PHY device's interface
+ *
+ * Description: Same as phy_attach_direct() except that a PHY bus_id
+ *     string is passed instead of a pointer to a struct phy_device.
+ */
+struct phy_device *phy_attach(struct net_device *dev, const char *bus_id,
+			      phy_interface_t interface)
+{
+	struct bus_type *bus = &mdio_bus_type;
+	struct phy_device *phydev;
+	struct device *d;
+	int rc;
+
+	if (!dev)
+		return ERR_PTR(-EINVAL);
+
+	/* Search the list of PHY devices on the mdio bus for the
+	 * PHY with the requested name
+	 */
+	d = bus_find_device_by_name(bus, NULL, bus_id);
+	if (!d) {
+		pr_err("PHY %s not found\n", bus_id);
+		return ERR_PTR(-ENODEV);
+	}
+	phydev = to_phy_device(d);
+
+	rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);
+	put_device(d);
+	if (rc)
+		return ERR_PTR(rc);
+
+	return phydev;
+}
+EXPORT_SYMBOL(phy_attach);
+
+static bool phy_driver_is_genphy_kind(struct phy_device *phydev,
+				      struct device_driver *driver)
+{
+	struct device *d = &phydev->mdio.dev;
+	bool ret = false;
+
+	if (!phydev->drv)
+		return ret;
+
+	get_device(d);
+	ret = d->driver == driver;
+	put_device(d);
+
+	return ret;
+}
+
+bool phy_driver_is_genphy(struct phy_device *phydev)
+{
+	return phy_driver_is_genphy_kind(phydev,
+					 &genphy_driver.mdiodrv.driver);
+}
+EXPORT_SYMBOL_GPL(phy_driver_is_genphy);
+
+bool phy_driver_is_genphy_10g(struct phy_device *phydev)
+{
+	return phy_driver_is_genphy_kind(phydev,
+					 &genphy_c45_driver.mdiodrv.driver);
+}
+EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g);
+
+/**
+ * phy_package_join - join a common PHY group
+ * @phydev: target phy_device struct
+ * @addr: cookie and PHY address for global register access
+ * @priv_size: if non-zero allocate this amount of bytes for private data
+ *
+ * This joins a PHY group and provides a shared storage for all phydevs in
+ * this group. This is intended to be used for packages which contain
+ * more than one PHY, for example a quad PHY transceiver.
+ *
+ * The addr parameter serves as a cookie which has to have the same value
+ * for all members of one group and as a PHY address to access generic
+ * registers of a PHY package. Usually, one of the PHY addresses of the
+ * different PHYs in the package provides access to these global registers.
+ * The address which is given here, will be used in the phy_package_read()
+ * and phy_package_write() convenience functions. If your PHY doesn't have
+ * global registers you can just pick any of the PHY addresses.
+ *
+ * This will set the shared pointer of the phydev to the shared storage.
+ * If this is the first call for a this cookie the shared storage will be
+ * allocated. If priv_size is non-zero, the given amount of bytes are
+ * allocated for the priv member.
+ *
+ * Returns < 1 on error, 0 on success. Esp. calling phy_package_join()
+ * with the same cookie but a different priv_size is an error.
+ */
+int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size)
+{
+	struct mii_bus *bus = phydev->mdio.bus;
+	struct phy_package_shared *shared;
+	int ret;
+
+	if (addr < 0 || addr >= PHY_MAX_ADDR)
+		return -EINVAL;
+
+	mutex_lock(&bus->shared_lock);
+	shared = bus->shared[addr];
+	if (!shared) {
+		ret = -ENOMEM;
+		shared = kzalloc(sizeof(*shared), GFP_KERNEL);
+		if (!shared)
+			goto err_unlock;
+		if (priv_size) {
+			shared->priv = kzalloc(priv_size, GFP_KERNEL);
+			if (!shared->priv)
+				goto err_free;
+			shared->priv_size = priv_size;
+		}
+		shared->addr = addr;
+		refcount_set(&shared->refcnt, 1);
+		bus->shared[addr] = shared;
+	} else {
+		ret = -EINVAL;
+		if (priv_size && priv_size != shared->priv_size)
+			goto err_unlock;
+		refcount_inc(&shared->refcnt);
+	}
+	mutex_unlock(&bus->shared_lock);
+
+	phydev->shared = shared;
+
+	return 0;
+
+err_free:
+	kfree(shared);
+err_unlock:
+	mutex_unlock(&bus->shared_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(phy_package_join);
+
+/**
+ * phy_package_leave - leave a common PHY group
+ * @phydev: target phy_device struct
+ *
+ * This leaves a PHY group created by phy_package_join(). If this phydev
+ * was the last user of the shared data between the group, this data is
+ * freed. Resets the phydev->shared pointer to NULL.
+ */
+void phy_package_leave(struct phy_device *phydev)
+{
+	struct phy_package_shared *shared = phydev->shared;
+	struct mii_bus *bus = phydev->mdio.bus;
+
+	if (!shared)
+		return;
+
+	if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
+		bus->shared[shared->addr] = NULL;
+		mutex_unlock(&bus->shared_lock);
+		kfree(shared->priv);
+		kfree(shared);
+	}
+
+	phydev->shared = NULL;
+}
+EXPORT_SYMBOL_GPL(phy_package_leave);
+
+static void devm_phy_package_leave(struct device *dev, void *res)
+{
+	phy_package_leave(*(struct phy_device **)res);
+}
+
+/**
+ * devm_phy_package_join - resource managed phy_package_join()
+ * @dev: device that is registering this PHY package
+ * @phydev: target phy_device struct
+ * @addr: cookie and PHY address for global register access
+ * @priv_size: if non-zero allocate this amount of bytes for private data
+ *
+ * Managed phy_package_join(). Shared storage fetched by this function,
+ * phy_package_leave() is automatically called on driver detach. See
+ * phy_package_join() for more information.
+ */
+int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
+			  int addr, size_t priv_size)
+{
+	struct phy_device **ptr;
+	int ret;
+
+	ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
+			   GFP_KERNEL);
+	if (!ptr)
+		return -ENOMEM;
+
+	ret = phy_package_join(phydev, addr, priv_size);
+
+	if (!ret) {
+		*ptr = phydev;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(devm_phy_package_join);
+
+/**
+ * phy_detach - detach a PHY device from its network device
+ * @phydev: target phy_device struct
+ *
+ * This detaches the phy device from its network device and the phy
+ * driver, and drops the reference count taken in phy_attach_direct().
+ */
+void phy_detach(struct phy_device *phydev)
+{
+	struct net_device *dev = phydev->attached_dev;
+	struct module *ndev_owner = NULL;
+	struct mii_bus *bus;
+
+	if (phydev->sysfs_links) {
+		if (dev)
+			sysfs_remove_link(&dev->dev.kobj, "phydev");
+		sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev");
+	}
+
+	if (!phydev->attached_dev)
+		sysfs_remove_file(&phydev->mdio.dev.kobj,
+				  &dev_attr_phy_standalone.attr);
+
+	phy_suspend(phydev);
+	if (dev) {
+		phydev->attached_dev->phydev = NULL;
+		phydev->attached_dev = NULL;
+	}
+	phydev->phylink = NULL;
+
+	phy_led_triggers_unregister(phydev);
+
+	if (phydev->mdio.dev.driver)
+		module_put(phydev->mdio.dev.driver->owner);
+
+	/* If the device had no specific driver before (i.e. - it
+	 * was using the generic driver), we unbind the device
+	 * from the generic driver so that there's a chance a
+	 * real driver could be loaded
+	 */
+	if (phy_driver_is_genphy(phydev) ||
+	    phy_driver_is_genphy_10g(phydev))
+		device_release_driver(&phydev->mdio.dev);
+
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
+
+	/*
+	 * The phydev might go away on the put_device() below, so avoid
+	 * a use-after-free bug by reading the underlying bus first.
+	 */
+	bus = phydev->mdio.bus;
+
+	put_device(&phydev->mdio.dev);
+	if (dev)
+		ndev_owner = dev->dev.parent->driver->owner;
+	if (ndev_owner != bus->owner)
+		module_put(bus->owner);
+}
+EXPORT_SYMBOL(phy_detach);
+
+int phy_suspend(struct phy_device *phydev)
+{
+	struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+	struct net_device *netdev = phydev->attached_dev;
+	struct phy_driver *phydrv = phydev->drv;
+	int ret;
+
+	if (phydev->suspended)
+		return 0;
+
+	/* If the device has WOL enabled, we cannot suspend the PHY */
+	phy_ethtool_get_wol(phydev, &wol);
+	if (wol.wolopts || (netdev && netdev->wol_enabled))
+		return -EBUSY;
+
+	if (!phydrv || !phydrv->suspend)
+		return 0;
+
+	ret = phydrv->suspend(phydev);
+	if (!ret)
+		phydev->suspended = true;
+
+	return ret;
+}
+EXPORT_SYMBOL(phy_suspend);
+
+int __phy_resume(struct phy_device *phydev)
+{
+	struct phy_driver *phydrv = phydev->drv;
+	int ret;
+
+	WARN_ON(!mutex_is_locked(&phydev->lock));
+
+	if (!phydrv || !phydrv->resume)
+		return 0;
+
+	ret = phydrv->resume(phydev);
+	if (!ret)
+		phydev->suspended = false;
+
+	return ret;
+}
+EXPORT_SYMBOL(__phy_resume);
+
+int phy_resume(struct phy_device *phydev)
+{
+	int ret;
+
+	mutex_lock(&phydev->lock);
+	ret = __phy_resume(phydev);
+	mutex_unlock(&phydev->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(phy_resume);
+
+int phy_loopback(struct phy_device *phydev, bool enable)
+{
+	struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
+	int ret = 0;
+
+	mutex_lock(&phydev->lock);
+
+	if (enable && phydev->loopback_enabled) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	if (!enable && !phydev->loopback_enabled) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (phydev->drv && phydrv->set_loopback)
+		ret = phydrv->set_loopback(phydev, enable);
+	else
+		ret = -EOPNOTSUPP;
+
+	if (ret)
+		goto out;
+
+	phydev->loopback_enabled = enable;
+
+out:
+	mutex_unlock(&phydev->lock);
+	return ret;
+}
+EXPORT_SYMBOL(phy_loopback);
+
+/**
+ * phy_reset_after_clk_enable - perform a PHY reset if needed
+ * @phydev: target phy_device struct
+ *
+ * Description: Some PHYs are known to need a reset after their refclk was
+ *   enabled. This function evaluates the flags and perform the reset if it's
+ *   needed. Returns < 0 on error, 0 if the phy wasn't reset and 1 if the phy
+ *   was reset.
+ */
+int phy_reset_after_clk_enable(struct phy_device *phydev)
+{
+	if (!phydev || !phydev->drv)
+		return -ENODEV;
+
+	if (phydev->drv->flags & PHY_RST_AFTER_CLK_EN) {
+		phy_device_reset(phydev, 1);
+		phy_device_reset(phydev, 0);
+		return 1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_reset_after_clk_enable);
+
+/* Generic PHY support and helper functions */
+
+/**
+ * genphy_config_advert - sanitize and advertise auto-negotiation parameters
+ * @phydev: target phy_device struct
+ *
+ * Description: Writes MII_ADVERTISE with the appropriate values,
+ *   after sanitizing the values to make sure we only advertise
+ *   what is supported.  Returns < 0 on error, 0 if the PHY's advertisement
+ *   hasn't changed, and > 0 if it has changed.
+ */
+static int genphy_config_advert(struct phy_device *phydev)
+{
+	int err, bmsr, changed = 0;
+	u32 adv;
+
+	/* Only allow advertising what this PHY supports */
+	linkmode_and(phydev->advertising, phydev->advertising,
+		     phydev->supported);
+
+	adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
+
+	/* Setup standard advertisement */
+	err = phy_modify_changed(phydev, MII_ADVERTISE,
+				 ADVERTISE_ALL | ADVERTISE_100BASE4 |
+				 ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
+				 adv);
+	if (err < 0)
+		return err;
+	if (err > 0)
+		changed = 1;
+
+	bmsr = phy_read(phydev, MII_BMSR);
+	if (bmsr < 0)
+		return bmsr;
+
+	/* Per 802.3-2008, Section 22.2.4.2.16 Extended status all
+	 * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a
+	 * logical 1.
+	 */
+	if (!(bmsr & BMSR_ESTATEN))
+		return changed;
+
+	adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
+
+	err = phy_modify_changed(phydev, MII_CTRL1000,
+				 ADVERTISE_1000FULL | ADVERTISE_1000HALF,
+				 adv);
+	if (err < 0)
+		return err;
+	if (err > 0)
+		changed = 1;
+
+	return changed;
+}
+
+/**
+ * genphy_c37_config_advert - sanitize and advertise auto-negotiation parameters
+ * @phydev: target phy_device struct
+ *
+ * Description: Writes MII_ADVERTISE with the appropriate values,
+ *   after sanitizing the values to make sure we only advertise
+ *   what is supported.  Returns < 0 on error, 0 if the PHY's advertisement
+ *   hasn't changed, and > 0 if it has changed. This function is intended
+ *   for Clause 37 1000Base-X mode.
+ */
+static int genphy_c37_config_advert(struct phy_device *phydev)
+{
+	u16 adv = 0;
+
+	/* Only allow advertising what this PHY supports */
+	linkmode_and(phydev->advertising, phydev->advertising,
+		     phydev->supported);
+
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+			      phydev->advertising))
+		adv |= ADVERTISE_1000XFULL;
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+			      phydev->advertising))
+		adv |= ADVERTISE_1000XPAUSE;
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+			      phydev->advertising))
+		adv |= ADVERTISE_1000XPSE_ASYM;
+
+	return phy_modify_changed(phydev, MII_ADVERTISE,
+				  ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE |
+				  ADVERTISE_1000XHALF | ADVERTISE_1000XPSE_ASYM,
+				  adv);
+}
+
+/**
+ * genphy_config_eee_advert - disable unwanted eee mode advertisement
+ * @phydev: target phy_device struct
+ *
+ * Description: Writes MDIO_AN_EEE_ADV after disabling unsupported energy
+ *   efficent ethernet modes. Returns 0 if the PHY's advertisement hasn't
+ *   changed, and 1 if it has changed.
+ */
+int genphy_config_eee_advert(struct phy_device *phydev)
+{
+	int err;
+
+	/* Nothing to disable */
+	if (!phydev->eee_broken_modes)
+		return 0;
+
+	err = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV,
+				     phydev->eee_broken_modes, 0);
+	/* If the call failed, we assume that EEE is not supported */
+	return err < 0 ? 0 : err;
+}
+EXPORT_SYMBOL(genphy_config_eee_advert);
+
+/**
+ * genphy_setup_forced - configures/forces speed/duplex from @phydev
+ * @phydev: target phy_device struct
+ *
+ * Description: Configures MII_BMCR to force speed/duplex
+ *   to the values in phydev. Assumes that the values are valid.
+ *   Please see phy_sanitize_settings().
+ */
+int genphy_setup_forced(struct phy_device *phydev)
+{
+	u16 ctl = 0;
+
+	phydev->pause = 0;
+	phydev->asym_pause = 0;
+
+	if (SPEED_1000 == phydev->speed)
+		ctl |= BMCR_SPEED1000;
+	else if (SPEED_100 == phydev->speed)
+		ctl |= BMCR_SPEED100;
+
+	if (DUPLEX_FULL == phydev->duplex)
+		ctl |= BMCR_FULLDPLX;
+
+	return phy_modify(phydev, MII_BMCR,
+			  ~(BMCR_LOOPBACK | BMCR_ISOLATE | BMCR_PDOWN), ctl);
+}
+EXPORT_SYMBOL(genphy_setup_forced);
+
+static int genphy_setup_master_slave(struct phy_device *phydev)
+{
+	u16 ctl = 0;
+
+	if (!phydev->is_gigabit_capable)
+		return 0;
+
+	switch (phydev->master_slave_set) {
+	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+		ctl |= CTL1000_PREFER_MASTER;
+		break;
+	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+		break;
+	case MASTER_SLAVE_CFG_MASTER_FORCE:
+		ctl |= CTL1000_AS_MASTER;
+		fallthrough;
+	case MASTER_SLAVE_CFG_SLAVE_FORCE:
+		ctl |= CTL1000_ENABLE_MASTER;
+		break;
+	case MASTER_SLAVE_CFG_UNKNOWN:
+	case MASTER_SLAVE_CFG_UNSUPPORTED:
+		return 0;
+	default:
+		phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+		return -EOPNOTSUPP;
+	}
+
+	return phy_modify_changed(phydev, MII_CTRL1000,
+				  (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER |
+				   CTL1000_PREFER_MASTER), ctl);
+}
+
+static int genphy_read_master_slave(struct phy_device *phydev)
+{
+	int cfg, state;
+	int val;
+
+	if (!phydev->is_gigabit_capable) {
+		phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED;
+		phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
+		return 0;
+	}
+
+	phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
+	phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+
+	val = phy_read(phydev, MII_CTRL1000);
+	if (val < 0)
+		return val;
+
+	if (val & CTL1000_ENABLE_MASTER) {
+		if (val & CTL1000_AS_MASTER)
+			cfg = MASTER_SLAVE_CFG_MASTER_FORCE;
+		else
+			cfg = MASTER_SLAVE_CFG_SLAVE_FORCE;
+	} else {
+		if (val & CTL1000_PREFER_MASTER)
+			cfg = MASTER_SLAVE_CFG_MASTER_PREFERRED;
+		else
+			cfg = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
+	}
+
+	val = phy_read(phydev, MII_STAT1000);
+	if (val < 0)
+		return val;
+
+	if (val & LPA_1000MSFAIL) {
+		state = MASTER_SLAVE_STATE_ERR;
+	} else if (phydev->link) {
+		/* this bits are valid only for active link */
+		if (val & LPA_1000MSRES)
+			state = MASTER_SLAVE_STATE_MASTER;
+		else
+			state = MASTER_SLAVE_STATE_SLAVE;
+	} else {
+		state = MASTER_SLAVE_STATE_UNKNOWN;
+	}
+
+	phydev->master_slave_get = cfg;
+	phydev->master_slave_state = state;
+
+	return 0;
+}
+
+/**
+ * genphy_restart_aneg - Enable and Restart Autonegotiation
+ * @phydev: target phy_device struct
+ */
+int genphy_restart_aneg(struct phy_device *phydev)
+{
+	/* Don't isolate the PHY if we're negotiating */
+	return phy_modify(phydev, MII_BMCR, BMCR_ISOLATE,
+			  BMCR_ANENABLE | BMCR_ANRESTART);
+}
+EXPORT_SYMBOL(genphy_restart_aneg);
+
+/**
+ * genphy_check_and_restart_aneg - Enable and restart auto-negotiation
+ * @phydev: target phy_device struct
+ * @restart: whether aneg restart is requested
+ *
+ * Check, and restart auto-negotiation if needed.
+ */
+int genphy_check_and_restart_aneg(struct phy_device *phydev, bool restart)
+{
+	int ret;
+
+	if (!restart) {
+		/* Advertisement hasn't changed, but maybe aneg was never on to
+		 * begin with?  Or maybe phy was isolated?
+		 */
+		ret = phy_read(phydev, MII_BMCR);
+		if (ret < 0)
+			return ret;
+
+		if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE))
+			restart = true;
+	}
+
+	if (restart)
+		return genphy_restart_aneg(phydev);
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_check_and_restart_aneg);
+
+/**
+ * __genphy_config_aneg - restart auto-negotiation or write BMCR
+ * @phydev: target phy_device struct
+ * @changed: whether autoneg is requested
+ *
+ * Description: If auto-negotiation is enabled, we configure the
+ *   advertising, and then restart auto-negotiation.  If it is not
+ *   enabled, then we write the BMCR.
+ */
+int __genphy_config_aneg(struct phy_device *phydev, bool changed)
+{
+	int err;
+
+	if (genphy_config_eee_advert(phydev))
+		changed = true;
+
+	err = genphy_setup_master_slave(phydev);
+	if (err < 0)
+		return err;
+	else if (err)
+		changed = true;
+
+	if (AUTONEG_ENABLE != phydev->autoneg)
+		return genphy_setup_forced(phydev);
+
+	err = genphy_config_advert(phydev);
+	if (err < 0) /* error */
+		return err;
+	else if (err)
+		changed = true;
+
+	return genphy_check_and_restart_aneg(phydev, changed);
+}
+EXPORT_SYMBOL(__genphy_config_aneg);
+
+/**
+ * genphy_c37_config_aneg - restart auto-negotiation or write BMCR
+ * @phydev: target phy_device struct
+ *
+ * Description: If auto-negotiation is enabled, we configure the
+ *   advertising, and then restart auto-negotiation.  If it is not
+ *   enabled, then we write the BMCR. This function is intended
+ *   for use with Clause 37 1000Base-X mode.
+ */
+int genphy_c37_config_aneg(struct phy_device *phydev)
+{
+	int err, changed;
+
+	if (phydev->autoneg != AUTONEG_ENABLE)
+		return genphy_setup_forced(phydev);
+
+	err = phy_modify(phydev, MII_BMCR, BMCR_SPEED1000 | BMCR_SPEED100,
+			 BMCR_SPEED1000);
+	if (err)
+		return err;
+
+	changed = genphy_c37_config_advert(phydev);
+	if (changed < 0) /* error */
+		return changed;
+
+	if (!changed) {
+		/* Advertisement hasn't changed, but maybe aneg was never on to
+		 * begin with?  Or maybe phy was isolated?
+		 */
+		int ctl = phy_read(phydev, MII_BMCR);
+
+		if (ctl < 0)
+			return ctl;
+
+		if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
+			changed = 1; /* do restart aneg */
+	}
+
+	/* Only restart aneg if we are advertising something different
+	 * than we were before.
+	 */
+	if (changed > 0)
+		return genphy_restart_aneg(phydev);
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_c37_config_aneg);
+
+/**
+ * genphy_aneg_done - return auto-negotiation status
+ * @phydev: target phy_device struct
+ *
+ * Description: Reads the status register and returns 0 either if
+ *   auto-negotiation is incomplete, or if there was an error.
+ *   Returns BMSR_ANEGCOMPLETE if auto-negotiation is done.
+ */
+int genphy_aneg_done(struct phy_device *phydev)
+{
+	int retval = phy_read(phydev, MII_BMSR);
+
+	return (retval < 0) ? retval : (retval & BMSR_ANEGCOMPLETE);
+}
+EXPORT_SYMBOL(genphy_aneg_done);
+
+/**
+ * genphy_update_link - update link status in @phydev
+ * @phydev: target phy_device struct
+ *
+ * Description: Update the value in phydev->link to reflect the
+ *   current link value.  In order to do this, we need to read
+ *   the status register twice, keeping the second value.
+ */
+int genphy_update_link(struct phy_device *phydev)
+{
+	int status = 0, bmcr;
+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 
+	bmcr = phy_read_cl(phydev, MII_BMCR);
+#else
+	bmcr = phy_read(phydev, MII_BMCR);
+#endif
+	if (bmcr < 0)
+		return bmcr;
+
+	/* Autoneg is being started, therefore disregard BMSR value and
+	 * report link as down.
+	 */
+	if (bmcr & BMCR_ANRESTART)
+		goto done;
+
+	/* The link state is latched low so that momentary link
+	 * drops can be detected. Do not double-read the status
+	 * in polling mode to detect such short link drops except
+	 * the link was already down.
+	 */
+	if (!phy_polling_mode(phydev) || !phydev->link) {
+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 
+		status = phy_read_cl(phydev, MII_BMSR);
+#else
+		status = phy_read(phydev, MII_BMSR);
+#endif
+		if (status < 0)
+			return status;
+		else if (status & BMSR_LSTATUS)
+			goto done;
+	}
+
+	/* Read link and autonegotiation status */
+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 
+	status = phy_read_cl(phydev, MII_BMSR);
+#else
+	status = phy_read(phydev, MII_BMSR);
+#endif
+	if (status < 0)
+		return status;
+done:
+	phydev->link = status & BMSR_LSTATUS ? 1 : 0;
+	phydev->autoneg_complete = status & BMSR_ANEGCOMPLETE ? 1 : 0;
+
+	/* Consider the case that autoneg was started and "aneg complete"
+	 * bit has been reset, but "link up" bit not yet.
+	 */
+	if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete)
+		phydev->link = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_update_link);
+
+int genphy_read_lpa(struct phy_device *phydev)
+{
+	int lpa, lpagb;
+
+	if (phydev->autoneg == AUTONEG_ENABLE) {
+		if (!phydev->autoneg_complete) {
+			mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
+							0);
+			mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
+			return 0;
+		}
+
+		if (phydev->is_gigabit_capable) {
+			lpagb = phy_read(phydev, MII_STAT1000);
+			if (lpagb < 0)
+				return lpagb;
+
+			if (lpagb & LPA_1000MSFAIL) {
+				int adv = phy_read(phydev, MII_CTRL1000);
+
+				if (adv < 0)
+					return adv;
+
+				if (adv & CTL1000_ENABLE_MASTER)
+					phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n");
+				else
+					phydev_err(phydev, "Master/Slave resolution failed\n");
+				return -ENOLINK;
+			}
+
+			mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
+							lpagb);
+		}
+
+		lpa = phy_read(phydev, MII_LPA);
+		if (lpa < 0)
+			return lpa;
+
+		mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
+	} else {
+		linkmode_zero(phydev->lp_advertising);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_read_lpa);
+
+/**
+ * genphy_read_status_fixed - read the link parameters for !aneg mode
+ * @phydev: target phy_device struct
+ *
+ * Read the current duplex and speed state for a PHY operating with
+ * autonegotiation disabled.
+ */
+int genphy_read_status_fixed(struct phy_device *phydev)
+{
+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 
+	int bmcr = phy_read_cl(phydev, MII_BMCR);
+#else
+	int bmcr = phy_read(phydev, MII_BMCR);
+#endif
+
+	if (bmcr < 0)
+		return bmcr;
+
+	if (bmcr & BMCR_FULLDPLX)
+		phydev->duplex = DUPLEX_FULL;
+	else
+		phydev->duplex = DUPLEX_HALF;
+
+	if (bmcr & BMCR_SPEED1000)
+		phydev->speed = SPEED_1000;
+	else if (bmcr & BMCR_SPEED100)
+		phydev->speed = SPEED_100;
+	else
+		phydev->speed = SPEED_10;
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_read_status_fixed);
+
+/**
+ * genphy_read_status - check the link status and update current link state
+ * @phydev: target phy_device struct
+ *
+ * Description: Check the link, then figure out the current state
+ *   by comparing what we advertise with what the link partner
+ *   advertises.  Start by checking the gigabit possibilities,
+ *   then move on to 10/100.
+ */
+int genphy_read_status(struct phy_device *phydev)
+{
+	int err, old_link = phydev->link;
+
+	/* Update the link, but return if there was an error */
+	err = genphy_update_link(phydev);
+	if (err)
+		return err;
+
+	/* why bother the PHY if nothing can have changed */
+	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
+		return 0;
+
+	phydev->speed = SPEED_UNKNOWN;
+	phydev->duplex = DUPLEX_UNKNOWN;
+	phydev->pause = 0;
+	phydev->asym_pause = 0;
+
+	err = genphy_read_master_slave(phydev);
+	if (err < 0)
+		return err;
+
+	err = genphy_read_lpa(phydev);
+	if (err < 0)
+		return err;
+
+	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
+		phy_resolve_aneg_linkmode(phydev);
+	} else if (phydev->autoneg == AUTONEG_DISABLE) {
+		err = genphy_read_status_fixed(phydev);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_read_status);
+
+/**
+ * genphy_c37_read_status - check the link status and update current link state
+ * @phydev: target phy_device struct
+ *
+ * Description: Check the link, then figure out the current state
+ *   by comparing what we advertise with what the link partner
+ *   advertises. This function is for Clause 37 1000Base-X mode.
+ */
+int genphy_c37_read_status(struct phy_device *phydev)
+{
+	int lpa, err, old_link = phydev->link;
+
+	/* Update the link, but return if there was an error */
+	err = genphy_update_link(phydev);
+	if (err)
+		return err;
+
+	/* why bother the PHY if nothing can have changed */
+	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
+		return 0;
+
+	phydev->duplex = DUPLEX_UNKNOWN;
+	phydev->pause = 0;
+	phydev->asym_pause = 0;
+
+	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
+		lpa = phy_read(phydev, MII_LPA);
+		if (lpa < 0)
+			return lpa;
+
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+				 phydev->lp_advertising, lpa & LPA_LPACK);
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+				 phydev->lp_advertising, lpa & LPA_1000XFULL);
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+				 phydev->lp_advertising, lpa & LPA_1000XPAUSE);
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+				 phydev->lp_advertising,
+				 lpa & LPA_1000XPAUSE_ASYM);
+
+		phy_resolve_aneg_linkmode(phydev);
+	} else if (phydev->autoneg == AUTONEG_DISABLE) {
+		int bmcr = phy_read(phydev, MII_BMCR);
+
+		if (bmcr < 0)
+			return bmcr;
+
+		if (bmcr & BMCR_FULLDPLX)
+			phydev->duplex = DUPLEX_FULL;
+		else
+			phydev->duplex = DUPLEX_HALF;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_c37_read_status);
+
+/**
+ * genphy_soft_reset - software reset the PHY via BMCR_RESET bit
+ * @phydev: target phy_device struct
+ *
+ * Description: Perform a software PHY reset using the standard
+ * BMCR_RESET bit and poll for the reset bit to be cleared.
+ *
+ * Returns: 0 on success, < 0 on failure
+ */
+int genphy_soft_reset(struct phy_device *phydev)
+{
+	u16 res = BMCR_RESET;
+	int ret;
+
+	if (phydev->autoneg == AUTONEG_ENABLE)
+		res |= BMCR_ANRESTART;
+
+	ret = phy_modify(phydev, MII_BMCR, BMCR_ISOLATE, res);
+	if (ret < 0)
+		return ret;
+
+	/* Clause 22 states that setting bit BMCR_RESET sets control registers
+	 * to their default value. Therefore the POWER DOWN bit is supposed to
+	 * be cleared after soft reset.
+	 */
+	phydev->suspended = 0;
+
+	ret = phy_poll_reset(phydev);
+	if (ret)
+		return ret;
+
+	/* BMCR may be reset to defaults */
+	if (phydev->autoneg == AUTONEG_DISABLE)
+		ret = genphy_setup_forced(phydev);
+
+	return ret;
+}
+EXPORT_SYMBOL(genphy_soft_reset);
+
+/**
+ * genphy_read_abilities - read PHY abilities from Clause 22 registers
+ * @phydev: target phy_device struct
+ *
+ * Description: Reads the PHY's abilities and populates
+ * phydev->supported accordingly.
+ *
+ * Returns: 0 on success, < 0 on failure
+ */
+int genphy_read_abilities(struct phy_device *phydev)
+{
+	int val;
+
+	linkmode_set_bit_array(phy_basic_ports_array,
+			       ARRAY_SIZE(phy_basic_ports_array),
+			       phydev->supported);
+
+	val = phy_read(phydev, MII_BMSR);
+	if (val < 0)
+		return val;
+
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported,
+			 val & BMSR_ANEGCAPABLE);
+
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported,
+			 val & BMSR_100FULL);
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported,
+			 val & BMSR_100HALF);
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported,
+			 val & BMSR_10FULL);
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported,
+			 val & BMSR_10HALF);
+
+	if (val & BMSR_ESTATEN) {
+		val = phy_read(phydev, MII_ESTATUS);
+		if (val < 0)
+			return val;
+
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+				 phydev->supported, val & ESTATUS_1000_TFULL);
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+				 phydev->supported, val & ESTATUS_1000_THALF);
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+				 phydev->supported, val & ESTATUS_1000_XFULL);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_read_abilities);
+
+/* This is used for the phy device which doesn't support the MMD extended
+ * register access, but it does have side effect when we are trying to access
+ * the MMD register via indirect method.
+ */
+int genphy_read_mmd_unsupported(struct phy_device *phdev, int devad, u16 regnum)
+{
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(genphy_read_mmd_unsupported);
+
+int genphy_write_mmd_unsupported(struct phy_device *phdev, int devnum,
+				 u16 regnum, u16 val)
+{
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(genphy_write_mmd_unsupported);
+
+int genphy_suspend(struct phy_device *phydev)
+{
+	return phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN);
+}
+EXPORT_SYMBOL(genphy_suspend);
+
+int genphy_resume(struct phy_device *phydev)
+{
+	return phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);
+}
+EXPORT_SYMBOL(genphy_resume);
+
+int genphy_loopback(struct phy_device *phydev, bool enable)
+{
+	return phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
+			  enable ? BMCR_LOOPBACK : 0);
+}
+EXPORT_SYMBOL(genphy_loopback);
+
+/**
+ * phy_remove_link_mode - Remove a supported link mode
+ * @phydev: phy_device structure to remove link mode from
+ * @link_mode: Link mode to be removed
+ *
+ * Description: Some MACs don't support all link modes which the PHY
+ * does.  e.g. a 1G MAC often does not support 1000Half. Add a helper
+ * to remove a link mode.
+ */
+void phy_remove_link_mode(struct phy_device *phydev, u32 link_mode)
+{
+	linkmode_clear_bit(link_mode, phydev->supported);
+	phy_advertise_supported(phydev);
+}
+EXPORT_SYMBOL(phy_remove_link_mode);
+
+static void phy_copy_pause_bits(unsigned long *dst, unsigned long *src)
+{
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, dst,
+		linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, src));
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, dst,
+		linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, src));
+}
+
+/**
+ * phy_advertise_supported - Advertise all supported modes
+ * @phydev: target phy_device struct
+ *
+ * Description: Called to advertise all supported modes, doesn't touch
+ * pause mode advertising.
+ */
+void phy_advertise_supported(struct phy_device *phydev)
+{
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(new);
+
+	linkmode_copy(new, phydev->supported);
+	phy_copy_pause_bits(new, phydev->advertising);
+	linkmode_copy(phydev->advertising, new);
+}
+EXPORT_SYMBOL(phy_advertise_supported);
+
+/**
+ * phy_support_sym_pause - Enable support of symmetrical pause
+ * @phydev: target phy_device struct
+ *
+ * Description: Called by the MAC to indicate is supports symmetrical
+ * Pause, but not asym pause.
+ */
+void phy_support_sym_pause(struct phy_device *phydev)
+{
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);
+	phy_copy_pause_bits(phydev->advertising, phydev->supported);
+}
+EXPORT_SYMBOL(phy_support_sym_pause);
+
+/**
+ * phy_support_asym_pause - Enable support of asym pause
+ * @phydev: target phy_device struct
+ *
+ * Description: Called by the MAC to indicate is supports Asym Pause.
+ */
+void phy_support_asym_pause(struct phy_device *phydev)
+{
+	phy_copy_pause_bits(phydev->advertising, phydev->supported);
+}
+EXPORT_SYMBOL(phy_support_asym_pause);
+
+/**
+ * phy_set_sym_pause - Configure symmetric Pause
+ * @phydev: target phy_device struct
+ * @rx: Receiver Pause is supported
+ * @tx: Transmit Pause is supported
+ * @autoneg: Auto neg should be used
+ *
+ * Description: Configure advertised Pause support depending on if
+ * receiver pause and pause auto neg is supported. Generally called
+ * from the set_pauseparam .ndo.
+ */
+void phy_set_sym_pause(struct phy_device *phydev, bool rx, bool tx,
+		       bool autoneg)
+{
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
+
+	if (rx && tx && autoneg)
+		linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+				 phydev->supported);
+
+	linkmode_copy(phydev->advertising, phydev->supported);
+}
+EXPORT_SYMBOL(phy_set_sym_pause);
+
+/**
+ * phy_set_asym_pause - Configure Pause and Asym Pause
+ * @phydev: target phy_device struct
+ * @rx: Receiver Pause is supported
+ * @tx: Transmit Pause is supported
+ *
+ * Description: Configure advertised Pause support depending on if
+ * transmit and receiver pause is supported. If there has been a
+ * change in adverting, trigger a new autoneg. Generally called from
+ * the set_pauseparam .ndo.
+ */
+void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx)
+{
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(oldadv);
+
+	linkmode_copy(oldadv, phydev->advertising);
+	linkmode_set_pause(phydev->advertising, tx, rx);
+
+	if (!linkmode_equal(oldadv, phydev->advertising) &&
+	    phydev->autoneg)
+		phy_start_aneg(phydev);
+}
+EXPORT_SYMBOL(phy_set_asym_pause);
+
+/**
+ * phy_validate_pause - Test if the PHY/MAC support the pause configuration
+ * @phydev: phy_device struct
+ * @pp: requested pause configuration
+ *
+ * Description: Test if the PHY/MAC combination supports the Pause
+ * configuration the user is requesting. Returns True if it is
+ * supported, false otherwise.
+ */
+bool phy_validate_pause(struct phy_device *phydev,
+			struct ethtool_pauseparam *pp)
+{
+	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+			       phydev->supported) && pp->rx_pause)
+		return false;
+
+	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+			       phydev->supported) &&
+	    pp->rx_pause != pp->tx_pause)
+		return false;
+
+	return true;
+}
+EXPORT_SYMBOL(phy_validate_pause);
+
+/**
+ * phy_get_pause - resolve negotiated pause modes
+ * @phydev: phy_device struct
+ * @tx_pause: pointer to bool to indicate whether transmit pause should be
+ * enabled.
+ * @rx_pause: pointer to bool to indicate whether receive pause should be
+ * enabled.
+ *
+ * Resolve and return the flow control modes according to the negotiation
+ * result. This includes checking that we are operating in full duplex mode.
+ * See linkmode_resolve_pause() for further details.
+ */
+void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause)
+{
+	if (phydev->duplex != DUPLEX_FULL) {
+		*tx_pause = false;
+		*rx_pause = false;
+		return;
+	}
+
+	return linkmode_resolve_pause(phydev->advertising,
+				      phydev->lp_advertising,
+				      tx_pause, rx_pause);
+}
+EXPORT_SYMBOL(phy_get_pause);
+
+#if IS_ENABLED(CONFIG_OF_MDIO)
+static int phy_get_int_delay_property(struct device *dev, const char *name)
+{
+	s32 int_delay;
+	int ret;
+
+	ret = device_property_read_u32(dev, name, &int_delay);
+	if (ret)
+		return ret;
+
+	return int_delay;
+}
+#else
+static int phy_get_int_delay_property(struct device *dev, const char *name)
+{
+	return -EINVAL;
+}
+#endif
+
+/**
+ * phy_get_delay_index - returns the index of the internal delay
+ * @phydev: phy_device struct
+ * @dev: pointer to the devices device struct
+ * @delay_values: array of delays the PHY supports
+ * @size: the size of the delay array
+ * @is_rx: boolean to indicate to get the rx internal delay
+ *
+ * Returns the index within the array of internal delay passed in.
+ * If the device property is not present then the interface type is checked
+ * if the interface defines use of internal delay then a 1 is returned otherwise
+ * a 0 is returned.
+ * The array must be in ascending order. If PHY does not have an ascending order
+ * array then size = 0 and the value of the delay property is returned.
+ * Return -EINVAL if the delay is invalid or cannot be found.
+ */
+s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev,
+			   const int *delay_values, int size, bool is_rx)
+{
+	s32 delay;
+	int i;
+
+	if (is_rx) {
+		delay = phy_get_int_delay_property(dev, "rx-internal-delay-ps");
+		if (delay < 0 && size == 0) {
+			if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+			    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+				return 1;
+			else
+				return 0;
+		}
+
+	} else {
+		delay = phy_get_int_delay_property(dev, "tx-internal-delay-ps");
+		if (delay < 0 && size == 0) {
+			if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+			    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+				return 1;
+			else
+				return 0;
+		}
+	}
+
+	if (delay < 0)
+		return delay;
+
+	if (delay && size == 0)
+		return delay;
+
+	if (delay < delay_values[0] || delay > delay_values[size - 1]) {
+		phydev_err(phydev, "Delay %d is out of range\n", delay);
+		return -EINVAL;
+	}
+
+	if (delay == delay_values[0])
+		return 0;
+
+	for (i = 1; i < size; i++) {
+		if (delay == delay_values[i])
+			return i;
+
+		/* Find an approximate index by looking up the table */
+		if (delay > delay_values[i - 1] &&
+		    delay < delay_values[i]) {
+			if (delay - delay_values[i - 1] <
+			    delay_values[i] - delay)
+				return i - 1;
+			else
+				return i;
+		}
+	}
+
+	phydev_err(phydev, "error finding internal delay index for %d\n",
+		   delay);
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(phy_get_internal_delay);
+
+static bool phy_drv_supports_irq(struct phy_driver *phydrv)
+{
+	return phydrv->config_intr && phydrv->ack_interrupt;
+}
+
+/**
+ * phy_probe - probe and init a PHY device
+ * @dev: device to probe and init
+ *
+ * Description: Take care of setting up the phy_device structure,
+ *   set the state to READY (the driver's init function should
+ *   set it to STARTING if needed).
+ */
+static int phy_probe(struct device *dev)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	struct device_driver *drv = phydev->mdio.dev.driver;
+	struct phy_driver *phydrv = to_phy_driver(drv);
+	int err = 0;
+
+	phydev->drv = phydrv;
+
+	/* Disable the interrupt if the PHY doesn't support it
+	 * but the interrupt is still a valid one
+	 */
+	 if (!phy_drv_supports_irq(phydrv) && phy_interrupt_is_valid(phydev))
+		phydev->irq = PHY_POLL;
+
+	if (phydrv->flags & PHY_IS_INTERNAL)
+		phydev->is_internal = true;
+
+	mutex_lock(&phydev->lock);
+
+	/* Deassert the reset signal */
+	phy_device_reset(phydev, 0);
+
+	if (phydev->drv->probe) {
+		err = phydev->drv->probe(phydev);
+		if (err)
+			goto out;
+	}
+
+	/* Start out supporting everything. Eventually,
+	 * a controller will attach, and may modify one
+	 * or both of these values
+	 */
+	if (phydrv->features) {
+		linkmode_copy(phydev->supported, phydrv->features);
+	} else if (phydrv->get_features) {
+		err = phydrv->get_features(phydev);
+	} else if (phydev->is_c45) {
+		err = genphy_c45_pma_read_abilities(phydev);
+	} else {
+		err = genphy_read_abilities(phydev);
+	}
+
+	if (err)
+		goto out;
+
+	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+			       phydev->supported))
+		phydev->autoneg = 0;
+
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+			      phydev->supported))
+		phydev->is_gigabit_capable = 1;
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+			      phydev->supported))
+		phydev->is_gigabit_capable = 1;
+
+	of_set_phy_supported(phydev);
+	phy_advertise_supported(phydev);
+
+	/* Get the EEE modes we want to prohibit. We will ask
+	 * the PHY stop advertising these mode later on
+	 */
+	of_set_phy_eee_broken(phydev);
+
+	/* The Pause Frame bits indicate that the PHY can support passing
+	 * pause frames. During autonegotiation, the PHYs will determine if
+	 * they should allow pause frames to pass.  The MAC driver should then
+	 * use that result to determine whether to enable flow control via
+	 * pause frames.
+	 *
+	 * Normally, PHY drivers should not set the Pause bits, and instead
+	 * allow phylib to do that.  However, there may be some situations
+	 * (e.g. hardware erratum) where the driver wants to set only one
+	 * of these bits.
+	 */
+	if (!test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported) &&
+	    !test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported)) {
+		linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+				 phydev->supported);
+		linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+				 phydev->supported);
+	}
+
+	/* Set the state to READY by default */
+	phydev->state = PHY_READY;
+
+out:
+	/* Assert the reset signal */
+	if (err)
+		phy_device_reset(phydev, 1);
+
+	mutex_unlock(&phydev->lock);
+
+	return err;
+}
+
+static int phy_remove(struct device *dev)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+
+	cancel_delayed_work_sync(&phydev->state_queue);
+
+	mutex_lock(&phydev->lock);
+	phydev->state = PHY_DOWN;
+	mutex_unlock(&phydev->lock);
+
+	sfp_bus_del_upstream(phydev->sfp_bus);
+	phydev->sfp_bus = NULL;
+
+	if (phydev->drv && phydev->drv->remove)
+		phydev->drv->remove(phydev);
+
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
+
+	phydev->drv = NULL;
+
+	return 0;
+}
+
+/**
+ * phy_driver_register - register a phy_driver with the PHY layer
+ * @new_driver: new phy_driver to register
+ * @owner: module owning this PHY
+ */
+int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
+{
+	int retval;
+
+	/* Either the features are hard coded, or dynamically
+	 * determined. It cannot be both.
+	 */
+	if (WARN_ON(new_driver->features && new_driver->get_features)) {
+		pr_err("%s: features and get_features must not both be set\n",
+		       new_driver->name);
+		return -EINVAL;
+	}
+
+	new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY;
+	new_driver->mdiodrv.driver.name = new_driver->name;
+	new_driver->mdiodrv.driver.bus = &mdio_bus_type;
+	new_driver->mdiodrv.driver.probe = phy_probe;
+	new_driver->mdiodrv.driver.remove = phy_remove;
+	new_driver->mdiodrv.driver.owner = owner;
+	new_driver->mdiodrv.driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
+
+	retval = driver_register(&new_driver->mdiodrv.driver);
+	if (retval) {
+		pr_err("%s: Error %d in registering driver\n",
+		       new_driver->name, retval);
+
+		return retval;
+	}
+
+	pr_debug("%s: Registered new driver\n", new_driver->name);
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_driver_register);
+
+int phy_drivers_register(struct phy_driver *new_driver, int n,
+			 struct module *owner)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < n; i++) {
+		ret = phy_driver_register(new_driver + i, owner);
+		if (ret) {
+			while (i-- > 0)
+				phy_driver_unregister(new_driver + i);
+			break;
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL(phy_drivers_register);
+
+void phy_driver_unregister(struct phy_driver *drv)
+{
+	driver_unregister(&drv->mdiodrv.driver);
+}
+EXPORT_SYMBOL(phy_driver_unregister);
+
+void phy_drivers_unregister(struct phy_driver *drv, int n)
+{
+	int i;
+
+	for (i = 0; i < n; i++)
+		phy_driver_unregister(drv + i);
+}
+EXPORT_SYMBOL(phy_drivers_unregister);
+
+static struct phy_driver genphy_driver = {
+	.phy_id		= 0xffffffff,
+	.phy_id_mask	= 0xffffffff,
+	.name		= "Generic PHY",
+	.get_features	= genphy_read_abilities,
+	.suspend	= genphy_suspend,
+	.resume		= genphy_resume,
+	.set_loopback   = genphy_loopback,
+};
+
+static const struct ethtool_phy_ops phy_ethtool_phy_ops = {
+	.get_sset_count		= phy_ethtool_get_sset_count,
+	.get_strings		= phy_ethtool_get_strings,
+	.get_stats		= phy_ethtool_get_stats,
+	.start_cable_test	= phy_start_cable_test,
+	.start_cable_test_tdr	= phy_start_cable_test_tdr,
+};
+
+static int __init phy_init(void)
+{
+	int rc;
+
+	rc = mdio_bus_init();
+	if (rc)
+		return rc;
+
+	ethtool_set_ethtool_phy_ops(&phy_ethtool_phy_ops);
+	features_init();
+
+	rc = phy_driver_register(&genphy_c45_driver, THIS_MODULE);
+	if (rc)
+		goto err_c45;
+
+	rc = phy_driver_register(&genphy_driver, THIS_MODULE);
+	if (rc) {
+		phy_driver_unregister(&genphy_c45_driver);
+err_c45:
+		mdio_bus_exit();
+	}
+
+	return rc;
+}
+
+static void __exit phy_exit(void)
+{
+	phy_driver_unregister(&genphy_c45_driver);
+	phy_driver_unregister(&genphy_driver);
+	mdio_bus_exit();
+	ethtool_set_ethtool_phy_ops(NULL);
+}
+
+subsys_initcall(phy_init);
+module_exit(phy_exit);
diff --git a/upstream/linux-5.10/include/linux/mfd/zx234290.h b/upstream/linux-5.10/include/linux/mfd/zx234290.h
new file mode 100755
index 0000000..ea89815
--- /dev/null
+++ b/upstream/linux-5.10/include/linux/mfd/zx234290.h
@@ -0,0 +1,1130 @@
+/*
+ * zx234290.h  --  ZTE ZX234290
+ *
+ * Copyright 2016 ZTE Corporation.
+ *
+ * Author: yuxiang
+ *
+ *  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.
+ *
+ */
+
+#ifndef __LINUX_MFD_ZX234290_H
+#define __LINUX_MFD_ZX234290_H
+
+#include <linux/mutex.h>
+
+#define zx234290_rails(_name) "zx234290_"#_name
+
+#define u8 unsigned char
+
+/* LDOs */
+#define ZX234290_REG_LDO1	0
+#define ZX234290_REG_LDO2	1
+#define ZX234290_REG_LDO3	2
+#define ZX234290_REG_LDO4	3
+#define ZX234290_REG_LDO5	4
+#define ZX234290_REG_LDO6	5
+#define ZX234290_REG_LDO7	6
+#define ZX234290_REG_LDO8	7
+
+#define ZX234290_REG_LDO9	8
+#define ZX234290_REG_LDO10	9
+#define ZX234290_REG_LDO11	10
+
+/* DCDC's */
+#define ZX234290_REG_DCDC0	11
+#define ZX234290_REG_DCDC1	12
+#define ZX234290_REG_DCDC2	13
+#define ZX234290_REG_DCDC3	14
+#define ZX234290_REG_DCDC4	15
+
+/* ZX regulator type list */
+#ifndef ZX234290_PWR_FAUL_PROCESS
+#define ZX234290_PWR_FAUL_PROCESS
+#define 	ZX234290_INT_LDO_FAUL		0
+#define 	ZX234290_INT_BUCK_FAUL		1
+#endif
+
+#define	ZX234290_INT_EOADC			2			/* xxxx x100	*/
+#define	ZX234290_INT_PWRON_SHORT	3
+#define	ZX234290_INT_PWRON_LONG		4
+#define	ZX234290_INT_PWRON			5
+
+#define	ZX234290_INT_RTC_ALRM		8
+#define	ZX234290_INT_BATT_DET		10
+#define	ZX234290_INT_RTC_MIN		11
+#define	ZX234290_INT_RTC_HOUR		12
+
+#define ZX234290_NUM_IRQ			13
+
+#ifdef ZX234290_PWR_FAUL_PROCESS
+int zx234290_register_client(struct notifier_block *nb);
+int zx234290_unregister_client(struct notifier_block *nb);
+int zx234290_notifier_call_chain(unsigned long val, void *v);
+#endif
+
+#if 0
+/* External controls requests */
+enum zx234290_ext_control {
+	PWR_REQ_INPUT_NONE	= 0x00000000,
+	PWR_REQ_INPUT_PREQ1	= 0x00000001,
+	PWR_REQ_INPUT_PREQ2	= 0x00000002,
+	PWR_REQ_INPUT_PREQ3	= 0x00000004,
+	PWR_OFF_ON_SLEEP	= 0x00000008,
+	PWR_ON_ON_SLEEP		= 0x00000010,
+};
+#endif
+
+#if 0
+typedef enum
+{
+	RESET_TO_NORMAL,                 /*reset to idle*/
+	RESET_TO_CHARGER,                /*reset to charger*/
+	RESET_TO_ALRAM,               /*reset to alarm*/
+	RESET_TO_EXCEPTRESET,
+	MAX_RESET_TYPE,
+} T_ZDrvSys_RESET_TYPE;
+#endif
+
+typedef enum _T_ZDrvPmic_Enable{
+    PM_DISABLE = 0,
+    PM_ENABLE,
+    PM_ENABLE_NOT_SUPPORT = -100,
+    PM_ENABLE_MAX_STATUS = -255
+} T_ZDrvPmic_Enable;
+
+typedef enum _T_ZDrvPmic_NrmMode{
+    PM_NRMMODE_AUTO = 0,
+    PM_NRMMODE_PFM,
+    PM_NRMMODE_PWM,
+    PM_NRMMODE_NOT_SUPPORT = -100,
+    PM_NRMMODE_MAX_STATUS = -255
+}T_ZDrvPmic_NrmMode;
+
+typedef enum _T_ZDrvPmic_SlpMode{
+    PM_SLPMODE_AUTO_NORMAL = 0,    //auto in dcdc, normal in ldo
+    PM_SLPMODE_ECO_NRMV,           //normal voltage
+    PM_SLPMODE_ECO_SLPV,           //sleep voltage
+    PM_SLPMODE_OFF,                //OFF
+    PM_SLPMODE_NOT_SUPPORT = -100,
+    PM_SLPMODE_MAX_STATUS = -255
+}T_ZDrvPmic_SlpMode;
+
+//consumer
+typedef enum _T_ZDrvPmic_Regulator{
+    VCORE0 = 0,
+    VCORE1,
+    VDDR,
+    VMMC,
+    VSD0,
+    VSD1,
+    VIO_LO,
+    VIO_HI,
+    VUSB_0V9,
+    VUSB_3V3,
+    VPLL_LO,
+    VPLL_HI,
+    VSIM1,
+    VSIM2,
+    VRF_LO,
+    VRF_HI,
+    VRF_SW,
+    VPA,
+    VCTCXO1,
+    VCTCXO2,
+    VSSBUF,
+    VRTC,
+} T_ZDrvPmic_Regulator;
+
+typedef enum _T_ZDrvPmic_Vcore{
+    PM_VOLT_0_6750 = 0,
+    PM_VOLT_0_6875 ,
+    PM_VOLT_0_7000 ,
+    PM_VOLT_0_7125 ,
+    PM_VOLT_0_7250 = 0x04,
+    PM_VOLT_0_7375 ,
+    PM_VOLT_0_7500 ,
+    PM_VOLT_0_7625 ,
+    PM_VOLT_0_7750 = 0x08 ,
+    PM_VOLT_0_7875 ,
+    PM_VOLT_0_8000 ,
+    PM_VOLT_0_8125 ,
+    PM_VOLT_0_8250 = 0x0c,
+    PM_VOLT_0_8375 ,
+    PM_VOLT_0_8500 ,
+    PM_VOLT_0_8625 ,
+    PM_VOLT_0_8750 = 0x10 ,
+    PM_VOLT_0_8875 ,
+    PM_VOLT_0_9000 ,
+    PM_VOLT_0_9125 ,
+    PM_VOLT_0_9250 = 0x14,
+    PM_VOLT_0_9375 ,
+    PM_VOLT_0_9500 ,
+    PM_VOLT_0_9625 ,
+    PM_VOLT_0_9750 = 0x18 ,
+    PM_VOLT_0_9875 ,
+    PM_VOLT_1_0000 ,
+    PM_VOLT_1_0125 ,
+    PM_VOLT_1_0250 = 0x1c,
+    PM_VOLT_1_0375 ,
+    PM_VOLT_1_0500 ,
+    PM_VOLT_1_0625 ,
+    PM_VOLT_1_0750 = 0x20 ,
+    PM_VOLT_1_0875 ,
+    PM_VOLT_1_1000 ,
+    PM_VOLT_1_1125 ,
+    PM_VOLT_1_1250 = 0x24,
+    PM_VOLT_1_1375 ,
+    PM_VOLT_1_1500 ,
+    PM_VOLT_1_1625 ,
+    PM_VOLT_1_1750 = 0x28 ,
+    PM_VOLT_1_1875 ,
+    PM_VOLT_1_2000 ,
+    PM_VOLT_1_2125 ,
+    PM_VOLT_1_2250 = 0x2c,
+    PM_VOLT_1_2375 ,
+    PM_VOLT_1_2500  = 0x2e,
+    PM_VOLT_1_2625 ,
+    PM_VOLT_1_2750 = 0x30 ,
+    PM_VOLT_1_2875 ,
+    PM_VOLT_1_3000 ,
+    PM_VOLT_1_3125 ,
+    PM_VOLT_1_3250 = 0x34,
+    PM_VOLT_1_3375 ,
+    PM_VOLT_1_3500  ,
+    PM_VOLT_1_3625 ,
+    PM_VOLT_1_3750 = 0x38 ,
+    PM_VOLT_1_3875 ,
+    PM_VOLT_1_4000 ,
+    PM_VOLT_1_4125 ,
+    PM_VOLT_1_4250 = 0x3c,
+    PM_VOLT_1_4375 ,
+    PM_VOLT_1_4500  ,
+    PM_VOLT_1_4625 ,
+    PM_VOLT_1_4750 = 0x40 ,
+    PM_VOLT_1_4875 ,
+    PM_VOLT_1_5000 ,
+    PM_VOLT_1_5125 ,
+    PM_VOLT_1_5250 = 0x44,
+    PM_VOLT_1_5375 ,
+    PM_VOLT_1_5500  ,
+    PM_VOLT_1_5625 ,
+    PM_VOLT_1_5750 = 0x48,
+    PM_VOLT_1_5875 ,
+    PM_VOLT_1_6000 ,
+    PM_VOLT_1_6125 ,
+    PM_VOLT_1_6250 = 0x4c,
+    PM_VOLT_1_6375 ,
+    PM_VOLT_1_6500  ,
+    PM_VOLT_1_6625 ,
+    PM_VOLT_1_6750 = 0x50,
+    PM_VOLT_1_6875 ,
+    PM_VOLT_1_7000 ,
+    PM_VOLT_1_7125 ,
+    PM_VOLT_1_7250 = 0x54,
+    PM_VOLT_1_7375 ,
+    PM_VOLT_1_7500  ,
+    PM_VOLT_1_7625 ,
+    PM_VOLT_1_7750 = 0x58,
+    PM_VOLT_1_7875 ,
+    PM_VOLT_1_8000 ,
+    PM_VOLT_1_8125 ,
+    PM_VOLT_1_8250 = 0x5c,
+    PM_VOLT_1_8375 ,
+    PM_VOLT_1_8500  ,
+    PM_VOLT_1_8625 ,
+    PM_VOLT_1_8750 = 0x60 ,
+    PM_VOLT_1_8875 ,
+    PM_VOLT_1_9000 ,
+    PM_VOLT_1_9125 ,
+    PM_VOLT_1_9250 = 0x64,
+    PM_VOLT_1_9375 ,
+    PM_VOLT_1_9500  ,
+    PM_VOLT_1_9625 ,
+    PM_VOLT_1_9750 = 0x68,
+    PM_VOLT_1_9875 ,
+    PM_VOLT_2_0000 ,
+    PM_VOLT_2_0125 ,
+    PM_VOLT_2_0250 = 0x6c,
+    PM_VOLT_2_0375 ,
+    PM_VOLT_2_0500  ,
+    PM_VOLT_2_0625 ,
+    PM_VOLT_2_0750 = 0x70,
+    PM_VOLT_2_0875 ,
+    PM_VOLT_2_1000 ,
+    PM_VOLT_2_1125 ,
+    PM_VOLT_2_1250 = 0x74,
+    PM_VOLT_2_1375 ,
+    PM_VOLT_2_1500  ,
+    PM_VOLT_2_1625 ,
+    PM_VOLT_2_1750 = 0x78 ,
+    PM_VOLT_2_1875 ,
+    PM_VOLT_2_2000 ,
+    PM_VOLT_2_2125 ,
+    PM_VOLT_2_2250 = 0x7c,
+    PM_VOLT_2_2375 ,
+    PM_VOLT_2_2500  ,
+    PM_VOLT_2_2625 ,
+    PM_VOLT_2_2750 = 0x80,
+    PM_VOLT_2_2875 ,
+    PM_VOLT_2_3000 ,
+    PM_VOLT_2_3125 ,
+    PM_VOLT_2_3250 = 0x84,
+    PM_VOLT_2_3375 ,
+    PM_VOLT_2_3500  ,
+    PM_VOLT_2_3625 ,
+    PM_VOLT_2_3750 = 0x88 ,
+    PM_VOLT_2_3875 ,
+    PM_VOLT_2_4000 ,
+    PM_VOLT_2_4125 ,
+    PM_VOLT_2_4250 = 0x8c,
+    PM_VOLT_2_4375 ,
+    PM_VOLT_2_4500  ,
+    PM_VOLT_2_4625 ,
+    PM_VOLT_2_4750 = 0x90,
+    PM_VOLT_2_4875 ,
+    PM_VOLT_2_5000 ,
+    PM_VOLT_2_5125 ,
+    PM_VOLT_2_5250 = 0x94,
+    PM_VOLT_2_5375 ,
+    PM_VOLT_2_5500  ,
+    PM_VOLT_2_5625 ,
+    PM_VOLT_2_5750 = 0x98 ,
+    PM_VOLT_2_5875 ,
+    PM_VOLT_2_6000 ,
+    PM_VOLT_2_6125 ,
+    PM_VOLT_2_6250 = 0x9c,
+    PM_VOLT_2_6375 ,
+    PM_VOLT_2_6500  ,
+    PM_VOLT_2_6625 ,
+    PM_VOLT_2_6750 = 0xa0,
+    PM_VOLT_2_6875 ,
+    PM_VOLT_2_7000 ,
+    PM_VOLT_2_7125 ,
+    PM_VOLT_2_7250 = 0xa4,
+    PM_VOLT_2_7375 ,
+    PM_VOLT_2_7500  ,
+    PM_VOLT_2_7625 ,
+    PM_VOLT_2_7750 = 0xa8,
+    PM_VOLT_2_7875 ,
+    PM_VOLT_2_8000 ,
+    PM_VOLT_2_8125 ,
+    PM_VOLT_2_8250 = 0xac,
+    PM_VOLT_2_8375 ,
+    PM_VOLT_2_8500  ,
+    PM_VOLT_2_8625 ,
+    PM_VOLT_2_8750 = 0xb0,
+    PM_VOLT_2_8875 ,
+    PM_VOLT_2_9000 ,
+    PM_VOLT_2_9125 ,
+    PM_VOLT_2_9250 = 0xb4,
+    PM_VOLT_2_9375 ,
+    PM_VOLT_2_9500  ,
+    PM_VOLT_2_9625 ,
+    PM_VOLT_2_9750 = 0xb8,
+    PM_VOLT_2_9875 ,
+    PM_VOLT_3_0000 ,
+    PM_VOLT_3_0125 ,
+    PM_VOLT_3_0250 = 0xbc,
+    PM_VOLT_3_0375 ,
+    PM_VOLT_3_0500  ,
+    PM_VOLT_3_0625 ,
+    PM_VOLT_3_0750 = 0xc0 ,
+    PM_VOLT_3_0875 ,
+    PM_VOLT_3_1000 ,
+    PM_VOLT_3_1125 ,
+    PM_VOLT_3_1250 = 0xc4,
+    PM_VOLT_3_1375 ,
+    PM_VOLT_3_1500  ,
+    PM_VOLT_3_1625 ,
+    PM_VOLT_3_1750 = 0xc8 ,
+    PM_VOLT_3_1875 ,
+    PM_VOLT_3_2000 ,
+    PM_VOLT_3_2125 ,
+    PM_VOLT_3_2250 = 0xcc,
+    PM_VOLT_3_2375 ,
+    PM_VOLT_3_2500  ,
+    PM_VOLT_3_2625 ,
+    PM_VOLT_3_2750 = 0xd0 ,
+    PM_VOLT_3_2875 ,
+    PM_VOLT_3_3000 ,
+    PM_VOLT_3_3125 ,
+    PM_VOLT_3_3250 = 0xd4,
+    PM_VOLT_3_3375 ,
+    PM_VOLT_3_3500  ,
+    PM_VOLT_3_3625 ,
+    PM_VOLT_3_3750 = 0xd8 ,
+    PM_VOLT_3_3875 ,
+
+    PM_VOLT_NOT_SUPPORT = -100,
+    PM_VOLT_MAX_STATUS = -255,
+ } T_ZDrvPmic_Voltage;
+
+
+
+
+/**
+ * struct zx234290 - zx234290 sub-driver chip access routines
+ */
+
+struct zx234290 {
+	struct device *dev;
+	/* for read/write acces */
+	struct mutex io_mutex;
+
+	/* For device IO interfaces: I2C or SPI */
+	void *control_data;
+
+	int (*read)(struct zx234290 *zx234290, u8 reg, int size, void *dest);
+	int (*write)(struct zx234290 *zx234290, u8 reg, int size, void *src);
+
+	/* Client devices */
+	struct zx234290_regulator *regulator;
+
+	/* GPIO Handling */
+
+	/* IRQ Handling */
+	struct mutex irq_lock;
+	int chip_irq;
+	int irq_base;
+	struct irq_domain * irq_domain;
+	int irq_num;
+	unsigned int irq_mask;
+};
+int zx234290_i2c_read_simple(u8 reg, void *dest);
+int zx234290_i2c_write_simple(u8 reg, void *src);
+int zx234290_i2c_read_simple_PSM(u8 reg, void *dest);
+int zx234290_i2c_write_simple_PSM(u8 reg, void *src);
+
+int zx234290_reg_read(struct zx234290 *zx234290, u8 reg);
+int zx234290_reg_write(struct zx234290 *zx234290, u8 reg, u8 val);
+int zx234290_device_init(struct zx234290 *zx234290);
+void zx234290_device_exit(struct zx234290 *zx234290);
+
+
+/*regulator defines*/
+#if 1
+/*
+ * List of registers for ZX234290
+*/
+
+/////////////////////////////////////////////////
+/*slave address 0x12*/
+/////////////////////////////////////////////////
+#define ZX234290_I2C_SLAVE_ADDR0   			(0x12)
+
+    /*  interrupt and mask */
+#define ZX234290_REG_ADDR_INTA         		0x00    /* INTERRUPT */
+#define ZX234290_REG_ADDR_INTB          	0x01
+#define ZX234290_REG_ADDR_INTA_MASK    		0x02
+#define ZX234290_REG_ADDR_INTB_MASK   		0x03
+
+    /* interrupt status */
+#define ZX234290_REG_ADDR_STSA        		0x04
+#define ZX234290_REG_ADDR_STSB       		0x05
+#define ZX234290_REG_ADDR_STS_STARTUP  		0x06
+
+    /* adc & softon select  */
+#define ZX234290_REG_ADDR_SYS_CTRL        	0x07  /*0x8 0x9Ìø¹ý*/
+
+    /* bucks normal voltage and sleep voltage   */
+#define ZX234290_REG_ADDR_BUCK1_VOL        	0x0A  /*[00xx xxxx]0xB 0xC Ìø¹ý*/
+#define ZX234290_REG_ADDR_BUCK1_SLPVOL    	0x0D
+
+    /* bucks mode   */
+#define ZX234290_REG_ADDR_BUCK1_MODE       0x0E  	/* [xx] NRM [xx] SLP [00 00]*/
+#define ZX234290_REG_ADDR_BUCK23_MODE       0x0F    /*[xx]BUCK3 NRM [xx]BUCK3 SLP [xx]BUCK2 NRM [xx]BUCK2 SLP*/
+#define ZX234290_REG_ADDR_BUCK4_MODE       	0x11	/* [00 00] [xx] NRM [xx] SLP   0X10Ìø¹ý	*/
+
+    /* ldo normal voltage   */
+#define ZX234290_REG_ADDR_LDO12_VOL         0x12	/* [xxxx xxxx] */
+#define ZX234290_REG_ADDR_LDO34_VOL         0x13
+#define ZX234290_REG_ADDR_LDO56_VOL       	0x14
+#define ZX234290_REG_ADDR_LDO78_VOL         0x15
+#define ZX234290_REG_ADDR_LDO9_VOL          0x16    /* [xxxx 0000] */
+#define ZX234290_REG_ADDR_LDO10_RTCLDO_VOL  0x17	/* [00 xx]VORTC [xx xx]LDO10*/
+
+
+#define ZX234290_REG_ADDR_BUCK2_VOL        	0x1A	/* BUCK2 VLOT	*/
+
+    /* ldo sleep voltage    */
+#define ZX234290_REG_ADDR_LDO12_SLPVOL     	0x18	/* [xx xx]ldo2  [xx xx]ldo1*/
+#define ZX234290_REG_ADDR_LDO3_SLPVOL       0x19	/* [00 00] [xx xx] */
+#define ZX234290_REG_ADDR_LDO78_SLPVOL     	0x1B    /* [xx xx]ldo8  [xx xx]ldo7*/
+#define ZX234290_REG_ADDR_LDO9_SLPVOL       0x1C    /* [xx xx] [00 00] */
+#define ZX234290_REG_ADDR_LDO10_SLPVOL      0x1D    /* [00 00] [xx xx] */
+
+    /* ldo mode */
+#define ZX234290_REG_ADDR_LDO1234_MODE   	0x1E    /* [xx][xx][xx][xx]*/
+#define ZX234290_REG_ADDR_LDO5678_MODE      0x1F
+#define ZX234290_REG_ADDR_LDO910_MODE       0x20	/* [00] [xx] [xx] [00] */
+
+    /* ldo enable   */
+#define ZX234290_REG_ADDR_LDO_EN1			0x21	/* LDO8-1 */
+#define ZX234290_REG_ADDR_LDO_EN2			0x22	/* [xx xx]BUCK4-1, [0xx0]LDO10-9*/
+
+    /* adc code */
+#define ZX234290_REG_ADDR_VBATADC_MSB		0x23    /*[xxxx xxxx]*/
+#define ZX234290_REG_ADDR_VBATADC_LSB		0x24    /*[xxxx 0000]*/
+#define ZX234290_REG_ADDR_ADC1_MSB			0x25
+#define ZX234290_REG_ADDR_ADC1_LSB			0x26
+#define ZX234290_REG_ADDR_ADC2_MSB			0x27
+#define ZX234290_REG_ADDR_ADC2_LSB			0x28
+
+    /* sink control */
+#define ZX234297_REG_ADDR_SINK_CONTROL		0x29
+
+    /* rtc */
+#define ZX234290_REG_ADDR_RTC_CTRL1			0x30
+#define ZX234290_REG_ADDR_RTC_CTRL2			0x31
+
+    /* date and time */
+#define ZX234290_REG_ADDR_SECONDS         	0x32
+#define ZX234290_REG_ADDR_MINUTES         	0x33
+#define ZX234290_REG_ADDR_HOURS           	0x34
+#define ZX234290_REG_ADDR_DAY             	0x35
+#define ZX234290_REG_ADDR_WEEK            	0x36
+#define ZX234290_REG_ADDR_MONTH           	0x37
+#define ZX234290_REG_ADDR_YEAR            	0x38
+
+    /* alarm */
+#define ZX234290_REG_ADDR_ALARM_MINUTE      0x39
+#define ZX234290_REG_ADDR_ALARM_HOUR  		0x3A
+#define ZX234290_REG_ADDR_ALARM_DAY        	0x3B
+#define ZX234290_REG_ADDR_ALARM_WEEK      	0x3C
+#define ZX234290_REG_ADDR_ALARM_SECOND     	0x3D
+
+#define ZX234290_REG_ADDR_TIMER_CTRL		0x3E
+#define ZX234290_REG_ADDR_TIMER_CNT			0x3F
+
+    /* enable ldo output discharge resistance */
+#define ZX234290_REG_ADDR_EN_DISCH1			0x40
+#define ZX234290_REG_ADDR_EN_DISCH2			0x41
+
+    /* power key control */
+#define ZX234290_REG_ADDR_PWRKEY_CONTROL1	0x42
+#define ZX234290_REG_ADDR_PWRKEY_CONTROL2   0x43
+
+#define ZX234290_REG_ADDR_VERSION           0x44
+
+    /*fault status*/
+#define ZX234290_REG_ADDR_BUCK_FAULT_STATUS 0x45
+#define ZX234290_REG_ADDR_LDO_FAULT_STATUS  0x46
+
+#define ZX234290_REG_ADDR_BUCK_INT_MASK     0x47
+#define ZX234290_REG_ADDR_LDO_INT_MASK      0x48
+
+#define ZX234290_REG_ADDR_USER_RESERVED     0x50
+#define ZX234290_REG_ADDR_GMT_TESTING       0xf1
+
+#define ZX234290_MAX_REGISTER		        0x51 //yuxiang ?
+
+/*0x04 status A*/
+#define ZX234290_STATUSA_POWERON_LSH           	(5)
+#define ZX234290_STATUSA_POWERON_WID            (1)
+#define ZX234290_STATUSA_EOCADC_LSH           	(2)
+#define ZX234290_STATUSA_EOCADC_WID             (1)
+
+/* 0x06  STATUS REG -- STARTUP */
+#define ZX234290_SYSPOR_STATUS_PWRON_STARTUP        (0x1 << 0)  /* PWR ON button */
+#define ZX234290_SYSPOR_STATUS_RTC_ALARM_STARTUP 	(0x1 << 1)
+#define ZX234290_SYSPOR_STATUS_PSHOLD_STARTUP       (0x1 << 2)
+#define ZX234290_SYSPOR_STATUS_PWRONLLP_STARTUP  	(0x1 << 3)
+
+/* discharger	*/
+#define ZX234290_DISCHG1_LSB_LSH           	(0)
+#define ZX234290_DISCHG1_LSB_WID            (4)
+
+#define ZX234290_DISCHG1_MSB_LSH           	(5)
+#define ZX234290_DISCHG1_MSB_WID            (2)
+
+#define ZX234290_DISCHG2_LSH           	    (0)
+#define ZX234290_DISCHG2_WID                (8)
+
+
+/* BUCK VOLTAGE */
+#define ZX234290_BUCK01_VSEL_LSH           	(0)
+#define ZX234290_BUCK01_VSEL_WID            (6)
+
+/* BUCK SLEEP VOLTAGE */
+#define ZX234290_BUCK01_SLEEP_VSEL_LSH      (0)
+#define ZX234290_BUCK01_SLEEP_VSEL_WID      (6)
+
+/* BUCKS MODE CTROL	*/
+#define ZX234290_REGULATOR_MODE_WID         (2)
+
+#define ZX234290_BUCK0_SLPMODE_LSH          (0)
+#define ZX234290_BUCK0_NRMMODE_LSH          (2)
+#define ZX234290_BUCK1_SLPMODE_LSH          (4)
+#define ZX234290_BUCK1_NRMMODE_LSH          (6)	/*[7:6]*/
+#define ZX234290_BUCK2_SLPMODE_LSH          (0)
+#define ZX234290_BUCK2_NRMMODE_LSH          (2)
+#define ZX234290_BUCK3_SLPMODE_LSH          (4)
+#define ZX234290_BUCK3_NRMMODE_LSH          (6)
+#define ZX234290_BUCK4_SLPMODE_LSH          (0)
+#define ZX234290_BUCK4_NRMMODE_LSH          (2)
+
+/* LDO MODE, ONLY SLEEP MODE	 */
+#define ZX234290_LDO1_SLPMODE_LSH          	(0)
+#define ZX234290_LDO2_SLPMODE_LSH          	(2)
+#define ZX234290_LDO3_SLPMODE_LSH          	(4)
+#define ZX234290_LDO4_SLPMODE_LSH          	(6)
+#define ZX234290_LDO5_SLPMODE_LSH          	(0)
+#define ZX234290_LDO6_SLPMODE_LSH          	(2)
+#define ZX234290_LDO7_SLPMODE_LSH          	(4)
+#define ZX234290_LDO8_SLPMODE_LSH          	(6)
+#define ZX234290_LDO9_SLPMODE_LSH          	(2)
+#define ZX234290_LDO10_SLPMODE_LSH         	(4)
+//#define ZX234290_LDO11_SLPMODE_LSH         	(6)
+
+/* LDO VOLTAGE SELECT */
+#define ZX234290_LDO_VSEL_WID               (4)
+
+#define ZX234290_LDO1_VSEL_LSH           	(0)	/* [3:0]	*/
+#define ZX234290_LDO2_VSEL_LSH              (4)	/* [7:4]	*/
+#define ZX234290_LDO3_VSEL_LSH              (0)
+#define ZX234290_LDO4_VSEL_LSH              (4)
+#define ZX234290_LDO5_VSEL_LSH              (0)
+#define ZX234290_LDO6_VSEL_LSH              (4)
+#define ZX234290_LDO7_VSEL_LSH              (0)
+#define ZX234290_LDO8_VSEL_LSH              (4)
+#define ZX234290_LDO9_VSEL_LSH              (4)
+#define ZX234290_LDO10_VSEL_LSH             (0)
+#define ZX234290_LDO11_VSEL_LSH             (0)	/* [3:0]	*/
+
+#define ZX234290_VORTC_VSEL_WID             (2)
+#define ZX234290_VORTC_VSEL_LSH             (4)	/* [5][4]	*/
+#define ZX234290_LDO5_VSEL_WID              (2) /* [1][0]*/
+
+
+/* LDO SLEEP VOLTAGE	*/
+#define ZX234290_BUCK2_VSEL_WID             (5)
+
+#define ZX234290_BUCK2_VSEL_LSH         	(0)
+
+#define ZX234290_LDO1_SLP_VSEL_LSH   		(0)	/* [3:0]	*/
+#define ZX234290_LDO2_SLP_VSEL_LSH          (4)	/* [7:4]	*/
+#define ZX234290_LDO3_SLP_VSEL_LSH          (0)
+#define ZX234290_LDO7_SLP_VSEL_LSH          (0)
+#define ZX234290_LDO8_SLP_VSEL_LSH          (0)
+#define ZX234290_LDO11_SLP_VSEL_LSH         (0)	/* [3:0]	*/
+
+/* ENABLE 0x21-0x22 */
+#define ZX234290_LDOS_ON_WID                (1)
+
+#define ZX234290_LDO1_ON_LSH               	(0)
+#define ZX234290_LDO2_ON_LSH                (1)
+#define ZX234290_LDO3_ON_LSH                (2)
+#define ZX234290_LDO4_ON_LSH                (3)
+#define ZX234290_LDO5_ON_LSH                (4)
+#define ZX234290_LDO6_ON_LSH                (5)
+#define ZX234290_LDO7_ON_LSH                (6)
+#define ZX234290_LDO8_ON_LSH                (7)
+
+#define ZX234290_LDO9_ON_LSH                (1)
+#define ZX234297_LDO9_ON_LSH                (0)
+#define ZX234290_LDO10_ON_LSH               (2)
+#define ZX234297_LDO10_ON_LSH               (1)
+#define ZX234290_BUCK1_ON_LSH               (4)
+#define ZX234290_BUCK2_ON_LSH               (5)
+#define ZX234290_BUCK3_ON_LSH               (6)
+#define ZX234290_BUCK4_ON_LSH               (7)
+
+/* LONG PRESSED TIME	*/
+#define ZX234290_PWRON_TIME_LSH				(0)
+#define ZX234290_PWRON_TIME_WID				(2)
+#define ZX234290_PWRON_LONGPRESS_EN_LSH		(2)
+#define ZX234290_PWRON_LONGPRESS_EN_WID		(1)
+#define ZX234290_PWRON_LLP_TODO_LSH			(3)	/* LLP long long pressed */
+#define ZX234290_PWRON_LLP_TODO_WID			(1)
+
+/* sys ctrol 0x07	*/
+#define ZX234290_SINK1_EN_LSH				(0)
+#define ZX234290_SINK1_EN_WID				(1)
+#define ZX234290_SINK2_EN_LSH				(1)
+#define ZX234290_SINK2_EN_WID				(1)
+#define ZX234290_ADC1_EN_LSH				(4)
+#define ZX234290_ADC1_EN_WID				(1)
+#define ZX234290_ADC2_EN_LSH				(3)
+#define ZX234290_ADC2_EN_WID				(1)
+#define ZX234290_ADC_START_LSH				(5)
+#define ZX234290_ADC_START_WID				(1)
+#define ZX234290_SOFTON_LSH					(7)
+
+/* 0x08	*/
+#define ZX234290_SINK2_CURSEL_LSH           (0)
+#define ZX234290_SINK2_CURSEL_WID           (4)
+/* 0x09 */
+#define ZX234290_SINK1_CURSEL_LSH           (0)
+#define ZX234290_SINK1_CURSEL_WID           (4)
+
+/* 0x20	*/
+#define ZX234297_SINK1_SLP_MODE_LSH			(6)
+#define ZX234297_SINK2_SLP_MODE_LSH			(7)
+#define ZX234297_SINK_SLP_MODE_WID			(1)
+/* 0x22 */
+#define ZX234297_SINK1_ON_LSH				(2)
+#define ZX234297_SINK2_ON_LSH				(3)
+#define ZX234297_SINK_ON_WID				(1)
+/* 0x29 */
+#define ZX234297_SINK1_CURRENT_LSH			(0)
+#define ZX234297_SINK2_CURRENT_LSH			(4)
+#define ZX234297_SINK_CURRENT_WID			(4)
+
+#define ZX234290_LDO_RSTERR_LSH		(0)
+#define ZX234290_LDO_RSTERR_WID		(1)
+
+#endif  /* end of ZX234290 */
+
+#define ZX234290_BITFVAL(var, lsh)   ( (var) << (lsh) )
+#define ZX234290_BITFMASK(wid, lsh)  ( ((1U << (wid)) - 1) << (lsh) )
+#define ZX234290_BITFEXT(var, wid, lsh)   ((var & ZX234290_BITFMASK(wid, lsh)) >> (lsh))
+
+/* VBA - BUCK1 	6bit */
+typedef enum _T_ZDrvZx234290_VbuckA
+{
+	VBUCKA_0_675 = 0x00,
+	VBUCKA_0_700 = 0x02,
+	VBUCKA_0_750 = 0x06,
+	VBUCKA_0_800 = 0x0a,
+	VBUCKA_0_850 = 0x0e,
+	VBUCKA_0_900 = 0x12,/*default*/
+	VBUCKA_0_950 = 0x16,
+    VBUCKA_1_000 = 0x1a,
+    VBUCKA_1_050 = 0x1e,
+    VBUCKA_1_100 = 0x22,
+    VBUCKA_1_150 = 0x26,
+    VBUCKA_1_200 = 0x2a,
+    VBUCKA_1_250 = 0x2e,
+
+    VBUCKA_MAX
+
+}T_ZDrvZx234290_VbuckA;
+
+/* VBC - BUCK2 */
+typedef enum _T_ZDrvZx234290_VbuckC
+{
+    VBUCKC_0_850 = 0x00,
+	VBUCKC_0_900 = 0x02,
+	VBUCKC_0_950 = 0x04,
+	VBUCKC_1_000 = 0x06,
+	VBUCKC_1_050 = 0x08,
+	VBUCKC_1_100 = 0x0a,
+	VBUCKC_1_150 = 0x0c,
+    VBUCKC_1_200 = 0x0e,/*default*/
+    VBUCKC_1_250 = 0x10,
+    VBUCKC_1_300 = 0x12,
+    VBUCKC_1_350 = 0x14,
+    VBUCKC_1_400 = 0x16,
+    VBUCKC_1_450 = 0x18,
+    VBUCKC_1_500 = 0x1a,
+    VBUCKC_1_550 = 0x1c,
+    VBUCKC_1_600 = 0x1e,
+
+    VBUCKC_MAX
+
+}T_ZDrvZx234290_VbuckC;
+
+/* VLA - ldo1/9/10	*/
+typedef enum _T_ZDrvZx234290_VldoA
+{
+	VLDOA_0_725 = 0,
+	VLDOA_0_750 = 1,
+	VLDOA_0_775 = 2,
+	VLDOA_0_800 = 3,
+	VLDOA_0_825 = 4,
+	VLDOA_0_850 = 5,
+	VLDOA_0_875 = 6,
+    VLDOA_0_900 = 7,
+    VLDOA_0_925 = 8,
+    VLDOA_0_950 = 9,
+    VLDOA_0_975 = 10,
+    VLDOA_1_000 = 11,
+    VLDOA_1_025 = 12,
+    VLDOA_1_050 = 13,
+    VLDOA_1_075 = 14,
+    VLDOA_1_100 = 15,
+
+    VLDOA_MAX
+
+}T_ZDrvZx234290_VldoA;
+
+/* VLB - ldo5 2bit	*/
+typedef enum _T_ZDrvZx234290_VldoB
+{
+    VLDOB_3_300 = 0,
+    VLDOB_3_150 = 1,
+    VLDOB_3_000 = 2,
+    VLDOB_1_800 = 3,	/* 11	*/
+
+    VLDOB_MAX
+
+}T_ZDrvZx234290_VldoB;
+
+/* VLC - ldo2/ldo3	*/
+typedef enum _T_ZDrvZx234290_VldoC
+{
+	VLDOC_0_750 = 0,
+	VLDOC_0_800 = 1,
+	VLDOC_0_850 = 2,
+	VLDOC_0_900 = 3,
+    VLDOC_0_950 = 4,
+    VLDOC_1_000 = 5,
+    VLDOC_1_050 = 6,
+    VLDOC_1_100 = 7,
+    VLDOC_1_200 = 8,
+    VLDOC_1_500 = 9,
+    VLDOC_1_800 = 10,
+    VLDOC_2_000 = 11,
+    VLDOC_2_500 = 12,
+    VLDOC_2_800 = 13,
+    VLDOC_3_000 = 14,
+    VLDOC_3_300 = 15,
+
+    VLDOC_MAX
+
+}T_ZDrvZx234290_VldoC;
+
+/* VLD - ldo4/6/7/8	*/
+typedef enum _T_ZDrvZx234290_VldoD
+{
+    VLDOD_1_400 = 0,
+	VLDOD_1_500 = 1,
+	VLDOD_1_600 = 2,
+	VLDOD_1_800 = 3,
+	VLDOD_1_850 = 4,
+	VLDOD_2_000 = 5,
+	VLDOD_2_050 = 6,
+    VLDOD_2_500 = 7,
+    VLDOD_2_550 = 8,
+    VLDOD_2_700 = 9,
+    VLDOD_2_750 = 10,
+    VLDOD_2_800 = 11,
+    VLDOD_2_850 = 12,
+    VLDOD_2_900 = 13,
+    VLDOD_2_950 = 14,
+    VLDOD_3_000 = 15,
+
+    VLDOD_MAX
+
+}T_ZDrvZx234290_VldoD;
+
+/*  VORTC 2bit	*/
+typedef enum _T_ZDrvZx234290_VldoE
+{
+    VLDOE_1_800 = 0,
+    VLDOE_2_500 = 1,
+    VLDOE_3_000 = 2,
+    VLDOE_3_300 = 3,	/* 11	*/
+
+    VLDOE_MAX
+
+}T_ZDrvZx234290_VldoE;
+
+/* VLF - ldo10	*/
+typedef enum _T_ZDrvZx234297_VldoF
+{
+    VLDOF_0_800 = 0,
+	VLDOF_0_850 = 1,
+	VLDOF_0_900 = 2,
+	VLDOF_0_950 = 3,
+
+	VLDOF_1_000 = 4,
+	VLDOF_1_050 = 5,
+	VLDOF_1_100 = 6,
+    VLDOF_1_200 = 7,
+
+    VLDOF_1_300 = 8,
+    VLDOF_1_400 = 9,
+    VLDOF_1_500 = 10,
+    VLDOF_1_800 = 11,
+
+    VLDOF_2_500 = 12,
+    VLDOF_2_800 = 13,
+    VLDOF_3_000 = 14,
+    VLDOF_3_300 = 15,
+
+    VLDOF_MAX
+
+}T_ZDrvZx234297_VldoF;
+
+/* BUCK3/4 EXTERNAL ADJUSTABLE	*/
+
+typedef enum _T_ZDrvZx234290_LDO_ENABLE
+{
+    LDO_ENABLE_OFF  = 0,   /* 00 */
+    LDO_ENABLE_ON   = 1,   /* 10 */
+
+    LDO_AVTICE_MAX
+}T_ZDrvZx234290_LDO_ENABLE;
+
+
+/*
+    ¹ØÓÚ BUCKSµÄģʽ£¬·ÖΪÕý³£Ä£Ê½Óë˯Ãßģʽ£¬ Õý³£Ä£Ê½Ö»¹Ø×¢PFM/PWM£¬²»¹Ø×¢¿ª¹Ø¡£
+    ˯Ãßģʽ¹Ø×¢PFM/PWM/ECO/OFF/NRM£¬Ó¦¸Ã½âÊÍΪ ˯ÃßģʽµÄ״̬²»½ö¹Ø×¢PWM/PFM£¬
+    ¶øÇÒ¹Ø×¢´ò¿ª¹Ø±Õ£¬³ýÁËOFF£¬ÆäËû¶¼ÊÇÔÚ¿ª×ŵÄÇé¿öϵÄģʽ£»¶øÄ¬ÈÏ¿ªµÄÇé¿öÔòÊÇ
+    NRMMODE£¬µçѹÓÃ˯Ãßµçѹ£»
+    ¶øLDOSµÄ˯Ãßģʽ£¬Ò»ÑùÓëÕý³£Ä£Ê½²»Ïà¸É¡£ÆäÒ²ÓÐNRM/ECO/OFFÕ⼸ÖÖ״̬
+*/
+
+/* BUCK1/2/3/4 NORMAL MODE */
+typedef enum _T_ZDrvZx234290_BUCK_NRMMODE
+{
+    BUCK_NRM_AUTO_WITH_ECO   	= 0,	/* 00/01 AUTO PWM/PSM ECO */
+    BUCK_NRM_FORCE_PWM 	= 2,	/* 10 FORCE PWM	*/
+    BUCK_NRM_AUTO_WITHOUT_ECO   = 3,  /* 00/01 AUTO PWM/PSM ECO */
+    BUCK_NRMMODE_MAX
+}T_ZDrvZx234290_BUCK_NRMMODE;
+
+/* BUCK1 SLPMODE */
+typedef enum _T_ZDrvZx234290_BUCK1_SLPMODE
+{
+    BUCK1_SLP_AUTO_WITHOUT_ECO   			= 0,	/* 00/11 AUTO PWM/PFM */
+    BUCK1_SLP_AUTO_ECO    = 1,	/*BUCK1_SLP_AUTO_ECO_VOLT output voltage configred by FBDC1[5:0]*/
+    BUCK1_SLP_AUTO_ECO_SLP    = 2,  /* output voltage configred by FBDC1_SLP[5:0]*/
+    BUCK1_SLP_SHUTDOWN				= 3,	/* 11 OFF */
+    BUCK1_SLPMODE_MAX
+}T_ZDrvZx234290_BUCK1_SLPMODE;
+
+/* BUCK2/3/4 SLPMODE */
+typedef enum _T_ZDrvZx234290_BUCK234_SLPMODE
+{
+    BUCK234_SLP_AUTO_WITHOUT_ECO   			= 0,	/* 00 AUTO PWM/PFM without eco*/
+    BUCK234_SLP_ECO_WITH_ECO    			= 1,	/* 01Óë10¾ùÊÇ ECO */
+    BUCK234_SLP_SHUTDOWN				= 3,	/* 11 OFF */
+
+    BUCK234_SLPMODE_MAX
+}T_ZDrvZx234290_BUCK234_SLPMODE;
+
+/* LDO1/2/3/7/8/9/10 SLPMODE */
+typedef enum _T_ZDrvZx234290_LDOA_SLPMODE
+{
+    LDOA_SLP_NRM_MODE   		= 0,	/* VOLDOx[3:0]  */
+    LDOA_SLP_ECO_VOLT    		= 1,	/* VOLDOx[3:0]	*/
+    LDOA_SLP_ECO_VOLT_SLP 		= 2,	/* VOLDOx_SLP[3:0]	*/
+    LDOA_SLP_SHUTDOWN			= 3,	/* 11 OFF */
+    LDOA_SLPMODE_MAX
+}T_ZDrvZx234290_LDOA_SLPMODE;
+
+/* LDO4/5/6/ SLPMODE	*/
+typedef enum _T_ZDrvZx234290_LDOB_SLPMODE
+{
+    LDOB_SLP_NRM_MODE   			= 0,	/* VOLDOx[3:0]  */
+    LDOB_SLP_ECO_VOLT    			= 1,	/* VOLDOx[3:0] 	*/
+    LDOB_SLP_NRM_MODE_VOLT			= 2,	/* VOLDOx[3:0]	*/
+    LDOB_SLP_SHUTDOWN				= 3,	/* 11 OFF */
+    LDOB_SLPMODE_MAX
+}T_ZDrvZx234290_LDOB_SLPMODE;
+
+typedef enum _T_ZDrvZx234290_LdoDischarger
+{
+    DISCHARGER_LDO_9  = 0,
+    DISCHARGER_LDO_10,
+    DISCHARGER_LDO_X,   /*not support*/
+    DISCHARGER_BUCK_4,
+    DISCHARGER_BUCK_3,
+    DISCHARGER_BUCK_2,
+    DISCHARGER_BUCK_1,
+    DISCHARGER_BUCK_X,  /*not support*/
+
+    DISCHARGER_LDO_1,
+    DISCHARGER_LDO_2,
+    DISCHARGER_LDO_3,
+    DISCHARGER_LDO_4,
+    DISCHARGER_LDO_5,
+    DISCHARGER_LDO_6,
+    DISCHARGER_LDO_7,
+    DISCHARGER_LDO_8,
+
+    DISCHARGER_MAX
+}T_ZDrvZx234290_LdoDischarger;
+
+typedef enum _T_ZDrvZx234290_DISCHARGER_ENABLE
+{
+    DISCHARGER_DISBALE  = 0,   /* 00 */
+    DISCHARGER_ENABLE    = 1,   /* 10 */
+
+    DISCHARGER_ENABLE_MAX
+}T_ZDrvZx234290_DISCHARGER_ENABLE;
+
+typedef enum _T_ZDrvZx234290_LdoList
+{
+    LDOLIST_BUCK_1  = 0,
+    LDOLIST_BUCK_2,
+    LDOLIST_BUCK_3,
+    LDOLIST_BUCK_4,
+    LDOLIST_LDO_1,
+    LDOLIST_LDO_2,
+    LDOLIST_LDO_3,
+
+    LDOLIST_LDO_4,
+    LDOLIST_LDO_5,
+    LDOLIST_LDO_6,//default off
+    LDOLIST_LDO_7,
+    LDOLIST_LDO_8,
+    LDOLIST_LDO_9,//default off
+    LDOLIST_LDO_10,
+    LDOLIST_LDO_RTC,
+
+    LDOLIST_MAX
+}T_ZDrvZx234290_LdoList;
+
+typedef enum _T_ZDrvZx234297_SINK
+{
+    ZX234297_SINK1 = 0,   /* 00 */
+    ZX234297_SINK2 = 1,   /* 10 */
+
+    ZX234297_SINK_MAX
+}T_ZDrvZx234297_SINK;
+
+typedef enum _T_ZDrvZx234297_SINK_SLPMODE
+{
+    SLPMODE_NORMAL = 0,   /* 00 */
+    SLPMODE_SHUTDOWN = 1,   /* 10 */
+
+    SLPMODE_MAX
+}T_ZDrvZx234297_SINK_SLPMODE;
+
+typedef enum _T_ZDrvZx234297_SINK_CURRENT
+{
+	SINK_CURRENT_5MA,
+	SINK_CURRENT_10MA,
+	SINK_CURRENT_15MA,
+	SINK_CURRENT_20MA,
+	SINK_CURRENT_30MA,
+	SINK_CURRENT_40MA,
+	SINK_CURRENT_50MA,
+	SINK_CURRENT_60MA,
+	SINK_CURRENT_70MA,
+	SINK_CURRENT_80MA,
+	SINK_CURRENT_90MA,
+	SINK_CURRENT_100MA,
+	SINK_CURRENT_110MA,
+	SINK_CURRENT_120MA,
+
+    SINK_CURRENT_MAX
+}T_ZDrvZx234297_SINK_CURRENT;
+
+typedef enum _T_ZDrvZx234290_ResetType
+{
+#if 0
+	ZX234290_USER_RST_UNDEFINE	= 0,
+	ZX234290_USER_RST_TO_NORMAL = 1,
+	ZX234290_USER_RST_TO_CHARGER = 2,
+	ZX234290_USER_RST_TO_ALARM = 3,
+#else
+	ZX234290_USER_RST_UNDEFINE	= 3,
+	ZX234290_USER_RST_TO_NORMAL = 0,
+	ZX234290_USER_RST_TO_CHARGER = 1,
+	ZX234290_USER_RST_TO_ALARM = 2,
+#endif
+	ZX234290_USER_RST_TO_EXCEPT = 4,
+
+	ZX234290_USER_RST_MAX
+}T_ZDrvZx234290_ResetType;
+
+
+int zx234290_get_chip_version(void);
+int zx234290_irq_init(struct zx234290 *zx234290);
+
+int zx234290_set_buck1_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_buck1_onoff(void);
+int zx234290_set_buck1_active_mode(T_ZDrvZx234290_BUCK_NRMMODE status);
+T_ZDrvZx234290_BUCK_NRMMODE zx234290_get_buck1_active_mode(void);
+int zx234290_set_buck1_voltage(T_ZDrvZx234290_VbuckA vol);
+T_ZDrvZx234290_VbuckA zx234290_get_buck1_voltage(void);
+int zx234290_set_buck1_sleep_mode(T_ZDrvZx234290_BUCK1_SLPMODE status);
+T_ZDrvZx234290_BUCK1_SLPMODE zx234290_get_buck1_sleep_mode(void);
+int zx234290_set_buck1_sleep_voltage(T_ZDrvZx234290_VbuckA vol);
+
+int zx234290_set_buck2_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+int zx234290_set_buck2_active_mode(T_ZDrvZx234290_BUCK_NRMMODE status);
+int zx234290_set_buck2_sleep_mode(T_ZDrvZx234290_BUCK234_SLPMODE status);
+
+int zx234290_set_buck3_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+int zx234290_set_buck3_active_mode(T_ZDrvZx234290_BUCK_NRMMODE status);
+int zx234290_set_buck3_sleep_mode(T_ZDrvZx234290_BUCK234_SLPMODE status);
+
+
+int zx234290_set_buck4_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+int zx234290_set_buck4_active_mode(T_ZDrvZx234290_BUCK_NRMMODE status);
+int zx234290_set_buck4_sleep_mode(T_ZDrvZx234290_BUCK234_SLPMODE status);
+
+
+
+int zx234290_set_ldo1_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+int zx234290_set_ldo1_onoff_PSM(T_ZDrvZx234290_LDO_ENABLE status);
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo1_onoff(void);
+int zx234290_set_ldo1_voltage(T_ZDrvZx234290_VldoA vol);
+T_ZDrvZx234290_VldoA zx234290_get_ldo1_voltage(void);
+int zx234290_set_ldo1_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status);
+T_ZDrvZx234290_LDOA_SLPMODE zx234290_get_ldo1_sleep_mode(void);
+
+
+int zx234290_set_ldo2_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo2_onoff(void);
+int zx234290_set_ldo2_voltage(T_ZDrvZx234290_VldoC vol);
+T_ZDrvZx234290_VldoC zx234290_get_ldo2_voltage(void);
+int zx234290_set_ldo2_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status);
+T_ZDrvZx234290_LDOA_SLPMODE zx234290_get_ldo2_sleep_mode(void);
+
+int zx234290_set_ldo3_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+int zx234290_set_ldo3_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status);
+
+int zx234290_set_ldo4_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+int zx234290_set_ldo4_sleep_mode(T_ZDrvZx234290_LDOB_SLPMODE status);
+
+
+int zx234290_set_ldo5_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+int zx234290_set_ldo5_onoff_PSM(T_ZDrvZx234290_LDO_ENABLE status);
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo5_onoff(void);
+int zx234290_set_ldo5_voltage(T_ZDrvZx234290_VldoB vol);
+T_ZDrvZx234290_VldoB zx234290_get_ldo5_voltage(void);
+int zx234290_set_ldo5_sleep_mode(T_ZDrvZx234290_LDOB_SLPMODE status);
+T_ZDrvZx234290_LDOB_SLPMODE zx234290_get_ldo5_sleep_mode(void);
+
+int zx234290_set_ldo6_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo6_onoff(void);
+int zx234290_set_ldo6_voltage(T_ZDrvZx234290_VldoD vol);
+T_ZDrvZx234290_VldoD zx234290_get_ldo6_voltage(void);
+int zx234290_set_ldo6_sleep_mode(T_ZDrvZx234290_LDOB_SLPMODE status);
+T_ZDrvZx234290_LDOB_SLPMODE zx234290_get_ldo6_sleep_mode(void);
+
+int zx234290_set_ldo7_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+int zx234290_set_ldo7_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status);
+
+int zx234290_set_ldo8_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo8_onoff(void);
+int zx234290_set_ldo8_voltage(T_ZDrvZx234290_VldoD vol);
+T_ZDrvZx234290_VldoD zx234290_get_ldo8_voltage(void);
+int zx234290_set_ldo8_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status);
+T_ZDrvZx234290_LDOA_SLPMODE zx234290_get_ldo8_sleep_mode(void);
+int zx234290_set_ldo9_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+int zx234290_set_ldo9_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status);
+
+int zx234290_set_ldo10_onoff(T_ZDrvZx234290_LDO_ENABLE status);
+int zx234290_set_ldo10_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status);
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo10_onoff(void);
+T_ZDrvZx234297_VldoF zx234290_get_ldo10_voltageF(void);
+T_ZDrvZx234290_LDOA_SLPMODE zx234290_get_ldo10_sleep_mode(void);
+int zx234297_set_ldo10_voltageF(T_ZDrvZx234297_VldoF vol);
+
+int zDrvPmic_SetNormal_Onoff(T_ZDrvPmic_Regulator regulator, T_ZDrvPmic_Enable enable);
+int zDrvPmic_SetNormal_Onoff_PSM(T_ZDrvPmic_Regulator regulator, T_ZDrvPmic_Enable enable);
+int zDrvPmic_SetNormal_Voltage(T_ZDrvPmic_Regulator regulator, int voltage);
+int zDrvPmic_SetSleep_Voltage(T_ZDrvPmic_Regulator regulator, int voltage);
+int zDrvPmic_GetNormal_Onoff(T_ZDrvPmic_Regulator regulator, T_ZDrvPmic_Enable* enable);
+int zDrvPmic_GetNormal_Voltage(T_ZDrvPmic_Regulator regulator, int* voltage);
+
+
+/*adc fun*/
+uint get_battery_voltage(void);
+uint get_adc1_voltage(void);
+uint get_adc2_voltage(void);
+
+
+#endif /*  __LINUX_MFD_TPS65912_H */
diff --git a/upstream/linux-5.10/include/linux/mmc/mmc_func.h b/upstream/linux-5.10/include/linux/mmc/mmc_func.h
new file mode 100755
index 0000000..b2636ab
--- /dev/null
+++ b/upstream/linux-5.10/include/linux/mmc/mmc_func.h
@@ -0,0 +1,37 @@
+/*******************************************************************************
+* °æÈ¨ËùÓÐ (C)2014, ÉîÛÚÊÐÖÐÐËͨѶ΢µç×Ó
+*
+* ÎļþÃû³Æ£º emmc_ramdump.c
+* Îļþ±êʶ£º
+* ÄÚÈÝÕªÒª£º
+* ÆäËü˵Ã÷£º
+* µ±Ç°°æ±¾£º 1.0
+* ×÷¡¡¡¡Õߣº 
+* Íê³ÉÈÕÆÚ£º
+*******************************************************************************/
+
+
+#ifndef LINUX_MMC_MMC_FUNC_H
+#define LINUX_MMC_MMC_FUNC_H
+
+#include <linux/types.h>
+
+int mmc_ramdump_init(void);
+/*
+*  start_addr: the address is the emmc address you want to write,and it size is 
+*              an integer multiple of 512. defined by byte
+*  data_size: the size of data you want to write .defined by byte
+*  src_buf: data buffer where log or file stored; 
+*/
+int mmc_bwrite(u32 start_addr, u32 data_size, void *src_buf);
+
+/*
+*  start_addr: the address is the emmc address you want to write,and it size is 
+*              an integer multiple of 512. defined by byte
+*  data_size: the size of data you want to write .defined by byte
+*  src_buf: data buffer where log or file will store; 
+*/
+
+int mmc_bread(u32 start_addr, u32 data_size, void *dst);
+
+#endif /* LINUX_MMC_MMC_FUNC_H */
diff --git a/upstream/linux-5.10/kernel/ramdump/ramdump_client_cap.c b/upstream/linux-5.10/kernel/ramdump/ramdump_client_cap.c
new file mode 100755
index 0000000..bcb6a53
--- /dev/null
+++ b/upstream/linux-5.10/kernel/ramdump/ramdump_client_cap.c
@@ -0,0 +1,457 @@
+/*******************************************************************************
+* °æÈ¨ËùÓÐ (C)2016, ÖÐÐËͨѶ¹É·ÝÓÐÏÞ¹«Ë¾¡£
+* 
+* ÎļþÃû³Æ:     ramdump_client_cap.c
+* Îļþ±êʶ:     ramdump_client_cap.c
+* ÄÚÈÝÕªÒª:     ramdump cap¿Í»§¶ËÒì³£ËÀ»úÏÖ³¡Êý¾Ýµ¼³öʵÏÖ
+* 
+* ÐÞ¸ÄÈÕÆÚ        °æ±¾ºÅ      Ð޸ıê¼Ç        ÐÞ¸ÄÈË          ÐÞ¸ÄÄÚÈÝ
+* ------------------------------------------------------------------------------
+* 2019/10/10      V1.0        Create          00130574         ´´½¨
+* 
+*******************************************************************************/
+
+/*******************************************************************************
+*                                   Í·Îļþ                                     *
+*******************************************************************************/
+#include "ramdump.h"
+#include "ramdump_arch.h"
+#include <linux/module.h>
+#include <linux/soc/zte/rpmsg.h>
+#include "ram_config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*******************************************************************************
+*                                  ³£Á¿¶¨Òå                                    *
+*******************************************************************************/
+
+/*******************************************************************************
+*                                   ºê¶¨Òå                                     *
+*******************************************************************************/
+
+/*******************************************************************************
+*                                Êý¾ÝÀàÐͶ¨Òå                                  *
+*******************************************************************************/
+
+/*******************************************************************************
+*                                º¯ÊýÉùÃ÷                                  *
+*******************************************************************************/
+extern void ramdump_register_callbacks(void);
+extern unsigned char *ramdump_phy_to_vir(unsigned long phy, unsigned long size);
+extern void ramdump_shared_mem_init(void);
+extern void ramdump_data_transfer_to_device(void);
+extern void ramdump_oss_data_trans_init(void);
+extern unsigned char *ramdump_export_flag_base;
+
+/*******************************************************************************
+*                              ¾Ö²¿¾²Ì¬±äÁ¿¶¨Òå                                *
+*******************************************************************************/
+#define RAMDUMP_ON_DEFAULT_VAL  (1)
+
+/*******************************************************************************
+*                                È«¾Ö±äÁ¿¶¨Òå                                  *
+*******************************************************************************/
+/*
+ * run time control dump or not, use ( echo "0" > ramdump_on ) to close ramdump
+ */
+int sysctl_ramdump_on_panic = RAMDUMP_ON_DEFAULT_VAL;
+int ramdump_cap_init_flag = -1;
+int ramdump_count = 0;
+int ramdump_server_exp_core = RAMDUMP_FALSE;
+#ifdef CONFIG_RAMDUMP_USER
+unsigned int sysctl_ramdump_on_user = 1;
+#endif
+unsigned int ramdump_export_mode = 0xFF;
+/* Cmm file content */
+unsigned char *ramdump_cap_cmm_buf = NULL;
+/* err log file */
+unsigned char *ramdump_cap_error_log  = NULL;
+unsigned int *cap_ddr_len_base        = NULL;
+unsigned int   sysctl_ramdump_emmc_size = 0x0;
+unsigned int   sysctl_ramdump_emmc_start_addr = 0xFFFF;
+
+static struct ctl_table cfg_ramdump_array[] = {
+#ifdef CONFIG_RAMDUMP_USER
+	{
+		.procname	= "sysctl_ramdump_on_user",
+		.data		= &sysctl_ramdump_on_user,
+		.maxlen		= sizeof(sysctl_ramdump_on_user),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= SYSCTL_ZERO,
+		.extra2		= SYSCTL_ONE,
+	},
+#endif
+	{
+		  .procname   = "ramdump_start_addr",
+		  .data 	  = &sysctl_ramdump_emmc_start_addr,
+		  .maxlen	  = sizeof(u64),
+		  .mode 	  = 0644,
+		  .proc_handler  = proc_dointvec_minmax,
+	  },
+	  {
+		  .procname   = "ramdump_emmc_size",
+		  .data 	  = &sysctl_ramdump_emmc_size,
+		  .maxlen	  = sizeof(u64),
+		  .mode 	  = 0644,
+		  .proc_handler  = proc_doulongvec_minmax,
+	  },
+
+    { }
+};
+
+static struct ctl_table sysctl_ramdump_table[] = {
+    {
+        .procname   = "ramdump_ap",
+        .mode       = 0555,
+        .child      = cfg_ramdump_array,
+    },
+    { }
+};
+
+/*******************************************************************************
+*                                ¾Ö²¿º¯ÊýʵÏÖ                                  *
+*******************************************************************************/
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     ramdump_cap_icp_handle
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  buf: icp msg addr
+*               len: icp msg len
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     void
+* ÆäËü˵Ã÷:     This function is used for ramdump client icp msg handle, common entry
+*******************************************************************************/
+static void ramdump_cap_icp_handle(void *buf, unsigned int len)
+{
+	ramdump_msg_t *icp_msg = (ramdump_msg_t *)buf;
+
+	ramdump_server_exp_core = RAMDUMP_SUCCESS;
+
+	switch(icp_msg->msg_id)
+	{
+		case RAMDUMP_MSG_EXCEPT:
+		{
+			ramdump_panic("trans server received forced dump request from Ap server!\n");
+			break;
+		}
+
+		default:
+		{
+			ramdump_panic("trans server received forced dump request from Ap server!\n");
+			break;
+		}
+	}
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:    ramdump_oss_icp_create_channel
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý) actorID: icp send core id
+               chID:    icp channel id
+               size:    icp channel size
+*   (´«³ö²ÎÊý) void 
+* ·µ »Ø Öµ:    int: if msg send success 
+* ÆäËü˵Ã÷:    
+*******************************************************************************/
+static int ramdump_cap_icp_create_channel(T_RpMsg_CoreID dstCoreID, T_RpMsg_ChID chID, unsigned int size)
+{
+	return rpmsgCreateChannel(dstCoreID, chID, size);
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:    ramdump_oss_icp_regcallback
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý) actorID: icp send core id
+               chID:    icp channel id
+               callback:icp callback fun
+*   (´«³ö²ÎÊý) void 
+* ·µ »Ø Öµ:    int: if msg send success 
+* ÆäËü˵Ã÷:    
+*******************************************************************************/
+static int ramdump_cap_icp_regcallback (T_RpMsg_CoreID coreID, unsigned int chID, T_RpMsg_Callback callback)
+{
+	return rpmsgRegCallBack(coreID, chID, callback);
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:    ramdump_init_sysctl_table
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý) void
+*   (´«³ö²ÎÊý) void 
+* ·µ »Ø Öµ:    void 
+* ÆäËü˵Ã÷:    ×¢²ásysctlÃüÁÓû§Ì¬Ê¹ÓÃsysctl¿ØÖÆramdump´æ´¢µØÖ·
+*******************************************************************************/
+void ramdump_init_sysctl_table(void)
+{
+	register_sysctl_table(sysctl_ramdump_table);
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     ramdump_cap_icp_init
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  void
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     void
+* ÆäËü˵Ã÷:     This function is used for ramdump client icp init
+*******************************************************************************/
+static int ramdump_cap_icp_init(void)
+{
+	int ret = 0;
+
+	ret = ramdump_cap_icp_create_channel(
+			RAMDUMP_SERVER_AP, 
+			RAMDUMP_CHANNEL, 
+			RAMDUMP_CHANNEL_SIZE);
+
+	if (ret != RAMDUMP_SUCCESS)
+	{
+		return ret;
+	}
+	ret = ramdump_cap_icp_regcallback(
+			RAMDUMP_SERVER_AP,
+			RAMDUMP_CHANNEL, 
+			ramdump_cap_icp_handle);
+
+	if (ret != RAMDUMP_SUCCESS)
+	{
+		return ret;
+	}
+	return RAMDUMP_SUCCESS;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     ramdump_notify_server_panic
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  void
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     void
+* ÆäËü˵Ã÷:     This function is used for cap notify ramdump server to panic
+*******************************************************************************/
+static int ramdump_notify_server_panic(void)
+{
+	int ret = 0;
+	T_RpMsg_Msg rpMsg = {0};
+	ramdump_msg_t ramdumpMsg = {0};
+	
+	ramdumpMsg.msg_id = RAMDUMP_MSG_EXCEPT;
+	ramdumpMsg.cpu_id = CORE_AP;
+
+	rpMsg.coreID = RAMDUMP_SERVER_AP;
+	rpMsg.chID = RAMDUMP_CHANNEL;
+	rpMsg.flag = RPMSG_WRITE_INT | RPMSG_WRITE_IRQLOCK;
+	rpMsg.len = sizeof(ramdump_msg_t);
+	rpMsg.buf = &ramdumpMsg;
+
+	ret = rpmsgWrite(&rpMsg);
+	return ret;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:    ramdump_cap_store_ram_conf
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý) mem: addr
+*   (´«³ö²ÎÊý) void 
+* ·µ »Ø Öµ:    unsigend char*: changed addr
+* ÆäËü˵Ã÷:    This function is used to store ram conf
+*******************************************************************************/
+static unsigned char *ramdump_cap_store_ram_conf(unsigned char *mem)
+{
+	mem += sprintf(
+			mem, 
+			"data.load.binary &ramdump_dir\\%s A:0x%x--A:0x%x /noclear\n",
+			"cap_ddr.bin",
+			(unsigned int)DDR_BASE_CAP_ADDR_PA, 
+			(unsigned int)(DDR_BASE_CAP_ADDR_PA + *cap_ddr_len_base  - 1));
+	mem += sprintf(
+			mem, 
+			"data.load.binary &ramdump_dir\\%s A:0x%x--A:0x%x /noclear\n",
+			"cap.cmm",
+			(unsigned int)RAMDUMP_CAP_CMM_BUF_ADDR, 
+			(unsigned int)(RAMDUMP_CAP_CMM_BUF_ADDR + RAMDUMP_CAP_CMM_BUF_LEN_REAL - 1));
+	mem += sprintf(
+			mem, 
+			"data.load.binary &ramdump_dir\\%s A:0x%x--A:0x%x /noclear\n",
+			"cap_err_log.txt",
+			(unsigned int)RAMDUMP_CAP_LOG_BUF_ADDR, 
+			(unsigned int)(RAMDUMP_CAP_LOG_BUF_ADDR + RAMDUMP_CAP_LOG_BUF_LEN - 1));
+	return mem;
+}
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:    ramdump_cap_cmm_create
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý) void
+*   (´«³ö²ÎÊý) void
+* ·µ »Ø Öµ:    void
+* ÆäËü˵Ã÷:    This function is used for server to generate cmm scripts
+*******************************************************************************/
+static void ramdump_cap_cmm_create(void)
+{
+	unsigned char *pcmm_buf = ramdump_cap_cmm_buf;
+
+	memset(ramdump_cap_cmm_buf, 0, RAMDUMP_CAP_CMM_BUF_LEN_REAL);
+
+	// store the cmm BEGIN
+	pcmm_buf += sprintf(pcmm_buf, "ENTRY &ramdump_dir\n");
+
+	// store procmodes regs
+	pcmm_buf = ramdump_arch_store_modes_regs(pcmm_buf);
+
+	// store ram config 
+	pcmm_buf = ramdump_cap_store_ram_conf(pcmm_buf);
+
+	// store memory map control regs
+	pcmm_buf = ramdump_arch_store_mm_regs(pcmm_buf);
+
+	// store end symbol
+	pcmm_buf += sprintf(pcmm_buf, "ENDDO\n");
+
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:    ramdump_trans_cap_error_log_create
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý) void
+*   (´«³ö²ÎÊý) void
+* ·µ »Ø Öµ:    void
+* ÆäËü˵Ã÷:    This function is used to create err log file
+*******************************************************************************/
+static void ramdump_cap_error_log_create(void)
+{
+	unsigned char *buf = ramdump_cap_error_log;
+
+	memset(ramdump_cap_error_log, 0, RAMDUMP_CAP_LOG_BUF_LEN);
+	buf +=	sprintf(buf, "dump at core%d,", smp_processor_id());
+	if (current->mm != NULL)
+		buf += sprintf(buf, "in user,task is: %s\n", current->comm);
+	else
+		buf += sprintf(buf, "in kernel,task is: %s\n", current->comm);
+
+	if (ramdump_server_exp_core)
+		buf += sprintf(buf, "recv dumpinfo from ap\n");
+}
+
+/*******************************************************************************
+*                                È«¾Öº¯ÊýʵÏÖ                                  *
+*******************************************************************************/
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     ramdump_ram_conf_table_add
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  ram_name:    dump ram name
+                ram_start:   dump ram start(virtual addr)
+                ram_size:    dump ram size
+                ram_virt:    dump ram virt addr
+                ram_flag:    dump ram flag(copy/exter/callback)
+                ram_extra:   dump ram extra access addr
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     void
+* ÆäËü˵Ã÷:     This function is used to add dump ram conf into public table
+*******************************************************************************/
+void ramdump_ram_conf_table_add(
+		char *ram_name, 
+		unsigned long ram_phy, 
+		unsigned long ram_size, 
+		unsigned long ram_virt,
+		unsigned long ram_flag,
+		unsigned long ram_extra)
+{
+}
+void ramdump_init_cmm_buf(void)
+{
+	/* Cmm file content */
+	ramdump_cap_cmm_buf = ramdump_phy_to_vir((unsigned long)RAMDUMP_CAP_CMM_BUF_ADDR, RAMDUMP_CAP_CMM_BUF_LEN_REAL);
+	/* err log file */
+	ramdump_cap_error_log = ramdump_phy_to_vir((unsigned long)RAMDUMP_CAP_LOG_BUF_ADDR, RAMDUMP_CAP_LOG_BUF_LEN);
+	cap_ddr_len_base = (unsigned int *)ramdump_phy_to_vir((unsigned long)IRAM_BASE_ADDR_BOOT_DDR, sizeof(unsigned long));
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     ramdump_init
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  void
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     RAMDUMP_SUCCESS or RAMDUMP_FAILED
+* ÆäËü˵Ã÷:     This function is used for ramdump init
+*******************************************************************************/
+int __init ramdump_init(void)
+{
+	int ret = 0;
+	ramdump_printf("Ramdump cap init start!!!!!\n");
+
+	if (ramdump_cap_init_flag == RAMDUMP_TRUE)
+		return RAMDUMP_SUCCESS;
+	ramdump_printf("Ramdump cap init rpmsg start!!!!!\n");
+	ret = ramdump_cap_icp_init();
+	if (ret != RAMDUMP_ICP_SUCCESS) 
+		return ret;
+
+	ramdump_register_callbacks();
+
+	ramdump_init_cmm_buf();
+
+	ramdump_init_sysctl_table();
+
+	ramdump_shared_mem_init();
+	ramdump_oss_data_trans_init();
+
+	ramdump_printf("Ramdump cap init success!\n");
+	ramdump_cap_init_flag = RAMDUMP_TRUE;
+
+	return RAMDUMP_SUCCESS;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     ramdump_entry
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  void
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     void
+* ÆäËü˵Ã÷:     This function is used for ramdump entry
+*******************************************************************************/
+void ramdump_entry (void)
+{
+	unsigned long flags;
+	if (sysctl_ramdump_on_panic == false)
+		return;
+
+	/*
+	* we need lock the irq, this can`t be interrupt.
+	*/
+	ramdump_irq_lock(flags);
+
+	if (!ramdump_cap_init_flag)
+		while(true); /* endless circle */
+
+	if (++ramdump_count > 1)
+		while(true); /* endless circle */
+
+	/*
+	* save all regs first.
+	*/
+	ramdump_arch_save_all_regs();
+	// generate error log 
+	ramdump_cap_error_log_create();
+
+	//Éú³Écmm½Å±¾µÄµ¼³öÅäÖÃ
+	ramdump_cap_cmm_create();
+
+	/* notify client ramdump */
+	ramdump_notify_server_panic();
+
+	ramdump_arch_clean_caches();
+	ramdump_export_mode  = *(unsigned int *)ramdump_export_flag_base;
+
+	if((ramdump_export_mode == RAMDUMP_MODE_EMMC)
+		|| (ramdump_export_mode == RAMDUMP_MODE_SPINAND))
+		ramdump_data_transfer_to_device();
+
+	while(true)
+		;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/upstream/linux-5.10/kernel/ramdump/ramdump_emmc.c b/upstream/linux-5.10/kernel/ramdump/ramdump_emmc.c
new file mode 100755
index 0000000..0c28f27
--- /dev/null
+++ b/upstream/linux-5.10/kernel/ramdump/ramdump_emmc.c
@@ -0,0 +1,170 @@
+/**
+ * @file oss_ramdump_osa.c
+ * @brief Implementation of Ramdump os adapt
+ *
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * @author Qing Wang <wang.qing@sanechips.com.cn>
+ * @ingroup si_ap_oss_ramdump_id
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*******************************************************************************
+ *                           Include header files                              *
+ ******************************************************************************/
+#include "ramdump.h"
+#include "ramdump_emmc.h"
+#include "ram_config.h"
+#include "ramdump_compress.h"
+#include <linux/lzo.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*******************************************************************************
+*                          Extern function declarations                        *
+*******************************************************************************/
+
+/*******************************************************************************
+*                          Extern variable declarations                        *
+*******************************************************************************/
+extern unsigned char *ramdump_shared_mem_base;
+extern unsigned char *ramdump_emmc_flag_base;
+extern unsigned int ramdump_compress_flag;
+
+/*******************************************************************************
+ *                             Macro definitions                               *
+ ******************************************************************************/
+#define RAMDUMP_DELAY_MS_COUNT (2500)
+
+/*******************************************************************************
+ *                             Type definitions                                *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *                        Local function declarations                          *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *                         Local variable definitions                          *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *                        Global variable definitions                          *
+ ******************************************************************************/
+unsigned int ramdump_emmc_size = 0;
+volatile unsigned int ramdump_emmc_offset = 0;
+extern unsigned int ramdump_device_file_cnt;
+
+/*******************************************************************************
+ *                      Inline function implementations                        *
+ ******************************************************************************/
+static inline void ramdump_wait_delay( unsigned long ms)
+{
+	volatile int j = 0;
+	for (j = 0; j < 10000; j++);
+}
+
+/*******************************************************************************
+ *                      Local function implementations                         *
+ ******************************************************************************/
+int ramdump_emmc_init(ramdump_file_t *fp)
+{
+	fp->magic = 0x2A2A2A2A;
+	ramdump_emmc_offset = roundup(sizeof(ramdump_file_t), RAMDUMP_EMMC_ALIGN_SIZE);
+	
+	if(RAMDUMP_TRANS_EMMC_LEN > ramdump_emmc_offset)
+	{
+		ramdump_emmc_size = RAMDUMP_TRANS_EMMC_LEN - ramdump_emmc_offset;
+	}
+	else
+	{
+		printk("[ramdump] emmc start addr is %ld, emmc size= %ld, error: size smaller than ramdump file header, return!\n", sysctl_ramdump_emmc_start_addr, sysctl_ramdump_emmc_size);
+		return -1;
+	}
+
+	if(mmc_ramdump_init()){
+		ramdump_printf("EMMC init failed! No ramdump data trans to emmc!\n");
+		return -1;
+	}
+	return 0;
+}
+
+int ramdump_emmc_write_file(char *file_name, unsigned int file_size, ramdump_file_t *fp)
+{
+	if (ramdump_device_file_cnt >= RAMDUMP_FILE_NUM_MAX)
+		return -1;
+	if (ramdump_emmc_offset >= RAMDUMP_TRANS_EMMC_LEN)
+		return -1;
+
+	fp->file_fp[ramdump_device_file_cnt].magic = 0x3A3A3A3A;
+	strncpy(fp->file_fp[ramdump_device_file_cnt].file_name, file_name, RAMDUMP_RAMCONF_FILENAME_MAXLEN - 1);
+	fp->file_fp[ramdump_device_file_cnt].offset = ramdump_emmc_offset;
+	fp->file_fp[ramdump_device_file_cnt].size = file_size;
+	return 0;
+}
+
+int ramdump_emmc_write_file_head(ramdump_file_t *fp)
+{
+	int ret = -1;
+	mmc_bwrite(RAMDUMP_EMMC_ADDR, roundup(sizeof(ramdump_file_t), RAMDUMP_EMMC_ALIGN_SIZE), fp);
+	return ret;
+}
+
+int ramdump_emmc_write_data(ramdump_shmem_t *msg, ramdump_file_t *fp, unsigned int size)
+{
+	int ret = 0;
+	unsigned int buffer = RAMDUMP_EMMC_ADDR + ramdump_emmc_offset;
+
+	if (ramdump_device_file_cnt >= RAMDUMP_FILE_NUM_MAX)
+		return -1;
+
+	while(1){
+		if ((msg->core_flag == 1) && (msg->rw_flag == 2)){
+			if(msg->size >= (ramdump_emmc_size - fp->file_fp[ramdump_device_file_cnt].offset))
+				return -1;
+			ret = mmc_bwrite(buffer, msg->size, msg->buf);
+			ramdump_emmc_offset = ramdump_emmc_offset + roundup(msg->size, RAMDUMP_EMMC_ALIGN_SIZE);
+			msg->core_flag = 1;
+			msg->rw_flag = 1;
+			ret = msg->size;
+			break;
+		}
+		else
+			ramdump_wait_delay(0);
+	}
+	return ret;
+}
+
+int ramdump_emmc_read(char *buffer, ramdump_shmem_t *msg, unsigned int size)
+{
+	int ret = 0;
+
+	return ret;
+}
+
+void ramdump_emmc_close(ramdump_file_t *fp)
+{
+	fp->file_size = ramdump_emmc_offset;
+	ramdump_emmc_write_file_head(fp);
+	ramdump_printf("ramdump trans emmc finished!\n");
+}
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/upstream/linux-5.10/kernel/ramdump/ramdump_emmc.h b/upstream/linux-5.10/kernel/ramdump/ramdump_emmc.h
new file mode 100755
index 0000000..1028ab2
--- /dev/null
+++ b/upstream/linux-5.10/kernel/ramdump/ramdump_emmc.h
@@ -0,0 +1,71 @@
+/*******************************************************************************
+* °æÈ¨ËùÓÐ (C)2016, ÖÐÐËͨѶ¹É·ÝÓÐÏÞ¹«Ë¾¡£
+* 
+* ÎļþÃû³Æ: ramdump_emmc.h
+* Îļþ±êʶ: ramdump_emmc.h
+* ÄÚÈÝÕªÒª: ramdump emmcÍ·Îļþ
+* ʹÓ÷½·¨: #include "ramdump_emmc.h"
+* 
+* ÐÞ¸ÄÈÕÆÚ        °æ±¾ºÅ      Ð޸ıê¼Ç        ÐÞ¸ÄÈË          ÐÞ¸ÄÄÚÈÝ
+* ------------------------------------------------------------------------------
+* 2016/3/10      V1.0        Create           ÕÔ¾ü¿ü          ´´½¨
+* 
+*******************************************************************************/
+
+#ifndef _RAMDUMP_EMMC_H
+#define _RAMDUMP_EMMC_H
+
+/*******************************************************************************
+*                                   Í·Îļþ                                     *
+*******************************************************************************/
+#include "ramdump.h"
+#include <linux/mmc/mmc_func.h>
+
+/*******************************************************************************
+*                                Íⲿ±äÁ¿ÉùÃ÷                                  *
+*******************************************************************************/
+extern unsigned int sysctl_ramdump_emmc_start_addr;
+extern unsigned int sysctl_ramdump_emmc_size;
+extern volatile unsigned int ramdump_emmc_offset;
+
+/*******************************************************************************
+*                                   ºê¶¨Òå                                     *
+*******************************************************************************/
+#define RAMDUMP_EMMC_ADDR         (sysctl_ramdump_emmc_start_addr * 512) 
+#define RAMDUMP_TRANS_EMMC_LEN    (sysctl_ramdump_emmc_size * 512)
+
+/*******************************************************************************
+*                                Êý¾ÝÀàÐͶ¨Òå                                  *
+*******************************************************************************/
+
+/*******************************************************************************
+*                                È«¾Ö±äÁ¿ÉùÃ÷                                  *
+*******************************************************************************/
+
+/*******************************************************************************
+*                                È«¾Öº¯ÊýÉùÃ÷                                  *
+*******************************************************************************/
+/**
+ * @brief ramdump_emmc_init .
+ *
+ * @param void.
+ *
+ * @return int.
+ * @retval standard error
+ * @note   This function is used for ramdump init
+ */
+int ramdump_emmc_init(ramdump_file_t *fp);
+int ramdump_emmc_write_file(char *file_name, unsigned int file_size, ramdump_file_t *fp);
+int ramdump_emmc_write_file_head(ramdump_file_t *fp);
+int ramdump_emmc_modify_file_size(ramdump_file_t *fp, unsigned int file_size);
+int ramdump_emmc_write_data(ramdump_shmem_t *msg, ramdump_file_t *fp, unsigned int size);
+int ramdump_emmc_write_logbuf(ramdump_file_t *fp);
+void ramdump_emmc_close(ramdump_file_t *fp);
+int ramdump_emmc_write_log_txt(ramdump_file_t *fp);
+
+/*******************************************************************************
+*                                ÄÚÁªº¯ÊýʵÏÖ                                  *
+*******************************************************************************/
+
+#endif  //#ifndef _RAMDUMP_EMMC_H
+
diff --git a/upstream/linux-5.10/kernel/tracker.c b/upstream/linux-5.10/kernel/tracker.c
new file mode 100755
index 0000000..6f7e1ab
--- /dev/null
+++ b/upstream/linux-5.10/kernel/tracker.c
@@ -0,0 +1,459 @@
+/*
+ * tracker.c - System accounting over taskstats interface
+ *
+ * Copyright (C) Jay Lan,	<jlan@sgi.com>
+ *
+ *
+ * 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.
+ *
+ */
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/export.h>
+#include <linux/sched/clock.h>
+#include "ram_config.h"
+
+/*******************************************************************************
+*                                   ºê¶¨Òå                                     *
+*******************************************************************************/
+#define _OS_LINUX                    1
+
+#if defined(_OS_TOS)
+# define OS_STATISTIC_IRAM_BASE    (IRAM_BASE_ADDR_OS_STATISTIC_PSCPU)
+# define OS_STATISTIC_TIME         zDrvTimer_Stamp()
+#elif defined(_OS_LINUX)
+# define OS_STATISTIC_IRAM_BASE    g_zxic_trace_apcpu_addr //(IRAM_BASE_ADDR_OS_STATISTIC_APCPU)
+# define OS_STATISTIC_TIME         (cpu_clock(0)>>10)
+#else
+# error "unknown os"
+#endif
+
+
+
+#define OS_IRAM_STATISTIC_CNT         (5)
+#define OS_IRAM_STATISTIC_NAME_LEN    (16)
+#define OS_DDR_STATISTIC_CNT          (1000)
+
+#define OS_IRAM_THREAD_SWAPIN      (OS_STATISTIC_IRAM_BASE)
+#define OS_IRAM_IRQ_START          (OS_IRAM_THREAD_SWAPIN + sizeof(t_os_iram_thread_statistic))
+#define OS_IRAM_IRQ_END            (OS_IRAM_IRQ_START + sizeof(t_os_iram_statistic))
+
+#if defined(_OS_TOS)
+#define OS_IRAM_DSR_START          (OS_IRAM_IRQ_END + sizeof(t_os_iram_statistic))
+#define OS_IRAM_DSR_END            (OS_IRAM_DSR_START + sizeof(t_os_iram_statistic))
+#elif defined(_OS_LINUX)
+#define OS_IRAM_SOFTIRQ_START      (OS_IRAM_IRQ_END + sizeof(t_os_iram_statistic))
+#define OS_IRAM_SOFTIRQ_END        (OS_IRAM_SOFTIRQ_START + sizeof(t_os_iram_statistic))
+#define OS_IRAM_TIMER_START        (OS_IRAM_SOFTIRQ_END + sizeof(t_os_iram_statistic))
+#define OS_IRAM_TIMER_END          (OS_IRAM_TIMER_START + sizeof(t_os_iram_statistic))
+#endif
+
+#define os_statistic_check()       *((volatile unsigned long *)OS_STATISTIC_IRAM_BASE)
+#define os_statistic_enabled()     g_os_statistic_enable 
+
+/*******************************************************************************
+*                                   Êý¾Ý½á¹¹¶¨Òå                               *
+*******************************************************************************/
+typedef volatile struct {
+    unsigned int cnt;
+    unsigned int index;
+    struct {
+        unsigned char name[OS_IRAM_STATISTIC_NAME_LEN];
+        unsigned int data2;
+    } statistics[OS_IRAM_STATISTIC_CNT];
+}t_os_iram_thread_statistic;
+
+typedef volatile struct {
+    unsigned int cnt;
+    unsigned int index;
+    struct {
+        unsigned int data1;
+        unsigned int data2;
+    } statistics[OS_IRAM_STATISTIC_CNT];
+}t_os_iram_statistic;
+
+typedef struct {
+    unsigned int cnt;
+    unsigned int index;
+    struct {
+        unsigned int data1;
+        unsigned int data2;
+    } statistics[OS_DDR_STATISTIC_CNT];
+}t_os_ddr_statistic;
+
+/*******************************************************************************
+*                                   È«¾Ö±äÁ¿                                   *
+*******************************************************************************/
+#if defined(_OS_LINUX)
+volatile  static char *g_zxic_trace_apcpu_addr;
+#endif
+
+volatile  static int g_os_statistic_enable;
+volatile  static unsigned int g_os_statistic_cnt;
+
+volatile  static t_os_iram_thread_statistic *g_os_iram_swapin_statistic;
+volatile  static t_os_iram_statistic        *g_os_iram_irq_start_statistic;
+volatile  static t_os_iram_statistic        *g_os_iram_irq_end_statistic;
+
+#if defined(_OS_TOS)
+static t_os_iram_statistic *g_os_iram_dsr_start_statistic;
+static t_os_iram_statistic *g_os_iram_dsr_end_statistic;
+#elif defined(_OS_LINUX)
+volatile  static t_os_iram_statistic *g_os_iram_softirq_start_statistic;
+volatile  static t_os_iram_statistic *g_os_iram_softirq_end_statistic;
+volatile  static t_os_iram_statistic *g_os_iram_timer_start_statistic;
+volatile  static t_os_iram_statistic *g_os_iram_timer_end_statistic;
+#endif
+
+volatile  static t_os_ddr_statistic *g_os_ddr_swapin_statistic;
+volatile  static t_os_ddr_statistic *g_os_ddr_irq_start_statistic;
+volatile  static t_os_ddr_statistic *g_os_ddr_irq_end_statistic;
+
+#if defined(_OS_TOS)
+static t_os_ddr_statistic *g_os_ddr_dsr_start_statistic;
+static t_os_ddr_statistic *g_os_ddr_dsr_end_statistic;
+#elif defined(_OS_LINUX)
+volatile  static t_os_ddr_statistic *g_os_ddr_softirq_start_statistic;
+volatile  static t_os_ddr_statistic *g_os_ddr_softirq_end_statistic;
+volatile  static t_os_ddr_statistic *g_os_ddr_timer_start_statistic;
+volatile  static t_os_ddr_statistic *g_os_ddr_timer_end_statistic;
+#endif
+
+/*******************************************************************************
+*                                   È«¾Öº¯ÊýÉùÃ÷                               *
+*******************************************************************************/
+void os_statistic_enable(void);
+/*******************************************************************************
+*                                   ¾Ö²¿º¯Êý                                   *
+*******************************************************************************/
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     ¹ì¼£Í³¼Æµ½IRAM
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  iram_addr: µØÖ· 
+                data:      ʼþÏî
+                time:      ʱ¼ä
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     void
+* ÆäËü˵Ã÷:     ÎÞ
+*******************************************************************************/
+static inline void os_statistic_in_iram(volatile void *iram_addr, void *data, unsigned long time)
+{
+    unsigned long       index;
+    t_os_iram_statistic *iram;
+
+    iram = (t_os_iram_statistic *)iram_addr;
+    
+    index = iram->index;
+    if(index >= OS_IRAM_STATISTIC_CNT)
+    {
+        index = 0;
+    }
+
+    iram->statistics[index].data1 = (unsigned int)data;
+    iram->statistics[index].data2 = time;
+    index++;
+
+    iram->index = index;
+    iram->cnt = g_os_statistic_cnt;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     Ï̹߳켣ͳ¼Æµ½IRAM
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  iram_addr: µØÖ· 
+                data:      ʼþÏî
+                time:      ʱ¼ä
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     void
+* ÆäËü˵Ã÷:     ÎÞ
+*******************************************************************************/
+static inline void os_statistic_thread_in_iram(volatile void *iram_addr, void *data, unsigned long time)
+{
+    unsigned long              index;
+    t_os_iram_thread_statistic *iram;
+
+    iram = (t_os_iram_thread_statistic *)iram_addr;
+    
+    index = iram->index;
+    if(index >= OS_IRAM_STATISTIC_CNT)
+    {
+        index = 0;
+    }
+
+#if defined(_OS_TOS)
+    strncpy((char *)(iram->statistics[index].name), cyg_thread_get_name((cyg_handle_t)data), OS_IRAM_STATISTIC_NAME_LEN - 1);
+#elif defined(_OS_LINUX)
+    strncpy((char *)(iram->statistics[index].name), ((struct task_struct *)data)->comm, OS_IRAM_STATISTIC_NAME_LEN - 1);
+#else
+# error "unkown os"
+#endif
+    iram->statistics[index].name[OS_IRAM_STATISTIC_NAME_LEN - 1] = 0;
+    iram->statistics[index].data2 = time;
+    index++;
+
+    iram->index = index;
+    iram->cnt = g_os_statistic_cnt;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     ¹ì¼£Í³¼Æµ½DDR
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  iram_addr: µØÖ· 
+                data:      ʼþÏî
+                time:      ʱ¼ä
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     void
+* ÆäËü˵Ã÷:     ÎÞ
+*******************************************************************************/
+static inline void os_statistic_in_ddr(void *ddr_addr, void *data, unsigned long time)
+{
+    unsigned long              index;
+    t_os_ddr_statistic         *ddr;
+
+    ddr  = (t_os_ddr_statistic *)ddr_addr;
+    
+    index = ddr->index;
+    if (index >= OS_DDR_STATISTIC_CNT)
+    {
+        index = 0;
+    }
+    ddr->statistics[index].data1 = (unsigned int)data;
+    ddr->statistics[index].data2 = time;
+    index++;
+
+    ddr->index = index;
+    ddr->cnt   = g_os_statistic_cnt;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     ¹ì¼£Í³¼Æµ½DDR
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  iram_addr: µØÖ· 
+                data:      ʼþÏî
+                time:      ʱ¼ä
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     void
+* ÆäËü˵Ã÷:     ÎÞ
+*******************************************************************************/
+static inline void os_statistic_info_update(void)
+{
+    g_os_statistic_cnt++;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     ¶¨Ê±Æ÷»Øµ÷¹³×Ó
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     void
+* ÆäËü˵Ã÷:     ÎÞ
+*******************************************************************************/
+static int os_statistic_delayed_work_timer_fn(unsigned long data)
+{
+    int sec = 0;
+    msleep(20000);
+    while(!os_statistic_check())
+    {
+        //³¬¹ý40s£¬Ö±½ÓÍ˳ö
+        if(sec >= 4)
+            return 0;
+        msleep(10000);
+        sec++;
+    }
+    os_statistic_enable();
+    return 0;
+}
+
+/*******************************************************************************
+*                                 È«¾Öº¯ÊýʵÏÖ                                 *
+*******************************************************************************/
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     ʹÄܹ켣ͳ¼Æ¹¦ÄÜ
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  address: ¼Ç¼µ½IRAMÖеĵØÖ· 
+                size:    IRAM¿Õ¼ä´óС
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     void
+* ÆäËü˵Ã÷:     ÎÞ
+*******************************************************************************/
+void os_statistic_enable(void)
+{
+#if defined(_OS_TOS)
+    g_os_iram_swapin_statistic        = (t_os_iram_thread_statistic *)OS_IRAM_THREAD_SWAPIN;
+    g_os_iram_irq_start_statistic     = (t_os_iram_statistic *)OS_IRAM_IRQ_START;
+    g_os_iram_irq_end_statistic       = (t_os_iram_statistic *)OS_IRAM_IRQ_END;
+    g_os_iram_dsr_start_statistic     = (t_os_iram_statistic *)OS_IRAM_DSR_START;
+    g_os_iram_dsr_end_statistic       = (t_os_iram_statistic *)OS_IRAM_DSR_END;
+
+    g_os_ddr_swapin_statistic         = (t_os_ddr_statistic *)zOss_Malloc(sizeof(t_os_ddr_statistic));
+    g_os_ddr_irq_start_statistic      = (t_os_ddr_statistic *)zOss_Malloc(sizeof(t_os_ddr_statistic));
+    g_os_ddr_irq_end_statistic        = (t_os_ddr_statistic *)zOss_Malloc(sizeof(t_os_ddr_statistic));
+    g_os_ddr_dsr_start_statistic      = (t_os_ddr_statistic *)zOss_Malloc(sizeof(t_os_ddr_statistic));
+    g_os_ddr_dsr_end_statistic        = (t_os_ddr_statistic *)zOss_Malloc(sizeof(t_os_ddr_statistic));
+#elif defined(_OS_LINUX)
+    g_os_iram_swapin_statistic        = (t_os_iram_thread_statistic *)OS_IRAM_THREAD_SWAPIN;    
+    g_os_iram_irq_start_statistic     = (t_os_iram_statistic *)OS_IRAM_IRQ_START;    
+    g_os_iram_irq_end_statistic       = (t_os_iram_statistic *)OS_IRAM_IRQ_END;    
+    g_os_iram_softirq_start_statistic = (t_os_iram_statistic *)OS_IRAM_SOFTIRQ_START;    
+    g_os_iram_softirq_end_statistic   = (t_os_iram_statistic *)OS_IRAM_SOFTIRQ_END;    
+    g_os_iram_timer_start_statistic   = (t_os_iram_statistic *)OS_IRAM_TIMER_START;    
+    g_os_iram_timer_end_statistic     = (t_os_iram_statistic *)OS_IRAM_TIMER_END;
+
+    g_os_ddr_swapin_statistic         = (t_os_ddr_statistic *)kmalloc(sizeof(t_os_ddr_statistic), GFP_KERNEL);
+    g_os_ddr_irq_start_statistic      = (t_os_ddr_statistic *)kmalloc(sizeof(t_os_ddr_statistic), GFP_KERNEL);
+    g_os_ddr_irq_end_statistic        = (t_os_ddr_statistic *)kmalloc(sizeof(t_os_ddr_statistic), GFP_KERNEL);
+    g_os_ddr_softirq_start_statistic  = (t_os_ddr_statistic *)kmalloc(sizeof(t_os_ddr_statistic), GFP_KERNEL);
+    g_os_ddr_softirq_end_statistic    = (t_os_ddr_statistic *)kmalloc(sizeof(t_os_ddr_statistic), GFP_KERNEL);
+    g_os_ddr_timer_start_statistic    = (t_os_ddr_statistic *)kmalloc(sizeof(t_os_ddr_statistic), GFP_KERNEL);
+    g_os_ddr_timer_end_statistic      = (t_os_ddr_statistic *)kmalloc(sizeof(t_os_ddr_statistic), GFP_KERNEL);
+
+#else
+# error "unkown os"
+#endif
+    if ((unsigned int )g_os_iram_timer_end_statistic - (unsigned int )g_os_iram_swapin_statistic > (unsigned int )IRAM_BASE_LEN_OS_STATISTIC_PSCPU )
+    {
+        BUG();
+    }
+    g_os_statistic_enable = 1;
+}
+EXPORT_SYMBOL(os_statistic_enable);
+
+void zxic_trace_task_switch(struct task_struct *next)
+{
+	unsigned long time;
+    if (!g_os_statistic_enable)
+        return ;
+
+    time = OS_STATISTIC_TIME;
+    os_statistic_thread_in_iram(g_os_iram_swapin_statistic, next, time);
+    os_statistic_in_ddr(g_os_ddr_swapin_statistic, next, time);
+    os_statistic_info_update();
+}
+
+void zxic_trace_irq_enter(u32 irq)
+{
+	unsigned long time;
+    if (!g_os_statistic_enable)
+        return ;
+
+    time = OS_STATISTIC_TIME;
+    os_statistic_in_iram(g_os_iram_irq_start_statistic, irq, time);
+    os_statistic_in_ddr(g_os_ddr_irq_start_statistic, irq, time);
+    os_statistic_info_update();
+}
+
+void zxic_trace_irq_exit(u32 irq)
+{
+	unsigned long time;
+    if (!g_os_statistic_enable)
+        return ;
+
+    time = OS_STATISTIC_TIME;
+    os_statistic_in_iram(g_os_iram_irq_end_statistic, irq, time);
+    os_statistic_in_ddr(g_os_ddr_irq_end_statistic, irq, time);
+    os_statistic_info_update();
+}
+
+void zxic_trace_softirq_enter(u32 vec_nr)
+{
+	unsigned long time;
+    if (!g_os_statistic_enable)
+        return ;
+
+    time = OS_STATISTIC_TIME;
+    os_statistic_in_iram(g_os_iram_softirq_start_statistic, vec_nr, time);
+    os_statistic_in_ddr(g_os_ddr_softirq_start_statistic, vec_nr, time);
+    os_statistic_info_update();
+}
+
+void zxic_trace_softirq_exit(u32 vec_nr)
+{
+	unsigned long time;
+    if (!g_os_statistic_enable)
+        return ;
+
+    time = OS_STATISTIC_TIME;
+    os_statistic_in_iram(g_os_iram_softirq_end_statistic, vec_nr, time);
+    os_statistic_in_ddr(g_os_ddr_softirq_end_statistic, vec_nr, time);
+    os_statistic_info_update();
+}
+
+void zxic_trace_timer_enter(void *func)
+{
+	unsigned long time;
+    if (!g_os_statistic_enable)
+        return ;
+
+    time = OS_STATISTIC_TIME;
+    os_statistic_in_iram(g_os_iram_timer_start_statistic, func, time);
+    os_statistic_in_ddr(g_os_ddr_timer_start_statistic, func, time);
+    os_statistic_info_update();
+}
+
+void zxic_trace_timer_exit(void *func)
+{
+	unsigned long time;
+    if (!g_os_statistic_enable)
+        return ;
+
+    time = OS_STATISTIC_TIME;
+    os_statistic_in_iram(g_os_iram_timer_end_statistic, func, time);
+    os_statistic_in_ddr(g_os_ddr_timer_end_statistic, func, time);
+    os_statistic_info_update();
+}
+
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö:     ¹ì¼£Í³¼Æµ½DDR
+* ²ÎÊý˵Ã÷:     
+*   (´«Èë²ÎÊý)  iram_addr: µØÖ· 
+                data:      ʼþÏî
+                time:      ʱ¼ä
+*   (´«³ö²ÎÊý)  void
+* ·µ »Ø Öµ:     void
+* ÆäËü˵Ã÷:     ÎÞ
+*******************************************************************************/
+int __init zxic_enable_tracer(void)
+{
+    struct timer_list timer;
+    struct task_struct *task;
+
+#ifdef IRAM_BASE_ADDR_VA
+    g_zxic_trace_apcpu_addr = IRAM_BASE_ADDR_OS_STATISTIC_PSCPU;
+#else
+    g_zxic_trace_apcpu_addr = ioremap(IRAM_BASE_ADDR_OS_STATISTIC_PSCPU, IRAM_BASE_LEN_OS_STATISTIC_PSCPU);
+#endif
+
+    /*
+    init_timer(&timer);
+    timer.expires = jiffies + 40*HZ;//msecs_to_jiffies(40*1000);//ÑÓ³Ù40Ãë
+    timer.data = 0;
+    timer.function = os_statistic_delayed_work_timer_fn;
+    setup_timer(&timer, os_statistic_delayed_work_timer_fn, 0);
+    add_timer(&timer);
+    */
+    //task = kthread_create(os_statistic_delayed_work_timer_fn, 0, "g_zxic_trace_sync_thread", 0);
+    //wake_up_process(task);
+    os_statistic_enable();
+    return 0x0;
+}
+module_init(zxic_enable_tracer);
+
+
diff --git a/upstream/linux-5.10/sound/soc/sanechips/zx29_ak4940.c b/upstream/linux-5.10/sound/soc/sanechips/zx29_ak4940.c
new file mode 100755
index 0000000..f730067
--- /dev/null
+++ b/upstream/linux-5.10/sound/soc/sanechips/zx29_ak4940.c
@@ -0,0 +1,2041 @@
+/*
+ * zx297520v3_es8312.c  --  zx29-ak4940 ALSA SoC Audio board driver
+ *
+ * Copyright (C) 2022, ZTE Corporation.
+ *
+ * Based on smdk_wm8994.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "../codecs/ak4940.h"
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+
+ 
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+//#include <sound/tlv.h>
+//#include <sound/soc.h>
+//#include <sound/jack.h>
+//#include <sound/zx29_snd_platform.h>
+//#include <mach/iomap.h>
+//#include <mach/board.h>
+#include <linux/of_gpio.h>
+
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+
+
+#include "i2s.h"
+
+#define ZX29_I2S_TOP_LOOP_REG	0xac
+
+
+#if 1
+ 
+#define  ZXIC_MCLK                    26000000
+#define  ZX29_AK4940_FREQ   26000000
+
+#define  ZXIC_PLL_CLKIN_MCLK		  0
+
+
+#define zx_reg_sync_write(v, a) \
+        do {    \
+            iowrite32(v, a);    \
+        } while (0)
+
+#define zx_read_reg(addr) \
+    ioread32(addr)
+
+#define zx_write_reg(addr, val)   \
+	zx_reg_sync_write(val, addr)
+
+
+
+struct zx29_board_data {
+	const char *name;
+	struct device *dev;
+
+	int codec_refclk;
+	int gpio_pwen;	
+	int gpio_pdn;
+	void __iomem *sys_base_va;	
+};
+
+//#define AON_WIFI_BT_CLK_CFG2  ((volatile unsigned int *)(ZX_TOP_CRM_BASE + 0x94))
+ /* Default ZX29s */
+static struct zx29_board_data zx29_platform_data = {
+	.codec_refclk = ZX29_AK4940_FREQ,
+};
+ static struct platform_device *zx29_snd_device;
+ 
+ static DEFINE_RAW_SPINLOCK(codec_pa_lock);
+ 
+ static int set_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol);
+ static int get_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol);
+
+
+#ifdef USE_ALSA_VOICE_FUNC
+ extern int zDrv_Audio_Printf(void *pFormat, ...);
+ extern int zDrvVp_GetVol_Wrap(void);
+ extern int zDrvVp_SetVol_Wrap(int volume);
+ extern int zDrvVp_GetPath_Wrap(void);
+ extern int zDrvVp_SetPath_Wrap(int path);
+ extern int zDrvVp_SetMute_Wrap(bool enable);
+ extern bool zDrvVp_GetMute_Wrap(void);
+ extern int zDrvVp_SetTone_Wrap(int toneNum);
+ 
+ static int vp_GetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetVol(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_GetVol(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_getTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ 
+ static int audio_GetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int audio_SetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+
+ 
+ //static const DECLARE_TLV_DB_SCALE(vp_path_tlv, 0, 300, 0);
+ 
+ static const char * const vpath_in_text[] = {
+	 "handset", "speak", "headset", "bluetooth",
+ };
+ 
+ static const char *tone_class[] = {
+	 "Lowpower", "Sms", "Callstd", "Alarm", "Calltime",
+ };
+ 
+ static const struct soc_enum vpath_in_enum =	 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(vpath_in_text), vpath_in_text); 
+ 
+ static const struct soc_enum tone_class_enum[] = {
+	 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tone_class), tone_class),
+ };
+ 
+ static const struct snd_kcontrol_new vp_snd_controls[] = {  
+	 SOC_ENUM_EXT("voice processing path select",vpath_in_enum,vp_GetPath,vp_SetPath),
+	 //SOC_SINGLE_EXT_TLV("voice processing path Volume",0, 5, 5, 0,vp_GetVol, vp_SetVol,vp_path_tlv), 
+	 SOC_SINGLE_EXT("voice processing path Volume",0, 5, 5, 0,vp_GetVol, vp_SetVol),
+	 SOC_SINGLE_EXT("voice uplink mute", 0, 1, 1, 0,vp_GetMute, vp_SetMute),
+	 SOC_ENUM_EXT("voice tone sel", tone_class_enum[0], vp_getTone, vp_SetTone),
+	 SOC_SINGLE_BOOL_EXT("path stauts dump", 0,get_path_stauts_switch, set_path_stauts_switch),
+	 SOC_ENUM_EXT("audio path select",vpath_in_enum,audio_GetPath,audio_SetPath),
+ };
+ 
+ static int curtonetype = 0;
+ static int vp_getTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 ucontrol->value.integer.value[0] = curtonetype;
+	 return 0;
+ }
+ 
+ static int vp_SetTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 int vol = 0,ret = 0, tonenum;
+	 tonenum = ucontrol->value.integer.value[0];
+	 curtonetype = tonenum;
+	 //printk("Alsa vp_SetTone tonenum=%d\n", tonenum);
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetTone_Wrap)(tonenum);
+	 if(ret < 0)
+	 {
+		 printk(KERN_ERR "vp_SetTone fail = %d\n", tonenum);
+		 return ret;
+	 }
+	 return 0;
+ }
+ 
+ static int vp_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 int enable = 0,ret = 0;
+	 enable = ucontrol->value.integer.value[0];
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetMute_Wrap)(enable);
+	 if(ret < 0)
+	 {
+	   printk(KERN_ERR "vp_SetMute fail = %d\n",enable);
+	   return ret;
+	 }
+	 return 0;
+ }
+ 
+ static int vp_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {		 
+		//ucontrol->value.integer.value[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetMute_Wrap)();
+		return 0;
+ }
+ 
+ static int vp_SetVol(struct snd_kcontrol *kcontrol,
+								struct snd_ctl_elem_value *ucontrol)
+ {
+		int vol = 0,ret = 0;
+		vol = ucontrol->value.integer.value[0];
+		//ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetVol_Wrap)(vol);
+		if(ret < 0)
+		{
+		   printk(KERN_ERR "vp_SetVol fail = %d\n",vol);
+		   return ret;
+	   }
+	 return 0;
+ }
+ static int vp_GetVol(struct snd_kcontrol *kcontrol,
+								struct snd_ctl_elem_value *ucontrol)
+ {		 
+		//ucontrol->value.integer.value[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetVol_Wrap)();
+		return 0;
+ }
+ static int vp_GetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {	 
+	 //ucontrol->value.enumerated.item[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetPath_Wrap)();
+	 return 0;
+ }
+ static int vp_SetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {
+	 int ret = 0,path = 0;
+	 unsigned long	flags;
+	 path = ucontrol->value.enumerated.item[0];
+ 
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetPath_Wrap)(path);
+	 if(ret < 0)
+	 {
+	   printk(KERN_ERR "vp_SetPath fail = %d\n",path);
+	   return ret;
+	 }
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+	 switch (path) {
+	 case 0:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+		 break;
+	 case 1:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_40, GPIO_HIGH);
+		 break;
+	 case 2:
+		 break;
+	 case 3:
+		 break;
+	 default:
+		 break;
+	 }
+#endif
+	 return 0;
+ }
+ 
+ static int curpath = 0;
+ static int audio_GetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {	 
+	 ucontrol->value.enumerated.item[0] = curpath;
+	 return 0;
+ }
+ 
+ static int audio_SetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {
+	 int ret = 0,path = 0;
+	 unsigned long	flags;
+	 
+	 path = ucontrol->value.enumerated.item[0];
+	 curpath = path;
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+	 switch (path) {
+	 case 0:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+		 break;
+	 case 1:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_40, GPIO_HIGH);
+		 break;
+	 case 2:
+		 break;
+	 case 3:
+		 break;
+	 default:
+		 break;
+	 }
+#endif
+	 return 0;
+ }
+ 
+ typedef enum
+ {
+	 VP_PATH_HANDSET	=0, 	
+	 VP_PATH_SPEAKER,		 
+	 VP_PATH_HEADSET,					  
+	 VP_PATH_BLUETOOTH, 				   
+	 VP_PATH_BLUETOOTH_NO_NR,					 
+	 VP_PATH_HSANDSPK,
+	 
+	 VP_PATH_OFF = 255, 				 
+	 
+	 MAX_VP_PATH = VP_PATH_OFF				 
+ }T_ZDrv_VpPath;
+ 
+ extern int zDrvVp_Loop(T_ZDrv_VpPath path);
+
+ 
+//#else
+ static const struct snd_kcontrol_new machine_snd_controls[] = {		 
+	 SOC_SINGLE_BOOL_EXT("path stauts dump", 0,get_path_stauts_switch, set_path_stauts_switch),
+ };
+ 
+
+ 
+ //extern int rt5670_hs_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+ 
+ int path_stauts_switch = 0;
+ static int set_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+ {
+	 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	 struct snd_soc_dapm_path *p;
+ 
+	 int path_stauts_switch = ucontrol->value.integer.value[0];
+ 
+	 
+	 if (path_stauts_switch == 1)
+	 {
+		 list_for_each_entry(p, &card->paths, list){
+			 
+		   //print_audio("Alsa	path name (%s),longname (%s),sink (%s),source (%s),connect %d \n", p->name,p->long_name,p->sink->name,p->source->name,p->connect);
+		   //printk("Alsa  path longname %s,sink %s,source %s,connect %d \n", p->long_name,p->sink->name,p->source->name,p->connect);
+ 
+		 }
+	 }
+	 return 0;
+ }
+ 
+ static int get_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+ {
+	 
+	 ucontrol->value.integer.value[0] = path_stauts_switch;
+	 return 0;
+ };
+#endif 
+ 
+#ifdef CONFIG_SND_SOC_JACK_DECTEC
+ 
+ static struct snd_soc_jack codec_headset;
+ 
+ /* Headset jack detection DAPM pins */
+ static struct snd_soc_jack_pin codec_headset_pins[] = {
+	 {
+		 .pin = "Headphone",
+		 .mask = SND_JACK_HEADPHONE,
+	 },
+ };
+ 
+#endif
+ 
+ static int zx29startup(struct snd_pcm_substream *substream)
+ {
+ //  int ret = 0;
+	 print_audio("Alsa	Entered func %s\n", __func__);
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx29_startup device=%d,stream=%d\n", substream->pcm->device, substream->stream);
+ 
+	 struct snd_pcm *pcmC0D0p = snd_lookup_minor_data(16, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+	 struct snd_pcm *pcmC0D1p = snd_lookup_minor_data(17, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+	 struct snd_pcm *pcmC0D2p = snd_lookup_minor_data(18, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);	 
+	 struct snd_pcm *pcmC0D3p = snd_lookup_minor_data(19, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);	
+	 if ((pcmC0D0p == NULL) || (pcmC0D1p == NULL) || (pcmC0D2p == NULL) || (pcmC0D3p == NULL))
+		 return  -EINVAL;	  
+	 if ((pcmC0D0p->streams[0].substream_opened && pcmC0D1p->streams[0].substream_opened) || 
+		 (pcmC0D0p->streams[0].substream_opened && pcmC0D2p->streams[0].substream_opened) || 
+		 (pcmC0D0p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened) || 
+		 (pcmC0D1p->streams[0].substream_opened && pcmC0D2p->streams[0].substream_opened) ||
+		 (pcmC0D1p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened) ||
+		 (pcmC0D2p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened))
+		 BUG();
+#if 0
+	 unsigned long	flags;
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 mdelay(1);  
+ 
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+	 }
+#endif
+
+	 
+	 return 0;
+ }
+ 
+ static void zx29_shutdown(struct snd_pcm_substream *substream)
+ {
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx297520xx_shutdown device=%d, stream=%d\n", substream->pcm->device, substream->stream);
+ //  print_audio("Alsa	Entered func %s, stream=%d\n", __func__, substream->stream);
+ 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+#endif
+	 }
+	 
+	 if (snd_soc_dai_active(cpu_dai))
+		 return;
+ 
+
+ 
+ }
+ 
+ static void zx29_shutdown2(struct snd_pcm_substream *substream)
+ {
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx29_shutdown2 device=%d, stream=%d\n", substream->pcm->device, substream->stream);
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+#endif
+#ifdef USE_ALSA_VOICE_FUNC
+		 //CPPS_FUNC(cpps_callbacks, zDrvVp_Loop)(VP_PATH_OFF);
+#endif
+	 }
+ 
+	 if (snd_soc_dai_active(cpu_dai))
+		 return;
+ 
+
+ }
+ static int zx29_init_paiftx(struct snd_soc_pcm_runtime *rtd)
+ {
+	 //struct snd_soc_codec *codec = rtd->codec;
+	 //struct snd_soc_dapm_context *dapm = &codec->dapm;
+ 
+	 //snd_soc_dapm_enable_pin(dapm, "HPOL");
+	 //snd_soc_dapm_enable_pin(dapm, "HPOR");
+ 
+	 /* Other pins NC */
+ //  snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
+ 
+ //  print_audio("Alsa	Entered func %s\n", __func__);
+ 
+	 return 0;
+ }
+ static int zx29_hw_params(struct snd_pcm_substream *substream,
+										struct snd_pcm_hw_params *params)
+ {
+     print_audio("Alsa:	Entered func %s\n", __func__);
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	 int ret;
+	 int rfs = 0, frq_out = 0;	 
+	 switch (params_rate(params)) {
+	 case 8000:
+	 case 16000:
+	 case 11025:
+	 case 22050:
+	 case 24000:
+	 case 32000:
+	 case 44100:
+	 case 48000:
+		 rfs = 32;
+		 break;
+	 default:
+	 	{
+	 	    ret =  -EINVAL;
+		    print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret);	 	      
+		 	return ret;
+	 	}
+	 }
+	 
+	 frq_out = params_rate(params) * rfs * 2;
+	 
+	 /* Set the Codec DAI configuration */
+	 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+
+
+	 /* Set the AP DAI configuration */
+	 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: ap dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+ 
+	 /* Set the Codec DAI clk */	 
+	 /*ret =snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_BCLK1,
+								  fs*datawidth*2, 256*fs);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		 return ret;
+	}
+	 */
+	 
+	 ret = snd_soc_dai_set_sysclk(codec_dai, AK4940_CLKID_BCLK,ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+	 
+	 /* Set the AP DAI clk */
+	 ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_122M88, SND_SOC_CLOCK_IN);
+	 //ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+ 
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: cpu dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+     print_audio("Alsa:	Entered func %s end\n", __func__);
+	 
+	 return 0;
+ }
+
+static int zx29_hw_params_lp(struct snd_pcm_substream *substream,
+									   struct snd_pcm_hw_params *params)
+{
+	print_audio("Alsa: Entered func %s\n", __func__);
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	int ret;
+	int rfs = 0, frq_out = 0;	
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+	case 11025:
+	case 22050:
+	case 24000:
+	case 32000:
+	case 44100:
+	case 48000:
+		rfs = 32;
+		break;
+	default:
+	   {
+		   ret =  -EINVAL;
+		   print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret); 			 
+		   return ret;
+	   }
+	}
+	
+	frq_out = params_rate(params) * rfs * 2;
+	
+	/* Set the Codec DAI configuration */
+	/*
+	
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							  | SND_SOC_DAIFMT_NB_NF
+							  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0){
+	   
+		print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		return ret;
+	}
+	*/ 
+
+	
+	/* Set the AP DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+							  | SND_SOC_DAIFMT_NB_NF
+							  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0){
+	   
+		print_audio("Alsa: ap dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		return ret;
+	}
+
+	/* Set the Codec DAI clk */ 	
+	/*ret =snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_BCLK1,
+								 fs*datawidth*2, 256*fs);
+	if (ret < 0){
+	   
+		print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		return ret;
+   }
+	*/
+	/*
+	ret = snd_soc_dai_set_sysclk(codec_dai, ES8312_CLKID_MCLK,ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	if (ret < 0){	   
+		print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		return ret;
+	}
+	*/
+	/* Set the AP DAI clk */
+	ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+
+	if (ret < 0){	   
+		print_audio("Alsa: cpu dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		return ret;
+	}
+	print_audio("Alsa: Entered func %s end\n", __func__);
+	
+	return 0;
+}
+
+
+ 
+
+ 
+
+ static int zx29_hw_params_voice(struct snd_pcm_substream *substream,
+										struct snd_pcm_hw_params *params)
+ {
+	 print_audio("Alsa: Entered func %s\n", __func__);
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	 int ret;
+	 int rfs = 0, frq_out = 0;	 
+	 switch (params_rate(params)) {
+	 case 8000:
+	 case 16000:
+	 case 11025:
+	 case 22050:
+	 case 24000:
+	 case 32000:
+	 case 44100:
+	 case 48000:
+		 rfs = 32;
+		 break;
+	 default:
+		{
+			ret =  -EINVAL;
+			print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret);			  
+			return ret;
+		}
+	 }
+	 
+	 frq_out = params_rate(params) * rfs * 2;
+	 
+	 /* Set the Codec DAI configuration */
+	 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+		
+		 print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+ 
+ 
+
+	 /* Set the Codec DAI clk */	 
+	 /*ret =snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_BCLK1,
+								  fs*datawidth*2, 256*fs);
+	 if (ret < 0){
+		
+		 print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		 return ret;
+	}
+	
+	 
+	 ret = snd_soc_dai_set_sysclk(codec_dai, AK4940_CLKID_BCLK,ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	 if (ret < 0){		
+		 print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+	 
+	 */
+
+	 print_audio("Alsa: Entered func %s end\n", __func__);
+	 
+	 return 0;
+ }
+
+										 
+ int zx29_prepare2(struct snd_pcm_substream *substream)
+ {
+	 int path, ret;
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_Loop)(VP_PATH_SPEAKER);
+		 if (ret < 0)
+			 return -1;
+	 }
+	 
+	 return 0;
+ } 
+ static void zx29_i2s_top_reg_cfg(void)
+ {
+	 unsigned int i2s_top_reg;
+	 int ret = 0;
+ 
+#ifdef CONFIG_USE_PIN_I2S0
+	 ret = gpio_request(PIN_I2S0_WS, "i2s0_ws");
+	 if (ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S0_CLK, "i2s0_clk");
+	 if (ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S0_DIN, "i2s0_din");
+	 if (ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S0_DOUT, "i2s0_dout");
+	 if (ret < 0)
+		 BUG();
+	 zx29_gpio_config(PIN_I2S0_WS, FUN_I2S0_WS);
+	 zx29_gpio_config(PIN_I2S0_CLK, FUN_I2S0_CLK);
+	 zx29_gpio_config(PIN_I2S0_DIN, FUN_I2S0_DIN);
+	 zx29_gpio_config(PIN_I2S0_DOUT, FUN_I2S0_DOUT);
+	 
+	 //top i2s1 cfg
+	 i2s_top_reg = zx_read_reg(ZX29_I2S_LOOP_CFG);
+	 i2s_top_reg &= 0xfffffff8;
+	 i2s_top_reg |= 0x00000001; //	inter arm_i2s1--top i2s1
+	 zx_write_reg(ZX29_I2S_LOOP_CFG, i2s_top_reg);
+#elif defined (CONFIG_USE_PIN_I2S1)
+	
+
+	 ret = gpio_request(PIN_I2S1_WS,"i2s1_ws");
+	 if(ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S1_CLK,"i2s1_clk");
+	 if(ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S1_DIN,"i2s1_din");
+	 if(ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S1_DOUT,"i2s1_dout");
+	 if(ret < 0)
+		 BUG();
+	 zx29_gpio_config(PIN_I2S1_WS, FUN_I2S1_WS);
+	 zx29_gpio_config(PIN_I2S1_CLK, FUN_I2S1_CLK);
+	 zx29_gpio_config(PIN_I2S1_DIN, FUN_I2S1_DIN);
+	 zx29_gpio_config(PIN_I2S1_DOUT, FUN_I2S1_DOUT);
+		 
+	 //top i2s2 cfg
+	 i2s_top_reg = zx_read_reg(ZX29_I2S_LOOP_CFG);
+	 i2s_top_reg &= 0xfff8ffff;
+	 i2s_top_reg |= 0x00010000; //	inter arm_i2s1--top i2s2
+	 zx_write_reg(ZX29_I2S_LOOP_CFG, i2s_top_reg);
+#endif
+ 
+	 // inter loop
+	 //i2s_top_reg = zx_read_reg(ZX29_I2S_LOOP_CFG);
+	 //i2s_top_reg &= 0xfffffe07;
+	 //i2s_top_reg |= 0x000000a8; //	inter arm_i2s2--afe i2s
+	 //zx_write_reg(ZX29_I2S_LOOP_CFG, i2s_top_reg);
+	 
+ //  print_audio("Alsa %s i2s loop cfg reg=%x\n",__func__, zx_read_reg(ZX29_I2S_LOOP_CFG));  
+ }
+ 
+ static int zx29_late_probe(struct snd_soc_card *card)
+ {
+	 //struct snd_soc_codec *codec = card->rtd[0].codec;
+	 //struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+	 int ret;
+ //  print_audio("Alsa	zx29_late_probe entry!\n");
+ 
+#ifdef CONFIG_SND_SOC_JACK_DECTEC
+	 
+	 ret = snd_soc_jack_new(codec, "Headset",
+							SND_JACK_HEADSET |SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
+							&codec_headset);
+	 if (ret)
+		 return ret;
+ 
+	 ret = snd_soc_jack_add_pins(&codec_headset,
+								 ARRAY_SIZE(codec_headset_pins),
+								 codec_headset_pins);
+	 if (ret)
+		 return ret;
+       #ifdef CONFIG_SND_SOC_codec
+	 //rt5670_hs_detect(codec, &codec_headset);
+       #endif
+#endif
+ 
+	 return 0;
+ }
+ 
+ static struct snd_soc_ops zx29_ops = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 .hw_params = zx29_hw_params,
+ };
+  static struct snd_soc_ops zx29_ops_lp = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 .hw_params = zx29_hw_params_lp,
+ };
+ static struct snd_soc_ops zx29_ops1 = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 //.hw_params = zx29_hw_params1,
+ };
+ 
+ static struct snd_soc_ops zx29_ops2 = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown2,
+	 //.hw_params = zx29_hw_params1,
+	 .prepare = zx29_prepare2,
+ };
+ static struct snd_soc_ops voice_ops = {
+	 //.startup = zx29_startup,
+	 //.shutdown = zx29_shutdown2,
+	 .hw_params = zx29_hw_params_voice,
+	 //.prepare = zx29_prepare2,
+ };
+
+ 
+ enum {
+	 MERR_DPCM_AUDIO = 0,
+	 MERR_DPCM_DEEP_BUFFER,
+	 MERR_DPCM_COMPR,
+ };
+
+ 
+#if 0
+ 
+ static struct snd_soc_card zxic_soc_card = {
+	 .name = "zx298501_ak4940",
+	 .owner = THIS_MODULE,
+	 .dai_link = &zxic_dai_link,
+	 .num_links = ARRAY_SIZE(zxic_dai_link),
+#ifdef USE_ALSA_VOICE_FUNC
+	 .controls = vp_snd_controls,
+	 .num_controls = ARRAY_SIZE(vp_snd_controls),
+#endif
+ 
+ //  .late_probe = zx29_late_probe,
+	 
+ };
+#endif 
+ //static struct zx298501_ak4940_pdata *zx29_platform_data;
+ 
+ static int zx29_setup_pins(struct zx29_board_data *codec_pins, char *fun)
+ {
+	 int ret;
+ 
+	 //ret = gpio_request(codec_pins->codec_refclk, "codec_refclk");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio: %s pin already in use\n", fun);
+		 return ret;
+	 }
+	 //zx29_gpio_config(codec_pins->codec_refclk, GPIO17_CLK_OUT2);
+ 
+#ifdef  _USE_7520V3_PHONE_TYPE_C31F
+	 ret = gpio_request_one(ZX29_GPIO_39, GPIOF_OUT_INIT_LOW, "codec_pa");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio:  codec_pa in use\n");
+		 return ret;
+	 }
+	 
+	 ret = gpio_request_one(ZX29_GPIO_40, GPIOF_OUT_INIT_LOW, "codec_sw");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio:  codec_sw in use\n");
+		 return ret;
+	 }
+#endif
+ 
+	 return 0;
+ }
+#endif
+
+ 
+ static int zx29_remove(struct platform_device *pdev)
+ {
+	 gpio_free(zx29_platform_data.codec_refclk);
+	 platform_device_unregister(zx29_snd_device);
+	 return 0;
+ }
+ 
+
+ 
+#if  0
+
+ /*
+  * Default CFG switch settings to use this driver:
+  *	ZX29
+  */
+
+ /*
+  * Configure audio route as :-
+  * $ amixer sset 'DAC1' on,on
+  * $ amixer sset 'Right Headphone Mux' 'DAC'
+  * $ amixer sset 'Left Headphone Mux' 'DAC'
+  * $ amixer sset 'DAC1R Mixer AIF1.1' on
+  * $ amixer sset 'DAC1L Mixer AIF1.1' on
+  * $ amixer sset 'IN2L' on
+  * $ amixer sset 'IN2L PGA IN2LN' on
+  * $ amixer sset 'MIXINL IN2L' on
+  * $ amixer sset 'AIF1ADC1L Mixer ADC/DMIC' on
+  * $ amixer sset 'IN2R' on
+  * $ amixer sset 'IN2R PGA IN2RN' on
+  * $ amixer sset 'MIXINR IN2R' on
+  * $ amixer sset 'AIF1ADC1R Mixer ADC/DMIC' on
+  */
+
+/* ZX29 has a 16.934MHZ crystal attached to ak4940 */
+#define ZX29_AK4940_FREQ 16934000
+
+
+
+
+
+static int zx29_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	unsigned int pll_out;
+	int ret;
+
+	/* AIF1CLK should be >=3MHz for optimal performance */
+	if (params_width(params) == 24)
+		pll_out = params_rate(params) * 384;
+	else if (params_rate(params) == 8000 || params_rate(params) == 11025)
+		pll_out = params_rate(params) * 512;
+	else
+		pll_out = params_rate(params) * 256;
+
+	ret = snd_soc_dai_set_pll(codec_dai, AK4940_FLL1, AK4940_FLL_SRC_MCLK1,
+					ZX29_AK4940_FREQ, pll_out);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, AK4940_SYSCLK_FLL1,
+					pll_out, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * ZX29 AK4940 DAI operations.
+ */
+static struct snd_soc_ops zx29_ops = {
+	.hw_params = smdk_hw_params,
+};
+
+static int zx29_ak4940_init_paiftx(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
+
+	/* Other pins NC */
+	snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
+	snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTLN");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTLP");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTRP");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTRN");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
+	snd_soc_dapm_nc_pin(dapm, "IN1LP");
+	snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
+	snd_soc_dapm_nc_pin(dapm, "IN1RP");
+	snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
+
+	return 0;
+}
+#endif
+
+
+
+
+enum {
+	AUDIO_DL_MEDIA = 0,
+	AUDIO_DL_VOICE,
+	AUDIO_DL_2G_AND_3G_VOICE,
+	AUDIO_DL_VP_LOOP,	
+	AUDIO_DL_3G_VOICE,
+	
+	AUDIO_DL_MAX,
+};
+SND_SOC_DAILINK_DEF(dummy, \
+	DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+//SND_SOC_DAILINK_DEF(cpu_i2s0, \
+//	DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+SND_SOC_DAILINK_DEF(cpu_i2s0, \
+	DAILINK_COMP_ARRAY(COMP_CPU("E1D02000.i2s")));
+
+
+SND_SOC_DAILINK_DEF(voice_cpu, \
+	DAILINK_COMP_ARRAY(COMP_CPU("soc:voice_audio")));
+
+SND_SOC_DAILINK_DEF(voice_2g_3g, \
+	DAILINK_COMP_ARRAY(COMP_CPU("voice_2g_3g-dai")));
+
+SND_SOC_DAILINK_DEF(voice_3g, \
+		DAILINK_COMP_ARRAY(COMP_CPU("voice_3g-dai")));
+
+
+
+//SND_SOC_DAILINK_DEF(ak4940, \
+//	DAILINK_COMP_ARRAY(COMP_CODEC("ak4940.1-0012", "ak4940-aif")));
+SND_SOC_DAILINK_DEF(dummy_cpu, \
+		DAILINK_COMP_ARRAY(COMP_CPU("soc:zx29_snd_dummy")));
+//SND_SOC_DAILINK_DEF(dummy_platform, \
+//	DAILINK_COMP_ARRAY(COMP_PLATFORM("soc:zx29_snd_dummy")));
+
+SND_SOC_DAILINK_DEF(dummy_codec, \
+		DAILINK_COMP_ARRAY(COMP_CODEC("soc:zx29_snd_dummy", "zx29_snd_dummy_dai")));
+SND_SOC_DAILINK_DEF(ak4940_codec, \
+		DAILINK_COMP_ARRAY(COMP_CODEC("ak4940.1-0012", "ak4940-aif")));
+
+
+//SND_SOC_DAILINK_DEF(media_platform, \
+//	DAILINK_COMP_ARRAY(COMP_PLATFORM("zx29-pcm-audio")));
+SND_SOC_DAILINK_DEF(media_platform, \
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("E1D02000.i2s")));
+//SND_SOC_DAILINK_DEF(voice_cpu, \
+//	DAILINK_COMP_ARRAY(COMP_CPU("E1D02000.i2s")));
+
+SND_SOC_DAILINK_DEF(voice_platform, \
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("soc:voice_audio")));
+
+			
+//static struct snd_soc_dai_link zx29_dai_link[] = {
+struct snd_soc_dai_link zx29_dai_link[] = {
+
+ 
+
+
+ {
+	.name = "zx29_snd_dummy",//codec name
+	.stream_name = "zx29_snd_dumy",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &zx29_ops_lp,
+	.init = zx29_init_paiftx,
+	SND_SOC_DAILINK_REG(cpu_i2s0, dummy_codec, media_platform),
+	
+},
+{
+	.name = "ak4940.1-0012",//codec name
+	.stream_name = "MultiMedia",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &zx29_ops,
+
+ 	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(cpu_i2s0, ak4940_codec, media_platform),
+
+},
+{
+	.name = "voice",//codec name
+	.stream_name = "voice",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	//.init = zx29_init_paiftx,
+	
+	
+	//SND_SOC_DAILINK_REG(cpu_i2s0, ak4940_codec, voice_platform),
+
+	SND_SOC_DAILINK_REG(voice_cpu, ak4940_codec, voice_platform),
+
+},
+{
+	.name = "voice_2g3g_teak",//codec name
+	.stream_name = "voice_2g3g_teak",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	//.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, ak4940_codec, voice_platform),
+
+},
+
+{
+	.name = "voice_3g",//codec name
+	.stream_name = "voice_3g",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	//.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, ak4940_codec, voice_platform),
+
+},
+
+{
+	.name = "loop_test",//codec name
+	.stream_name = "loop_test",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &zx29_ops,
+
+	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(cpu_i2s0, ak4940_codec, dummy),
+
+},
+
+#if 0
+
+	 [AUDIO_DL_MEDIA] = {
+		 .name = "ak4940",
+		 .stream_name = "MultiMedia",
+		 .nonatomic = true,
+		 //.dynamic = 1,
+		 //.dpcm_playback = 1,
+		 .ops = &zx29_ops,
+		 .init = zx29_init_paiftx,
+		 SND_SOC_DAILINK_REG(cpu_i2s0, ak4940, media_platform),
+	 },
+	 
+	 [AUDIO_DL_VOICE] = {
+
+		 .name = "voice_call",
+		 .stream_name = "voice",
+		 //.codec_name = "es8312.1-0018",
+		 //.codec_dai_name = "ES8312 HiFi",
+		 //.cpu_dai_name = "voice", //"snd-soc-dummy-dai",
+		 //.platform_name  = "dummy",
+		 .init = zx29_init_paiftx,
+		 .ops = &zx29_ops1,
+	     SND_SOC_DAILINK_REG(voice, ak4940, dummy),
+		 
+		},
+	 [AUDIO_DL_2G_AND_3G_VOICE] = {
+
+		 .name = "voice_2g_3g",
+		 .stream_name = "voice_2g_3g",
+		 //.codec_name = "es8312.1-0018",
+		 //.codec_dai_name = "ES8312 HiFi",
+		 //.cpu_dai_name = "voice", //"snd-soc-dummy-dai",
+		 //.platform_name  = "voice_audio",
+		 .init = zx29_init_paiftx,
+		 .ops = &zx29_ops1,
+		 SND_SOC_DAILINK_REG(voice_2g_3g, ak4940, voice_audio),
+		 
+		},
+	 [AUDIO_DL_VP_LOOP] = {
+
+		 .name = "loop_test",
+			 //.codec_name = "es8312.1-0018",
+		 //.codec_dai_name = "ES8312 HiFi",
+		 //.cpu_dai_name = "voice", //"snd-soc-dummy-dai",
+		 //.platform_name  = "snd-soc-dummy",
+		 .init = zx29_init_paiftx,
+		 .ops = &zx29_ops2,
+		  SND_SOC_DAILINK_REG(voice, ak4940, dummy),
+		 
+		}, .stream_name = "loop_voice",
+	
+	 [AUDIO_DL_3G_VOICE] = {
+
+		 .name = "voice_3g", // 3g nb,wb
+		 .stream_name = "voice_3g",
+		 //.codec_name = "es8312.1-0018",
+		 //.codec_dai_name = "ES8312 HiFi",
+		 //.cpu_dai_name = "voice", //"snd-soc-dummy-dai",
+		 //.platform_name  = "voice_audio",
+		 .init = zx29_init_paiftx,
+		 .ops = &zx29_ops1,
+		 SND_SOC_DAILINK_REG(voice, ak4940, voice_audio),
+		 
+		}
+#endif
+};
+
+
+
+static struct snd_soc_card zx29_soc_card = {
+	.name = "zx29-sound-card",
+	.owner = THIS_MODULE,
+	.dai_link = zx29_dai_link,
+	.num_links = ARRAY_SIZE(zx29_dai_link),
+#ifdef USE_ALSA_VOICE_FUNC
+	 .controls = vp_snd_controls,
+	 .num_controls = ARRAY_SIZE(vp_snd_controls),
+#endif	
+};
+
+static const struct of_device_id zx29_ak4940_of_match[] = {
+	{ .compatible = "zxic,zx29_ak4940", .data = &zx29_platform_data },
+	{},
+};
+MODULE_DEVICE_TABLE(of, zx29_ak4940_of_match);
+
+static void zx29_i2s_top_pin_cfg(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct pinctrl *p;
+	struct pinctrl_state *s;
+	int ret = 0;
+
+
+	struct resource *res;
+	void __iomem	*reg_base;
+	unsigned int val;
+
+
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "soc_sys");
+	if (!res) {
+		dev_err(dev, "Reg region missing (%s)\n", "soc_sys");
+		//return -ENXIO;
+	}
+
+	#if 0
+	reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(reg_base )) {
+			dev_err(dev, "Reg region ioremap (%s) err=%li\n", "soc_sys",PTR_ERR(reg_base ));
+		//return PTR_ERR(reg_base );
+	}
+
+	#else
+	reg_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	#endif
+	 
+//#if 1 //CONFIG_USE_PIN_I2S0
+#ifdef 	CONFIG_USE_TOP_I2S0
+
+	dev_info(dev, "%s: arm i2s1 to top i2s0!!\n", __func__); 
+	//9300
+		 
+	//top i2s1 cfg
+	val = zx_read_reg(reg_base+ZX29_I2S_TOP_LOOP_REG);
+	val &= ~(0x7<<15);
+	val |= 0x1<<15;; //	inter arm_i2s1--top i2s1
+	zx_write_reg(reg_base+ZX29_I2S_TOP_LOOP_REG, val);
+#else //(CONFIG_USE_PIN_I2S1)
+    //8501evb    	
+
+	dev_info(dev, "%s: arm i2s1 to top i2s1!\n", __func__); 
+			 
+	//top i2s2 cfg
+	val = zx_read_reg(reg_base+ZX29_I2S_TOP_LOOP_REG);
+
+	val &= 0xfffffff8;
+	val |= 0x00000001;//	inter arm_i2s1--top i2s2
+	zx_write_reg(reg_base+ZX29_I2S_TOP_LOOP_REG, val);
+#endif
+
+
+
+	p = devm_pinctrl_get(dev);
+	if (IS_ERR(p)) {
+		dev_err(dev, "%s: pinctrl get failure ,p=0x%llx,dev=0x%llx!!\n", __func__,p,dev);
+		return;
+	}
+	
+	dev_info(dev, "%s: get pinctrl ,p=0x%llx,dev=0x%llx!!\n", __func__,p,dev); 
+
+	s = pinctrl_lookup_state(p, "top_i2s");
+	if (IS_ERR(s)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}
+	ret = pinctrl_select_state(p, s);
+	if (ret < 0) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " select state failure!!\n");
+		return;
+	}
+	dev_info(dev, "%s: set pinctrl end!\n", __func__);	
+
+	
+
+ 
+}
+#if 0
+static int codec_power_on(struct zx29_board_data * board,bool on_off)
+{
+	int ret = 0;
+	//struct zx29_board_data *board = dev_get_drvdata(dev);
+	struct device *dev = board->dev;
+
+	dev_info(dev, "%s:start %s board gpio_pwen=%d,gpio_pdn=%d on_off=%d\n",__func__,board->name,board->gpio_pwen,board->gpio_pdn,on_off);
+
+	if(on_off){
+
+		ret = gpio_direction_output(board->gpio_pwen, 1);	
+		if (ret < 0) {
+				dev_err(dev,"gpio_pwen %d direction fail set to 1: %d\n",board->gpio_pwen, ret);
+				return ret;
+		 }
+		
+		ret = gpio_direction_output(board->gpio_pdn, 1);	
+		if (ret < 0) {
+				dev_err(dev,"gpio_pdn %d direction fail set to 1: %d\n",board->gpio_pdn, ret);
+				return ret;
+		 }
+
+		
+	}
+	else{
+		ret = gpio_direction_output(board->gpio_pwen, 0);	
+		if (ret < 0) {
+				dev_err(dev,"gpio_pwen %d direction fail set to 0: %d\n",board->gpio_pwen, ret);
+				return ret;
+		 }
+		
+		ret = gpio_direction_output(board->gpio_pdn, 0);	
+		if (ret < 0) {
+				dev_err(dev,"gpio_pdn %d direction fail set to 0: %d\n",board->gpio_pdn, ret);
+				return ret;
+		 }
+
+	
+	}
+
+	return ret;
+
+}
+#endif
+
+
+#ifdef  CONFIG_PA_SA51034
+//sa51034
+#define SA51034_DEBUG
+
+#define SA51034_01_LATCHED_FAULT		0x01
+#define SA51034_02_STATUS_LOAD_DIAGNOSTIC      0x02
+#define SA51034_03_CONTROL			0x03
+#define SA51034_MAX_REGISTER SA51034_03_CONTROL
+
+struct sa51034_priv {
+	struct i2c_client *i2c;
+	struct regmap *regmap;
+	int pwen_gpio;//add new
+	int mute_gpio;
+	int fs;
+
+};
+static int sa51034_set_mute(struct sa51034_priv *sa51034,int mute);
+static int sa51034_get_mute(struct sa51034_priv *sa51034,int *mute); 
+
+
+
+
+struct sa51034_priv *g_sa51034 = NULL;
+/* ak4940 register cache & default register settings */
+static const struct reg_default sa51034_reg[] = {
+	{ 0x01, 0x00 },  /* SA51034_00_LATCHED_FAULT	*/
+	{ 0x02, 0x00 },  /* SA51034_01_STATUS_LOAD_DIAGNOSTIC	*/
+	{ 0x03, 0x00 },  /* SA51034_02_CONTROL			*/
+	
+};
+	
+static const char * const pa_gain_select_texts[] = {
+	"20dB", "26dB","30dB", "36dB",
+};
+static const char * const power_limit_select_texts[] = {
+	"PL-5V", "PL-5.9V","PL-7V", "PL-8.4V","PL-9.8V", "PL-11.8V","PL-14V", "PL-disV",
+};
+
+static const struct soc_enum pa_gain_enum[] = {
+	SOC_ENUM_SINGLE(SA51034_03_CONTROL, 6,
+	ARRAY_SIZE(pa_gain_select_texts), pa_gain_select_texts),
+};
+static const struct soc_enum power_limit_enum[] = {
+	SOC_ENUM_SINGLE(SA51034_03_CONTROL, 3,
+	ARRAY_SIZE(power_limit_select_texts), power_limit_select_texts),
+};
+	
+static const char * const reg_select[] = {
+	"read PA Reg 01:03",
+};
+
+static const struct soc_enum pa_enum2[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(reg_select),reg_select),
+};
+
+static int get_reg(
+	struct snd_kcontrol       *kcontrol,
+	struct snd_ctl_elem_value  *ucontrol)
+{
+	struct snd_soc_component *component; 
+	struct device *dev;    
+
+	
+
+	u32    currMode = ucontrol->value.enumerated.item[0];
+	int    i, ret;
+	int	   regs, rege;
+	unsigned int value;
+
+
+	if(g_sa51034 == NULL){
+	   pr_err("g_sa51034 null return %s\n", __func__);	  
+	   return -1;
+	}
+	dev = &g_sa51034->i2c->dev; 
+
+	component =  snd_soc_lookup_component(dev, NULL); 	
+	regs = 0x1;
+	rege = 0x4;
+
+	for (i = regs; i < rege; i++) {
+		value = snd_soc_component_read(component, i);
+		if (value < 0) {
+			pr_err("pa %s(%d),err value=%d\n", __func__, __LINE__, value);
+			return value;
+		}
+		pr_info("pa 2c_read Addr,Reg=(%x, %x)\n", i, value);
+	}
+	
+	return 0;
+}
+
+
+
+  int pa_get_enum_double(struct snd_kcontrol *kcontrol,
+	  struct snd_ctl_elem_value *ucontrol)
+  {
+	  //struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+  
+	  struct snd_soc_component *component; 
+	  struct device *dev;	 
+
+	  
+
+	  
+	  struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	  unsigned int val, item;
+	  unsigned int reg_val;
+	  int ret;
+	  if(g_sa51034 == NULL){
+ 	  	 pr_err("g_sa51034 null return %s\n", __func__);	
+ 		 return -1;
+	  }
+	  dev = &g_sa51034->i2c->dev; 
+
+	  
+	  component = snd_soc_lookup_component(dev, NULL);  
+	  reg_val = snd_soc_component_read(component, e->reg);
+
+
+	  if (reg_val < 0) {
+	  	  pr_err("pa %s(%d),err reg_val=%d\n", __func__, __LINE__, reg_val);
+		  return reg_val;
+	  }
+
+	  
+	  val = (reg_val >> e->shift_l) & e->mask;
+	  item = snd_soc_enum_val_to_item(e, val);
+	  ucontrol->value.enumerated.item[0] = item;
+	  if (e->shift_l != e->shift_r) {
+		  val = (reg_val >> e->shift_r) & e->mask;
+		  item = snd_soc_enum_val_to_item(e, val);
+		  ucontrol->value.enumerated.item[1] = item;
+	  }
+  
+	  return 0;
+  }
+
+  int pa_put_enum_double(struct snd_kcontrol *kcontrol,
+	  struct snd_ctl_elem_value *ucontrol)
+  {
+	  //struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	  
+	  struct snd_soc_component *component; 
+	  struct device *dev;	 
+	  struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	  unsigned int *item = ucontrol->value.enumerated.item;
+	  unsigned int val;
+	  unsigned int mask;
+
+	  if(g_sa51034 == NULL){
+ 	  	 pr_err("g_sa51034 null return %s\n", __func__);	
+ 		 return -1;
+	  }
+	  dev = &g_sa51034->i2c->dev; 
+	  component = snd_soc_lookup_component(dev, NULL);  
+  
+	  if (item[0] >= e->items)
+		  return -EINVAL;
+	  val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+	  mask = e->mask << e->shift_l;
+	  if (e->shift_l != e->shift_r) {
+		  if (item[1] >= e->items)
+			  return -EINVAL;
+		  val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
+		  mask |= e->mask << e->shift_r;
+	  }
+  
+	  return snd_soc_component_update_bits(component, e->reg, mask, val);
+  }
+
+
+static int pa_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	  int mute = 0,ret = 0;
+	  
+
+
+	  if(g_sa51034 == NULL){
+ 	  	 pr_err("g_sa51034 null return %s\n", __func__);	
+ 		 return -1;
+	  }	  
+	  mute = ucontrol->value.integer.value[0];
+	  ret = sa51034_set_mute(g_sa51034,mute);
+	  
+	  if(ret < 0)
+	  {
+		printk(KERN_ERR "sa51034_set_mute fail ret=%d,mute=%d\n",ret,mute);
+		return ret;
+	  }
+	  return 0;
+}
+
+static int pa_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{ 	
+	int mute = 0,ret = 0;
+	
+	if(g_sa51034 == NULL){
+		pr_err("g_sa51034 null return %s\n", __func__);    
+		return -1;
+	}
+	ret = sa51034_get_mute(g_sa51034,&mute);
+	
+	if(ret < 0)
+	{
+	  printk(KERN_ERR "sa51034_get_mute fail ret= %d\n",ret);
+	  return ret;
+	}
+	pr_info("[SA51034] %s mute gpio val=%d,integer.value[0]=%d\n", __func__, mute,ucontrol->value.integer.value[0]);
+
+	ucontrol->value.integer.value[0] = mute;
+
+	return 0;
+}
+
+
+
+
+
+const struct snd_kcontrol_new pa_controls[] =
+{
+	SOC_ENUM_EXT("PA gain", pa_gain_enum[0], pa_get_enum_double, pa_put_enum_double),
+    SOC_ENUM_EXT("Power limit", power_limit_enum[0], pa_get_enum_double, pa_put_enum_double),
+	SOC_ENUM_EXT("PA Reg Read", pa_enum2[0], get_reg, NULL),
+	SOC_SINGLE_EXT("pa mute", 0, 0, 1, 0,pa_GetMute, pa_SetMute),
+
+
+};
+	
+int pa_controls_size = sizeof(pa_controls) / sizeof(pa_controls[0]);
+
+
+
+
+static bool sa51034_volatile(struct device *dev, unsigned int reg)
+{
+	bool ret;
+
+#ifdef SA51034_DEBUG
+	ret = true;
+#else
+	ret = false;
+#endif
+
+	return ret;
+}
+
+static bool sa51034_readable(struct device *dev, unsigned int reg)
+{
+	if (reg <= SA51034_MAX_REGISTER)
+		return true;
+	else
+		return false;
+}
+
+static bool sa51034_writeable(struct device *dev, unsigned int reg)
+{
+	if (reg <= SA51034_MAX_REGISTER)
+		return true;
+	else
+		return false;
+}
+
+
+static const struct regmap_config sa51034_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = SA51034_MAX_REGISTER,
+	.volatile_reg = sa51034_volatile,
+	.writeable_reg = sa51034_writeable,
+	.readable_reg = sa51034_readable,
+
+	.reg_defaults = sa51034_reg,
+	.num_reg_defaults = ARRAY_SIZE(sa51034_reg),
+	.cache_type = REGCACHE_RBTREE,
+
+};
+
+static const struct snd_soc_component_driver pa_asoc_component = {
+	.name = "pa_component",
+
+
+	//.controls = pa_controls,
+	//.num_controls = ARRAY_SIZE(pa_controls),
+
+
+};
+
+static const struct of_device_id sa51034_i2c_dt_ids[] = {
+	{ .compatible = "sa51034"},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sa51034_i2c_dt_ids);
+static int sa51034_gpio_request(struct sa51034_priv *sa51034)
+{
+	struct device *dev;
+	struct device_node *np;
+    int ret;
+	dev = &(sa51034->i2c->dev);
+
+	np = dev->of_node;
+
+	if (!np)
+		return 0;
+
+	pr_info( "Read PDN pin from device tree\n");
+
+
+	sa51034->pwen_gpio = of_get_named_gpio(np, "sa51034,ctrl-gpio", 0);
+	if (sa51034->pwen_gpio < 0) {
+	    pr_err(  "sa51034 pwen pin of_get_named_gpio fail\n");
+		
+		sa51034->pwen_gpio = -1;
+		return -1;
+	}
+
+	if (!gpio_is_valid(sa51034->pwen_gpio)) {
+		pr_err(  "sa51034 pwen_gpio pin(%u) is invalid\n", sa51034->pwen_gpio);
+		sa51034->pwen_gpio = -1;
+		return -1;
+	}
+	sa51034->mute_gpio = of_get_named_gpio(np, "sa51034,ctrl-gpio", 1);
+	if (sa51034->mute_gpio < 0) {
+		
+	    pr_err(  "sa51034 mute_gpio pin of_get_named_gpio fail\n");
+		sa51034->mute_gpio = -1;
+		return -1;
+	}
+
+	if (!gpio_is_valid(sa51034->mute_gpio)) {
+		pr_err(  "sa51034 mute_gpio pin(%u) is invalid\n", sa51034->mute_gpio);
+		sa51034->mute_gpio = -1;
+		return -1;
+	}
+
+	
+	pr_info( "sa51034 get pwen_gpio pin(%u) mute_gpio pin(%u)\n", sa51034->pwen_gpio,sa51034->mute_gpio);
+
+	if (sa51034->pwen_gpio != -1) {
+		ret = devm_gpio_request(dev,sa51034->pwen_gpio, "sa51034 pwen");
+		if (ret < 0){
+			pr_err(  "sa51034 pwen_gpio request fail,ret=%d\n",ret);
+			return ret;			
+		}
+		pr_info("\t[sa51034] %s :pwen_gpio gpio_request ret = %d\n", __func__, ret);
+		gpio_direction_output(sa51034->pwen_gpio, 0);
+	}
+
+	
+	if (sa51034->mute_gpio != -1) {
+		ret = devm_gpio_request(dev,sa51034->mute_gpio, "sa51034 mute");
+		if (ret < 0){
+			pr_err(  "sa51034 mute_gpio request fail,ret=%d\n",ret);
+			return ret;			
+		}
+
+		pr_info("\t[AK4940] %s : mute_gpio gpio_request ret = %d\n", __func__, ret);
+		gpio_direction_output(sa51034->mute_gpio, 1);
+	}
+  
+	
+	return 0;
+}
+
+static int sa51034_set_mute(struct sa51034_priv *sa51034,int mute) 
+{
+	//struct snd_soc_component *component = dai->component;
+	//struct ak4940_priv *ak4940 = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+	//int ndt;
+
+	pr_info("[SA51034] %s mute=%d\n", __func__, mute);
+	if (sa51034->mute_gpio == -1) {
+			pr_err(  "sa51034 %s mute_gpio invalid return\n",__func__);
+			return -1;	
+	}
+
+	//ndt = 4080000 / sa51034->fs;
+	if (mute) {
+		/* SMUTE: 1 , MUTE */
+		ret = gpio_direction_output(sa51034->mute_gpio, 1);
+		//mdelay(ndt);
+	} else{
+		/* SMUTE:  0  ,NORMAL operation */
+		ret = gpio_direction_output(sa51034->mute_gpio, 0);
+		//mdelay(ndt);
+	}
+	return ret;
+}
+
+static int sa51034_get_mute(struct sa51034_priv *sa51034,int *mute) 
+{
+
+	int ret = 0;
+	if (sa51034->mute_gpio == -1) {
+			pr_err(  "sa51034 %s mute_gpio invalid return\n",__func__);
+			return -1;	
+	}
+	
+	*mute = gpio_get_value(sa51034->mute_gpio);
+	pr_info("[SA51034] %s mute gpio val=%d\n", __func__, *mute);
+	
+	return ret;
+}
+
+static int sa51034_set_pwen(struct sa51034_priv *sa51034,int en) 
+{
+	//struct snd_soc_component *component = dai->component;
+	//struct ak4940_priv *ak4940 = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+	//int ndt;
+
+	pr_info("\t[SA51034] %s en[%s]\n", __func__, en ? "ON":"OFF");
+	if (sa51034->pwen_gpio == -1) {
+			pr_err(  "sa51034 %s pwen_gpio invalid return\n",__func__);
+			return -1;	
+	}
+	//ndt = 4080000 / sa51034->fs;
+	if (en) {
+		/* SMUTE: 1 , MUTE */
+		ret = gpio_direction_output(sa51034->pwen_gpio, 1);
+		//mdelay(ndt);
+	} else{
+		/* SMUTE:  0  ,NORMAL operation */
+		ret = gpio_direction_output(sa51034->pwen_gpio, 0);
+		//mdelay(ndt);
+	}
+	return ret;
+}
+
+
+
+static int sa51034_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+	struct sa51034_priv *sa51034;
+	int ret = 0;
+	unsigned int val;
+
+	pr_info("\t[sa51034] %s(%d),i2c->addr=0x%x\n", __func__, __LINE__,i2c->addr);
+
+	sa51034 = devm_kzalloc(&i2c->dev, sizeof(struct sa51034_priv), GFP_KERNEL);
+	if (sa51034 == NULL)
+		return -ENOMEM;
+
+
+	sa51034->regmap = devm_regmap_init_i2c(i2c, &sa51034_regmap);
+
+	if (IS_ERR(sa51034->regmap)) {
+		devm_kfree(&i2c->dev, sa51034);
+		return PTR_ERR(sa51034->regmap);
+	}
+
+
+	i2c_set_clientdata(i2c, sa51034);
+	sa51034->i2c = i2c;
+	ret = devm_snd_soc_register_component(&i2c->dev, &pa_asoc_component,
+					      NULL, 0);
+	if (ret) {
+		pr_err( "pa component register failed,ret=%d\n",ret);
+		return ret;
+	}
+
+	pr_info("[sa51034] %s(%d) pa component register end,ret=0x%x\n", __func__, __LINE__,ret);
+
+	sa51034_gpio_request(sa51034);
+
+
+	sa51034_set_pwen(sa51034,1); 
+
+	//sa51034_set_mute(sa51034,0);
+
+	g_sa51034 = sa51034;
+
+	
+	pr_info("\t[sa51034] %s end\n", __func__);
+	return ret;
+}
+
+static const struct i2c_device_id sa51034_i2c_id[] = {
+
+	{ "sa51034", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, sa51034_i2c_id);
+
+static struct i2c_driver sa51034_i2c_driver = {
+	.driver = {
+		.name = "sa51034",
+		.of_match_table = of_match_ptr(sa51034_i2c_dt_ids),
+	},
+	.probe = sa51034_i2c_probe,
+	//.remove = sa51034_i2c_remove,
+	.id_table = sa51034_i2c_id,
+};
+
+static int  sa51034_init(void)
+{
+	pr_info("\t[sa51034] %s(%d)\n", __func__, __LINE__);
+
+	return i2c_add_driver(&sa51034_i2c_driver);
+}
+
+#endif
+static int zx29_audio_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device_node *np = pdev->dev.of_node;
+	struct snd_soc_card *card = &zx29_soc_card;
+	struct zx29_board_data *board;
+	const struct of_device_id *id;
+	enum of_gpio_flags flags;
+	unsigned int idx;
+
+	struct device *dev = &pdev->dev;
+	dev_info(&pdev->dev,"zx29_audio_probe start!\n");
+
+	card->dev = &pdev->dev;
+
+	board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
+	if (!board)
+		return -ENOMEM;
+
+	if (np) {
+		zx29_dai_link[0].cpus->dai_name = NULL;
+		zx29_dai_link[0].cpus->of_node = of_parse_phandle(np,
+				"zxic,i2s-controller", 0);
+		if (!zx29_dai_link[0].cpus->of_node) {
+			dev_err(&pdev->dev,
+			   "Property 'zxic,i2s-controller' missing or invalid\n");
+			ret = -EINVAL;
+		}
+
+		zx29_dai_link[0].platforms->name = NULL;
+		zx29_dai_link[0].platforms->of_node = zx29_dai_link[0].cpus->of_node;
+
+		
+#if 0
+		zx29_dai_link[0].codecs->of_node = of_parse_phandle(np,
+				"zxic,audio-codec", 0);
+		if (!zx29_dai_link[0].codecs->of_node) {
+			dev_err(&pdev->dev,
+				"Property 'zxic,audio-codec' missing or invalid\n");
+			return -EINVAL;
+		}
+#endif	
+	}
+	
+
+
+
+
+
+	id = of_match_device(of_match_ptr(zx29_ak4940_of_match), &pdev->dev);
+	if (id)
+		*board = *((struct zx29_board_data *)id->data);
+	
+	board->name = "zx29_ak4940";
+	board->dev = &pdev->dev;
+
+	//platform_set_drvdata(pdev, board);
+
+
+#if 0
+
+	board->gpio_pwen = of_get_gpio_flags(dev->of_node, 0, &flags);
+	if (!gpio_is_valid(board->gpio_pwen)) {
+		dev_err(dev,"  gpio_pwen no found\n");
+		return -EBUSY;
+	}
+	dev_info(dev, "board->gpio_pwen=0x%x  flags = %d\n",board->gpio_pwen,flags);
+	ret = devm_gpio_request(&pdev->dev,board->gpio_pwen, "codec_pwen");
+	if (ret < 0) {
+		dev_err(dev,"gpio_pwen request error.\n");
+		return ret;
+
+	}
+
+	board->gpio_pdn = of_get_gpio_flags(dev->of_node, 1, &flags);
+	if (!gpio_is_valid(board->gpio_pdn)) {
+		dev_err(dev,"  gpio_pdn no found\n");
+		return -EBUSY;
+	}
+	dev_info(dev, "board->gpio_pdn=0x%x  flags = %d\n",board->gpio_pdn,flags);
+	ret = devm_gpio_request(&pdev->dev,board->gpio_pdn, "codec_pdn");
+	if (ret < 0) {
+		dev_err(dev,"gpio_pdn request error.\n");
+		return ret;
+
+	}
+#endif
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+	if (ret){
+		dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+	    return ret;
+	}
+	zx29_i2s_top_pin_cfg(pdev);	
+
+	
+	//codec_power_on(board,1);
+#ifdef  CONFIG_PA_SA51034
+
+	dev_info(&pdev->dev,"zx29_audio_probe start sa51034_init!\n");
+
+	ret = sa51034_init();
+	if (ret != 0) {
+
+		pr_err("sa51034_init Failed to register I2C driver: %d\n", ret);
+		//return ret;
+
+	}
+	else{
+
+		for (idx = 0; idx < ARRAY_SIZE(pa_controls); idx++) {
+			ret = snd_ctl_add(card->snd_card,
+					  snd_ctl_new1(&pa_controls[idx],
+						       NULL));
+			if (ret < 0){
+				return ret;
+			}
+		}
+
+	}
+	 ret  = 0;
+
+#endif	
+	dev_info(&pdev->dev,"zx29_audio_probe end!\n");
+
+	return ret;
+}
+
+static struct platform_driver zx29_platform_driver = {
+	.driver		= {
+		.name	= "zx29_ak4940",
+		.of_match_table = of_match_ptr(zx29_ak4940_of_match),
+		.pm	= &snd_soc_pm_ops,
+	},
+	.probe		= zx29_audio_probe,
+	//.remove 	= zx29_remove,
+};
+
+
+#if 0
+static int zx29_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	print_audio("Alsa  zx297520xx SoC Audio driver\n");
+
+	zx29_platform_data = pdev->dev.platform_data;
+	if (zx29_platform_data == NULL) {
+		printk(KERN_ERR "Alsa  zx297520xx SoC Audio: unable to find platform data\n");
+		return -ENODEV;
+	}
+
+	if (zx297520xx_setup_pins(zx29_platform_data, "codec") < 0)
+		return -EBUSY;
+
+	zx29_i2s_top_reg_cfg();
+
+	zx29_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!zx29_snd_device) {
+		printk(KERN_ERR "Alsa  zx297520xx SoC Audio: Unable to register\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(zx29_snd_device, &zxic_soc_card);
+//	platform_device_add_data(zx29xx_ti3100_snd_device, &zx29xx_ti3100, sizeof(zx29xx_ti3100));
+	ret = platform_device_add(zx29_snd_device);
+	if (ret) {
+		printk(KERN_ERR "Alsa  zx29 SoC Audio: Unable to add\n");
+		platform_device_put(zx29_snd_device);
+	}
+
+	return ret;
+}
+#endif
+
+
+
+module_platform_driver(zx29_platform_driver);
+
+MODULE_DESCRIPTION("zx29 ALSA SoC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:zx29-audio-ak4940");
diff --git a/upstream/linux-5.10/sound/soc/sanechips/zx29_dummycodec.c b/upstream/linux-5.10/sound/soc/sanechips/zx29_dummycodec.c
new file mode 100755
index 0000000..1a8cf3e
--- /dev/null
+++ b/upstream/linux-5.10/sound/soc/sanechips/zx29_dummycodec.c
@@ -0,0 +1,1346 @@
+/*
+ * zx297520v3_es8312.c  --  zx298501-dummycodec ALSA SoC Audio board driver
+ *
+ * Copyright (C) 2022, ZTE Corporation.
+ *
+ * Based on smdk_wm8994.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+
+ 
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+//#include <sound/tlv.h>
+//#include <sound/soc.h>
+//#include <sound/jack.h>
+//#include <sound/zx29_snd_platform.h>
+//#include <mach/iomap.h>
+//#include <mach/board.h>
+#include <linux/of_gpio.h>
+
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+
+
+#include "i2s.h"
+
+#define ZX29_I2S_TOP_LOOP_REG	0x60
+
+
+#if 1
+ 
+#define  ZXIC_MCLK                    26000000
+#define  ZX29_AK4940_FREQ   26000000
+
+#define  ZXIC_PLL_CLKIN_MCLK		  0
+
+
+#define zx_reg_sync_write(v, a) \
+        do {    \
+            iowrite32(v, a);    \
+        } while (0)
+
+#define zx_read_reg(addr) \
+    ioread32(addr)
+
+#define zx_write_reg(addr, val)   \
+	zx_reg_sync_write(val, addr)
+
+
+
+struct zx29_board_data {
+	const char *name;
+	struct device *dev;
+
+	int codec_refclk;
+	int gpio_pwen;	
+	int gpio_pdn;
+	void __iomem *sys_base_va;	
+};
+
+//#define AON_WIFI_BT_CLK_CFG2  ((volatile unsigned int *)(ZX_TOP_CRM_BASE + 0x94))
+ /* Default ZX29s */
+static struct zx29_board_data zx29_platform_data = {
+	.codec_refclk = ZX29_AK4940_FREQ,
+};
+ static struct platform_device *zx29_snd_device;
+ 
+ static DEFINE_RAW_SPINLOCK(codec_pa_lock);
+ 
+ static int set_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol);
+ static int get_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol);
+
+
+#ifdef USE_ALSA_VOICE_FUNC
+ extern int zDrv_Audio_Printf(void *pFormat, ...);
+ extern int zDrvVp_GetVol_Wrap(void);
+ extern int zDrvVp_SetVol_Wrap(int volume);
+ extern int zDrvVp_GetPath_Wrap(void);
+ extern int zDrvVp_SetPath_Wrap(int path);
+ extern int zDrvVp_SetMute_Wrap(bool enable);
+ extern bool zDrvVp_GetMute_Wrap(void);
+ extern int zDrvVp_SetTone_Wrap(int toneNum);
+ 
+ static int vp_GetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetVol(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_GetVol(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_getTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ 
+ static int audio_GetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int audio_SetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+
+ 
+ //static const DECLARE_TLV_DB_SCALE(vp_path_tlv, 0, 300, 0);
+ 
+ static const char * const vpath_in_text[] = {
+	 "handset", "speak", "headset", "bluetooth",
+ };
+ 
+ static const char *tone_class[] = {
+	 "Lowpower", "Sms", "Callstd", "Alarm", "Calltime",
+ };
+ 
+ static const struct soc_enum vpath_in_enum =	 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(vpath_in_text), vpath_in_text); 
+ 
+ static const struct soc_enum tone_class_enum[] = {
+	 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tone_class), tone_class),
+ };
+ 
+ static const struct snd_kcontrol_new vp_snd_controls[] = {  
+	 SOC_ENUM_EXT("voice processing path select",vpath_in_enum,vp_GetPath,vp_SetPath),
+	 //SOC_SINGLE_EXT_TLV("voice processing path Volume",0, 5, 5, 0,vp_GetVol, vp_SetVol,vp_path_tlv), 
+	 SOC_SINGLE_EXT("voice processing path Volume",0, 5, 5, 0,vp_GetVol, vp_SetVol),
+	 SOC_SINGLE_EXT("voice uplink mute", 0, 1, 1, 0,vp_GetMute, vp_SetMute),
+	 SOC_ENUM_EXT("voice tone sel", tone_class_enum[0], vp_getTone, vp_SetTone),
+	 SOC_SINGLE_BOOL_EXT("path stauts dump", 0,get_path_stauts_switch, set_path_stauts_switch),
+	 SOC_ENUM_EXT("audio path select",vpath_in_enum,audio_GetPath,audio_SetPath),
+ };
+ 
+ static int curtonetype = 0;
+ static int vp_getTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 ucontrol->value.integer.value[0] = curtonetype;
+	 return 0;
+ }
+ 
+ static int vp_SetTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 int vol = 0,ret = 0, tonenum;
+	 tonenum = ucontrol->value.integer.value[0];
+	 curtonetype = tonenum;
+	 //printk("Alsa vp_SetTone tonenum=%d\n", tonenum);
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetTone_Wrap)(tonenum);
+	 if(ret < 0)
+	 {
+		 printk(KERN_ERR "vp_SetTone fail = %d\n", tonenum);
+		 return ret;
+	 }
+	 return 0;
+ }
+ 
+ static int vp_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 int enable = 0,ret = 0;
+	 enable = ucontrol->value.integer.value[0];
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetMute_Wrap)(enable);
+	 if(ret < 0)
+	 {
+	   printk(KERN_ERR "vp_SetMute fail = %d\n",enable);
+	   return ret;
+	 }
+	 return 0;
+ }
+ 
+ static int vp_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {		 
+		//ucontrol->value.integer.value[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetMute_Wrap)();
+		return 0;
+ }
+ 
+ static int vp_SetVol(struct snd_kcontrol *kcontrol,
+								struct snd_ctl_elem_value *ucontrol)
+ {
+		int vol = 0,ret = 0;
+		vol = ucontrol->value.integer.value[0];
+		//ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetVol_Wrap)(vol);
+		if(ret < 0)
+		{
+		   printk(KERN_ERR "vp_SetVol fail = %d\n",vol);
+		   return ret;
+	   }
+	 return 0;
+ }
+ static int vp_GetVol(struct snd_kcontrol *kcontrol,
+								struct snd_ctl_elem_value *ucontrol)
+ {		 
+		//ucontrol->value.integer.value[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetVol_Wrap)();
+		return 0;
+ }
+ static int vp_GetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {	 
+	 //ucontrol->value.enumerated.item[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetPath_Wrap)();
+	 return 0;
+ }
+ static int vp_SetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {
+	 int ret = 0,path = 0;
+	 unsigned long	flags;
+	 path = ucontrol->value.enumerated.item[0];
+ 
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetPath_Wrap)(path);
+	 if(ret < 0)
+	 {
+	   printk(KERN_ERR "vp_SetPath fail = %d\n",path);
+	   return ret;
+	 }
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+	 switch (path) {
+	 case 0:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+		 break;
+	 case 1:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_40, GPIO_HIGH);
+		 break;
+	 case 2:
+		 break;
+	 case 3:
+		 break;
+	 default:
+		 break;
+	 }
+#endif
+	 return 0;
+ }
+ 
+ static int curpath = 0;
+ static int audio_GetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {	 
+	 ucontrol->value.enumerated.item[0] = curpath;
+	 return 0;
+ }
+ 
+ static int audio_SetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {
+	 int ret = 0,path = 0;
+	 unsigned long	flags;
+	 
+	 path = ucontrol->value.enumerated.item[0];
+	 curpath = path;
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+	 switch (path) {
+	 case 0:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+		 break;
+	 case 1:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_40, GPIO_HIGH);
+		 break;
+	 case 2:
+		 break;
+	 case 3:
+		 break;
+	 default:
+		 break;
+	 }
+#endif
+	 return 0;
+ }
+ 
+ typedef enum
+ {
+	 VP_PATH_HANDSET	=0, 	
+	 VP_PATH_SPEAKER,		 
+	 VP_PATH_HEADSET,					  
+	 VP_PATH_BLUETOOTH, 				   
+	 VP_PATH_BLUETOOTH_NO_NR,					 
+	 VP_PATH_HSANDSPK,
+	 
+	 VP_PATH_OFF = 255, 				 
+	 
+	 MAX_VP_PATH = VP_PATH_OFF				 
+ }T_ZDrv_VpPath;
+ 
+ extern int zDrvVp_Loop(T_ZDrv_VpPath path);
+
+ 
+//#else
+ static const struct snd_kcontrol_new machine_snd_controls[] = {		 
+	 SOC_SINGLE_BOOL_EXT("path stauts dump", 0,get_path_stauts_switch, set_path_stauts_switch),
+ };
+ 
+
+ 
+ //extern int rt5670_hs_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+ 
+ int path_stauts_switch = 0;
+ static int set_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+ {
+	 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	 struct snd_soc_dapm_path *p;
+ 
+	 int path_stauts_switch = ucontrol->value.integer.value[0];
+ 
+	 
+	 if (path_stauts_switch == 1)
+	 {
+		 list_for_each_entry(p, &card->paths, list){
+			 
+		   //print_audio("Alsa	path name (%s),longname (%s),sink (%s),source (%s),connect %d \n", p->name,p->long_name,p->sink->name,p->source->name,p->connect);
+		   //printk("Alsa  path longname %s,sink %s,source %s,connect %d \n", p->long_name,p->sink->name,p->source->name,p->connect);
+ 
+		 }
+	 }
+	 return 0;
+ }
+ 
+ static int get_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+ {
+	 
+	 ucontrol->value.integer.value[0] = path_stauts_switch;
+	 return 0;
+ };
+#endif 
+ 
+#ifdef CONFIG_SND_SOC_JACK_DECTEC
+ 
+ static struct snd_soc_jack codec_headset;
+ 
+ /* Headset jack detection DAPM pins */
+ static struct snd_soc_jack_pin codec_headset_pins[] = {
+	 {
+		 .pin = "Headphone",
+		 .mask = SND_JACK_HEADPHONE,
+	 },
+ };
+ 
+#endif
+ 
+ static int zx29startup(struct snd_pcm_substream *substream)
+ {
+ //  int ret = 0;
+	 print_audio("Alsa	Entered func %s\n", __func__);
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx29_startup device=%d,stream=%d\n", substream->pcm->device, substream->stream);
+ 
+	 struct snd_pcm *pcmC0D0p = snd_lookup_minor_data(16, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+	 struct snd_pcm *pcmC0D1p = snd_lookup_minor_data(17, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+	 struct snd_pcm *pcmC0D2p = snd_lookup_minor_data(18, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);	 
+	 struct snd_pcm *pcmC0D3p = snd_lookup_minor_data(19, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);	
+	 if ((pcmC0D0p == NULL) || (pcmC0D1p == NULL) || (pcmC0D2p == NULL) || (pcmC0D3p == NULL))
+		 return  -EINVAL;	  
+	 if ((pcmC0D0p->streams[0].substream_opened && pcmC0D1p->streams[0].substream_opened) || 
+		 (pcmC0D0p->streams[0].substream_opened && pcmC0D2p->streams[0].substream_opened) || 
+		 (pcmC0D0p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened) || 
+		 (pcmC0D1p->streams[0].substream_opened && pcmC0D2p->streams[0].substream_opened) ||
+		 (pcmC0D1p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened) ||
+		 (pcmC0D2p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened))
+		 BUG();
+#if 0
+	 unsigned long	flags;
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 mdelay(1);  
+ 
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+	 }
+#endif
+ 
+	 unsigned int  armRegBit = 0;
+	 //armRegBit = zx_read_reg(AON_WIFI_BT_CLK_CFG2);
+	 //armRegBit &= 0xfffffffe;
+	 //armRegBit |= 0x1;
+	 //zx_write_reg(AON_WIFI_BT_CLK_CFG2, armRegBit);
+	 
+	 return 0;
+ }
+ 
+ static void zx29_shutdown(struct snd_pcm_substream *substream)
+ {
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx297520xx_shutdown device=%d, stream=%d\n", substream->pcm->device, substream->stream);
+ //  print_audio("Alsa	Entered func %s, stream=%d\n", __func__, substream->stream);
+ 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+#endif
+	 }
+	 
+	 if (snd_soc_dai_active(cpu_dai))
+		 return;
+ 
+	 u32 armRegBit;
+	 //armRegBit = zx_read_reg(AON_WIFI_BT_CLK_CFG2);
+	 //armRegBit &= 0xfffffffe;
+	 //armRegBit |= 0x0;
+	 //zx_write_reg(AON_WIFI_BT_CLK_CFG2, armRegBit);
+ 
+ }
+ 
+ static void zx29_shutdown2(struct snd_pcm_substream *substream)
+ {
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx29_shutdown2 device=%d, stream=%d\n", substream->pcm->device, substream->stream);
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+#endif
+#ifdef USE_ALSA_VOICE_FUNC
+		 //CPPS_FUNC(cpps_callbacks, zDrvVp_Loop)(VP_PATH_OFF);
+#endif
+	 }
+ 
+	 if (snd_soc_dai_active(cpu_dai))
+		 return;
+ 
+	 u32 armRegBit;
+	 //armRegBit = zx_read_reg(AON_WIFI_BT_CLK_CFG2);
+	 //armRegBit &= 0xfffffffe;
+	 //armRegBit |= 0x0;
+	 //zx_write_reg(AON_WIFI_BT_CLK_CFG2, armRegBit);
+ }
+ static int zx29_init_paiftx(struct snd_soc_pcm_runtime *rtd)
+ {
+	 //struct snd_soc_codec *codec = rtd->codec;
+	 //struct snd_soc_dapm_context *dapm = &codec->dapm;
+ 
+	 //snd_soc_dapm_enable_pin(dapm, "HPOL");
+	 //snd_soc_dapm_enable_pin(dapm, "HPOR");
+ 
+	 /* Other pins NC */
+ //  snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
+ 
+ //  print_audio("Alsa	Entered func %s\n", __func__);
+ 
+	 return 0;
+ }
+ static int zx29_hw_params(struct snd_pcm_substream *substream,
+										struct snd_pcm_hw_params *params)
+ {
+     print_audio("Alsa:	Entered func %s\n", __func__);
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	 int ret;
+	 int rfs = 0, frq_out = 0;	 
+	 switch (params_rate(params)) {
+	 case 8000:
+	 case 16000:
+	 case 11025:
+	 case 22050:
+	 case 24000:
+	 case 32000:
+	 case 44100:
+	 case 48000:
+		 rfs = 32;
+		 break;
+	 default:
+	 	{
+	 	    ret =  -EINVAL;
+		    print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret);	 	      
+		 	return ret;
+	 	}
+	 }
+	 
+	 frq_out = params_rate(params) * rfs * 2;
+	 
+	 /* Set the Codec DAI configuration */
+	 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+
+
+	 /* Set the AP DAI configuration */
+	 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: ap dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+ 
+	 /* Set the Codec DAI clk */	 
+	 /*ret =snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_BCLK1,
+								  fs*datawidth*2, 256*fs);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		 return ret;
+	}
+	 */
+	 
+	 //ret = snd_soc_dai_set_sysclk(codec_dai, AK4940_CLKID_BCLK,ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	 //if (ret < 0){	 	
+	 //	 print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+	 //	 return ret;
+	 // }
+	 
+	 /* Set the AP DAI clk */
+	 ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_122M88, SND_SOC_CLOCK_IN);
+	 //ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+ 
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: cpu dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+     print_audio("Alsa:	Entered func %s end\n", __func__);
+	 
+	 return 0;
+ }
+
+static int zx29_hw_params_lp(struct snd_pcm_substream *substream,
+									   struct snd_pcm_hw_params *params)
+{
+	print_audio("Alsa: Entered func %s\n", __func__);
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	int ret;
+	int rfs = 0, frq_out = 0;	
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+	case 11025:
+	case 22050:
+	case 24000:
+	case 32000:
+	case 44100:
+	case 48000:
+		rfs = 32;
+		break;
+	default:
+	   {
+		   ret =  -EINVAL;
+		   print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret); 			 
+		   return ret;
+	   }
+	}
+	
+	frq_out = params_rate(params) * rfs * 2;
+	
+	/* Set the Codec DAI configuration */
+	/*
+	
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							  | SND_SOC_DAIFMT_NB_NF
+							  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0){
+	   
+		print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		return ret;
+	}
+	*/ 
+
+	
+	/* Set the AP DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+							  | SND_SOC_DAIFMT_NB_NF
+							  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0){
+	   
+		print_audio("Alsa: ap dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		return ret;
+	}
+
+	/* Set the Codec DAI clk */ 	
+	/*ret =snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_BCLK1,
+								 fs*datawidth*2, 256*fs);
+	if (ret < 0){
+	   
+		print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		return ret;
+   }
+	*/
+	/*
+	ret = snd_soc_dai_set_sysclk(codec_dai, ES8312_CLKID_MCLK,ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	if (ret < 0){	   
+		print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		return ret;
+	}
+	*/
+	/* Set the AP DAI clk */
+	ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+
+	if (ret < 0){	   
+		print_audio("Alsa: cpu dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		return ret;
+	}
+	print_audio("Alsa: Entered func %s end\n", __func__);
+	
+	return 0;
+}
+
+
+ 
+
+ 
+
+ static int zx29_hw_params_voice(struct snd_pcm_substream *substream,
+										struct snd_pcm_hw_params *params)
+ {
+	 print_audio("Alsa: Entered func %s\n", __func__);
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	 int ret;
+	 int rfs = 0, frq_out = 0;	 
+	 switch (params_rate(params)) {
+	 case 8000:
+	 case 16000:
+	 case 11025:
+	 case 22050:
+	 case 24000:
+	 case 32000:
+	 case 44100:
+	 case 48000:
+		 rfs = 32;
+		 break;
+	 default:
+		{
+			ret =  -EINVAL;
+			print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret);			  
+			return ret;
+		}
+	 }
+	 
+	 frq_out = params_rate(params) * rfs * 2;
+	 
+	 /* Set the Codec DAI configuration */
+	 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+		
+		 print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+ 
+ 
+
+	 /* Set the Codec DAI clk */	 
+	 /*ret =snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_BCLK1,
+								  fs*datawidth*2, 256*fs);
+	 if (ret < 0){
+		
+		 print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		 return ret;
+	}
+	
+	 
+	 ret = snd_soc_dai_set_sysclk(codec_dai, AK4940_CLKID_BCLK,ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	 if (ret < 0){		
+		 print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+	 
+	 */
+
+	 print_audio("Alsa: Entered func %s end\n", __func__);
+	 
+	 return 0;
+ }
+
+										 
+ int zx29_prepare2(struct snd_pcm_substream *substream)
+ {
+	 int path, ret;
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_Loop)(VP_PATH_SPEAKER);
+		 if (ret < 0)
+			 return -1;
+	 }
+	 
+	 return 0;
+ } 
+ static void zx29_i2s_top_reg_cfg(void)
+ {
+	 unsigned int i2s_top_reg;
+	 int ret = 0;
+ 
+#ifdef CONFIG_USE_PIN_I2S0
+	 ret = gpio_request(PIN_I2S0_WS, "i2s0_ws");
+	 if (ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S0_CLK, "i2s0_clk");
+	 if (ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S0_DIN, "i2s0_din");
+	 if (ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S0_DOUT, "i2s0_dout");
+	 if (ret < 0)
+		 BUG();
+	 zx29_gpio_config(PIN_I2S0_WS, FUN_I2S0_WS);
+	 zx29_gpio_config(PIN_I2S0_CLK, FUN_I2S0_CLK);
+	 zx29_gpio_config(PIN_I2S0_DIN, FUN_I2S0_DIN);
+	 zx29_gpio_config(PIN_I2S0_DOUT, FUN_I2S0_DOUT);
+	 
+	 //top i2s1 cfg
+	 i2s_top_reg = zx_read_reg(ZX29_I2S_LOOP_CFG);
+	 i2s_top_reg &= 0xfffffff8;
+	 i2s_top_reg |= 0x00000001; //	inter arm_i2s1--top i2s1
+	 zx_write_reg(ZX29_I2S_LOOP_CFG, i2s_top_reg);
+#elif defined (CONFIG_USE_PIN_I2S1)
+	
+
+	 ret = gpio_request(PIN_I2S1_WS,"i2s1_ws");
+	 if(ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S1_CLK,"i2s1_clk");
+	 if(ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S1_DIN,"i2s1_din");
+	 if(ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S1_DOUT,"i2s1_dout");
+	 if(ret < 0)
+		 BUG();
+	 zx29_gpio_config(PIN_I2S1_WS, FUN_I2S1_WS);
+	 zx29_gpio_config(PIN_I2S1_CLK, FUN_I2S1_CLK);
+	 zx29_gpio_config(PIN_I2S1_DIN, FUN_I2S1_DIN);
+	 zx29_gpio_config(PIN_I2S1_DOUT, FUN_I2S1_DOUT);
+		 
+	 //top i2s2 cfg
+	 i2s_top_reg = zx_read_reg(ZX29_I2S_LOOP_CFG);
+	 i2s_top_reg &= 0xfff8ffff;
+	 i2s_top_reg |= 0x00010000; //	inter arm_i2s1--top i2s2
+	 zx_write_reg(ZX29_I2S_LOOP_CFG, i2s_top_reg);
+#endif
+ 
+	 // inter loop
+	 //i2s_top_reg = zx_read_reg(ZX29_I2S_LOOP_CFG);
+	 //i2s_top_reg &= 0xfffffe07;
+	 //i2s_top_reg |= 0x000000a8; //	inter arm_i2s2--afe i2s
+	 //zx_write_reg(ZX29_I2S_LOOP_CFG, i2s_top_reg);
+	 
+ //  print_audio("Alsa %s i2s loop cfg reg=%x\n",__func__, zx_read_reg(ZX29_I2S_LOOP_CFG));  
+ }
+ 
+ static int zx29_late_probe(struct snd_soc_card *card)
+ {
+	 //struct snd_soc_codec *codec = card->rtd[0].codec;
+	 //struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+	 int ret;
+ //  print_audio("Alsa	zx29_late_probe entry!\n");
+ 
+#ifdef CONFIG_SND_SOC_JACK_DECTEC
+	 
+	 ret = snd_soc_jack_new(codec, "Headset",
+							SND_JACK_HEADSET |SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
+							&codec_headset);
+	 if (ret)
+		 return ret;
+ 
+	 ret = snd_soc_jack_add_pins(&codec_headset,
+								 ARRAY_SIZE(codec_headset_pins),
+								 codec_headset_pins);
+	 if (ret)
+		 return ret;
+       #ifdef CONFIG_SND_SOC_codec
+	 //rt5670_hs_detect(codec, &codec_headset);
+       #endif
+#endif
+ 
+	 return 0;
+ }
+ 
+ static struct snd_soc_ops zx29_ops = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 .hw_params = zx29_hw_params,
+ };
+  static struct snd_soc_ops zx29_ops_lp = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 .hw_params = zx29_hw_params_lp,
+ };
+ static struct snd_soc_ops zx29_ops1 = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 //.hw_params = zx29_hw_params1,
+ };
+ 
+ static struct snd_soc_ops zx29_ops2 = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown2,
+	 //.hw_params = zx29_hw_params1,
+	 .prepare = zx29_prepare2,
+ };
+ static struct snd_soc_ops voice_ops = {
+	 //.startup = zx29_startup,
+	 //.shutdown = zx29_shutdown2,
+	 .hw_params = zx29_hw_params_voice,
+	 //.prepare = zx29_prepare2,
+ };
+
+ 
+ enum {
+	 MERR_DPCM_AUDIO = 0,
+	 MERR_DPCM_DEEP_BUFFER,
+	 MERR_DPCM_COMPR,
+ };
+
+ 
+#if 0
+ 
+ static struct snd_soc_card zxic_soc_card = {
+	 .name = "zx298501_ak4940",
+	 .owner = THIS_MODULE,
+	 .dai_link = &zxic_dai_link,
+	 .num_links = ARRAY_SIZE(zxic_dai_link),
+#ifdef USE_ALSA_VOICE_FUNC
+	 .controls = vp_snd_controls,
+	 .num_controls = ARRAY_SIZE(vp_snd_controls),
+#endif
+ 
+ //  .late_probe = zx29_late_probe,
+	 
+ };
+#endif 
+ //static struct zx298501_ak4940_pdata *zx29_platform_data;
+ 
+ static int zx29_setup_pins(struct zx29_board_data *codec_pins, char *fun)
+ {
+	 int ret;
+ 
+	 //ret = gpio_request(codec_pins->codec_refclk, "codec_refclk");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio: %s pin already in use\n", fun);
+		 return ret;
+	 }
+	 //zx29_gpio_config(codec_pins->codec_refclk, GPIO17_CLK_OUT2);
+ 
+#ifdef  _USE_7520V3_PHONE_TYPE_C31F
+	 ret = gpio_request_one(ZX29_GPIO_39, GPIOF_OUT_INIT_LOW, "codec_pa");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio:  codec_pa in use\n");
+		 return ret;
+	 }
+	 
+	 ret = gpio_request_one(ZX29_GPIO_40, GPIOF_OUT_INIT_LOW, "codec_sw");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio:  codec_sw in use\n");
+		 return ret;
+	 }
+#endif
+ 
+	 return 0;
+ }
+#endif
+
+ 
+ static int zx29_remove(struct platform_device *pdev)
+ {
+	 gpio_free(zx29_platform_data.codec_refclk);
+	 platform_device_unregister(zx29_snd_device);
+	 return 0;
+ }
+ 
+
+ 
+#if  0
+
+ /*
+  * Default CFG switch settings to use this driver:
+  *	ZX29
+  */
+
+ /*
+  * Configure audio route as :-
+  * $ amixer sset 'DAC1' on,on
+  * $ amixer sset 'Right Headphone Mux' 'DAC'
+  * $ amixer sset 'Left Headphone Mux' 'DAC'
+  * $ amixer sset 'DAC1R Mixer AIF1.1' on
+  * $ amixer sset 'DAC1L Mixer AIF1.1' on
+  * $ amixer sset 'IN2L' on
+  * $ amixer sset 'IN2L PGA IN2LN' on
+  * $ amixer sset 'MIXINL IN2L' on
+  * $ amixer sset 'AIF1ADC1L Mixer ADC/DMIC' on
+  * $ amixer sset 'IN2R' on
+  * $ amixer sset 'IN2R PGA IN2RN' on
+  * $ amixer sset 'MIXINR IN2R' on
+  * $ amixer sset 'AIF1ADC1R Mixer ADC/DMIC' on
+  */
+
+/* ZX29 has a 16.934MHZ crystal attached to ak4940 */
+#define ZX29_AK4940_FREQ 16934000
+
+
+
+
+
+static int zx29_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	unsigned int pll_out;
+	int ret;
+
+	/* AIF1CLK should be >=3MHz for optimal performance */
+	if (params_width(params) == 24)
+		pll_out = params_rate(params) * 384;
+	else if (params_rate(params) == 8000 || params_rate(params) == 11025)
+		pll_out = params_rate(params) * 512;
+	else
+		pll_out = params_rate(params) * 256;
+
+	ret = snd_soc_dai_set_pll(codec_dai, AK4940_FLL1, AK4940_FLL_SRC_MCLK1,
+					ZX29_AK4940_FREQ, pll_out);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, AK4940_SYSCLK_FLL1,
+					pll_out, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * ZX29 AK4940 DAI operations.
+ */
+static struct snd_soc_ops zx29_ops = {
+	.hw_params = smdk_hw_params,
+};
+
+static int zx29_ak4940_init_paiftx(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
+
+	/* Other pins NC */
+	snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
+	snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTLN");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTLP");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTRP");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTRN");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
+	snd_soc_dapm_nc_pin(dapm, "IN1LP");
+	snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
+	snd_soc_dapm_nc_pin(dapm, "IN1RP");
+	snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
+
+	return 0;
+}
+#endif
+
+
+
+
+enum {
+	AUDIO_DL_MEDIA = 0,
+	AUDIO_DL_VOICE,
+	AUDIO_DL_2G_AND_3G_VOICE,
+	AUDIO_DL_VP_LOOP,	
+	AUDIO_DL_3G_VOICE,
+	
+	AUDIO_DL_MAX,
+};
+SND_SOC_DAILINK_DEF(dummy, \
+	DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+//SND_SOC_DAILINK_DEF(cpu_i2s0, \
+//	DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+SND_SOC_DAILINK_DEF(cpu_i2s0, \
+	DAILINK_COMP_ARRAY(COMP_CPU("E1D02000.i2s")));
+
+
+SND_SOC_DAILINK_DEF(voice_cpu, \
+	DAILINK_COMP_ARRAY(COMP_CPU("soc:voice_audio")));
+
+SND_SOC_DAILINK_DEF(voice_2g_3g, \
+	DAILINK_COMP_ARRAY(COMP_CPU("voice_2g_3g-dai")));
+
+SND_SOC_DAILINK_DEF(voice_3g, \
+		DAILINK_COMP_ARRAY(COMP_CPU("voice_3g-dai")));
+
+
+
+//SND_SOC_DAILINK_DEF(ak4940, \
+//	DAILINK_COMP_ARRAY(COMP_CODEC("ak4940.1-0012", "ak4940-aif")));
+SND_SOC_DAILINK_DEF(dummy_cpu, \
+		DAILINK_COMP_ARRAY(COMP_CPU("soc:zx29_snd_dummy")));
+//SND_SOC_DAILINK_DEF(dummy_platform, \
+//	DAILINK_COMP_ARRAY(COMP_PLATFORM("soc:zx29_snd_dummy")));
+
+SND_SOC_DAILINK_DEF(dummy_codec, \
+		DAILINK_COMP_ARRAY(COMP_CODEC("soc:zx29_snd_dummy", "zx29_snd_dummy_dai")));
+SND_SOC_DAILINK_DEF(ti3100_codec, \
+		DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic31xx-codec.1-0012", "tlv320aic31xx-hifi")));
+
+
+//SND_SOC_DAILINK_DEF(media_platform, \
+//	DAILINK_COMP_ARRAY(COMP_PLATFORM("zx29-pcm-audio")));
+SND_SOC_DAILINK_DEF(media_platform, \
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("E1D02000.i2s")));
+//SND_SOC_DAILINK_DEF(voice_cpu, \
+//	DAILINK_COMP_ARRAY(COMP_CPU("E1D02000.i2s")));
+
+SND_SOC_DAILINK_DEF(voice_platform, \
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("soc:voice_audio")));
+
+			
+//static struct snd_soc_dai_link zx29_dai_link[] = {
+struct snd_soc_dai_link zx29_dai_link[] = {
+
+ 
+
+
+ {
+	.name = "zx29_snd_dummy",//codec name
+	.stream_name = "zx29_snd_dumy",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &zx29_ops_lp,
+	.init = zx29_init_paiftx,
+	SND_SOC_DAILINK_REG(cpu_i2s0, dummy_codec, media_platform),
+	
+},
+{
+	.name = "media",//codec name
+	.stream_name = "MultiMedia",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &zx29_ops,
+
+ 	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(cpu_i2s0, dummy_codec, media_platform),
+
+},
+{
+	.name = "voice",//codec name
+	.stream_name = "voice",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	//.init = zx29_init_paiftx,
+	
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, dummy_codec, voice_platform),
+
+},
+{
+	.name = "voice_2g3g_teak",//codec name
+	.stream_name = "voice_2g3g_teak",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	//.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, dummy_codec, voice_platform),
+
+},
+
+{
+	.name = "voice_3g",//codec name
+	.stream_name = "voice_3g",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	//.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, dummy_codec, voice_platform),
+
+},
+
+{
+	.name = "loop_test",//codec name
+	.stream_name = "loop_test",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &zx29_ops,
+
+	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(cpu_i2s0, dummy_codec, dummy),
+
+},
+
+};
+
+
+
+static struct snd_soc_card zx29_soc_card = {
+	.name = "zx29-sound-card",
+	.owner = THIS_MODULE,
+	.dai_link = zx29_dai_link,
+	.num_links = ARRAY_SIZE(zx29_dai_link),
+#ifdef USE_ALSA_VOICE_FUNC
+	 .controls = vp_snd_controls,
+	 .num_controls = ARRAY_SIZE(vp_snd_controls),
+#endif	
+};
+
+static const struct of_device_id zx29_dummycodec_of_match[] = {
+	{ .compatible = "zxic,zx29_dummycodec", .data = &zx29_platform_data },
+	{},
+};
+MODULE_DEVICE_TABLE(of, zx29_dummycodec_of_match);
+
+static void zx29_i2s_top_pin_cfg(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct pinctrl *p;
+	struct pinctrl_state *s;
+	int ret = 0;
+
+
+	struct resource *res;
+	void __iomem	*reg_base;
+	unsigned int val;
+
+
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "soc_sys");
+	if (!res) {
+		dev_err(dev, "Reg region missing (%s)\n", "soc_sys");
+		//return -ENXIO;
+	}
+
+	#if 0
+	reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(reg_base )) {
+			dev_err(dev, "Reg region ioremap (%s) err=%li\n", "soc_sys",PTR_ERR(reg_base ));
+		//return PTR_ERR(reg_base );
+	}
+
+	#else
+	reg_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	#endif
+	 
+//#if 1 //CONFIG_USE_PIN_I2S0
+#ifdef 	CONFIG_USE_TOP_I2S0
+
+	dev_info(dev, "%s: arm i2s1 to top i2s0!!\n", __func__); 
+	//9300
+		 
+	//top i2s1 cfg
+	val = zx_read_reg(reg_base+ZX29_I2S_TOP_LOOP_REG);
+	val &= ~(0x7<<0);
+	val |= 0x1<<0; //	inter arm_i2s1--top i2s1
+	zx_write_reg(reg_base+ZX29_I2S_TOP_LOOP_REG, val);
+#else //(CONFIG_USE_PIN_I2S1)
+    //8501evb    	
+
+	dev_info(dev, "%s: arm i2s1 to top i2s1!\n", __func__); 
+			 
+	//top i2s2 cfg
+	val = zx_read_reg(reg_base+ZX29_I2S_TOP_LOOP_REG);
+	//val &= 0xfffffff8;
+	val &= ~(0x7<<16);	
+	val |= 0x1<<16;//	inter arm_i2s1--top i2s2
+	zx_write_reg(reg_base+ZX29_I2S_TOP_LOOP_REG, val);
+#endif
+
+
+
+	p = devm_pinctrl_get(dev);
+	if (IS_ERR(p)) {
+		dev_err(dev, "%s: pinctrl get failure ,p=0x%llx,dev=0x%llx!!\n", __func__,p,dev);
+		return;
+	}
+	
+	dev_info(dev, "%s: get pinctrl ,p=0x%llx,dev=0x%llx!!\n", __func__,p,dev); 
+
+	s = pinctrl_lookup_state(p, "top_i2s");
+	if (IS_ERR(s)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}
+	ret = pinctrl_select_state(p, s);
+	if (ret < 0) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " select state failure!!\n");
+		return;
+	}
+	dev_info(dev, "%s: set pinctrl end!\n", __func__);	
+
+	
+
+ 
+}
+
+
+static int zx29_audio_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device_node *np = pdev->dev.of_node;
+	struct snd_soc_card *card = &zx29_soc_card;
+	struct zx29_board_data *board;
+	const struct of_device_id *id;
+	enum of_gpio_flags flags;
+	unsigned int idx;
+
+	struct device *dev = &pdev->dev;
+	dev_info(&pdev->dev,"zx29_audio_probe start!\n");
+
+	card->dev = &pdev->dev;
+
+	board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
+	if (!board)
+		return -ENOMEM;
+	board->name = "zx29_dummycodec";
+	board->dev = &pdev->dev;
+
+	if (np) {
+		zx29_dai_link[0].cpus->dai_name = NULL;
+		zx29_dai_link[0].cpus->of_node = of_parse_phandle(np,
+				"zxic,i2s-controller", 0);
+		if (!zx29_dai_link[0].cpus->of_node) {
+			dev_err(&pdev->dev,
+			   "Property 'zxic,i2s-controller' missing or invalid\n");
+			ret = -EINVAL;
+		}
+
+		zx29_dai_link[0].platforms->name = NULL;
+		zx29_dai_link[0].platforms->of_node = zx29_dai_link[0].cpus->of_node;
+
+		
+#if 0
+		zx29_dai_link[0].codecs->of_node = of_parse_phandle(np,
+				"zxic,audio-codec", 0);
+		if (!zx29_dai_link[0].codecs->of_node) {
+			dev_err(&pdev->dev,
+				"Property 'zxic,audio-codec' missing or invalid\n");
+			return -EINVAL;
+		}
+#endif	
+	}
+	
+
+
+
+
+
+	id = of_match_device(of_match_ptr(zx29_dummycodec_of_match), &pdev->dev);
+	if (id)
+		*board = *((struct zx29_board_data *)id->data);
+
+	//platform_set_drvdata(pdev, board);
+
+
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+	if (ret){
+		dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+	    return ret;
+	}
+	zx29_i2s_top_pin_cfg(pdev);	
+
+	
+	//codec_power_on(board,1);
+	dev_info(&pdev->dev,"zx29_audio_probe end!\n");
+
+	return ret;
+}
+
+static struct platform_driver zx29_platform_driver = {
+	.driver		= {
+		.name	= "zx29_dummycodec",
+		.of_match_table = of_match_ptr(zx29_dummycodec_of_match),
+		.pm	= &snd_soc_pm_ops,
+	},
+	.probe		= zx29_audio_probe,
+	//.remove 	= zx29_remove,
+};
+
+
+
+
+
+module_platform_driver(zx29_platform_driver);
+
+MODULE_DESCRIPTION("zx29 ALSA SoC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:zx29-audio-dummycodec");
diff --git a/upstream/linux-5.10/sound/soc/sanechips/zx29_max9867.c b/upstream/linux-5.10/sound/soc/sanechips/zx29_max9867.c
new file mode 100755
index 0000000..ea874ee
--- /dev/null
+++ b/upstream/linux-5.10/sound/soc/sanechips/zx29_max9867.c
@@ -0,0 +1,2012 @@
+/*
+ * zx297520v3_es8312.c  --  zx298501-ti3100 ALSA SoC Audio board driver
+ *
+ * Copyright (C) 2022, ZTE Corporation.
+ *
+ * Based on smdk_wm8994.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "../codecs/max9867.h"
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+
+ 
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+//#include <sound/tlv.h>
+//#include <sound/soc.h>
+//#include <sound/jack.h>
+//#include <sound/zx29_snd_platform.h>
+//#include <mach/iomap.h>
+//#include <mach/board.h>
+#include <linux/of_gpio.h>
+
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+
+
+#include "i2s.h"
+
+#define ZX29_I2S_TOP_LOOP_REG	0x60
+#define CODEC_CLK_ID 0
+#define CODEC_SCLK_MCLK_ID  0
+#define CODEC_SCLK_PLL_ID  1
+
+#if 1
+ 
+#define  ZXIC_MCLK                    26000000
+
+#define  ZXIC_PLL_CLKIN_MCLK		  0
+
+
+#define zx_reg_sync_write(v, a) \
+        do {    \
+            iowrite32(v, a);    \
+        } while (0)
+
+#define zx_read_reg(addr) \
+    ioread32(addr)
+
+#define zx_write_reg(addr, val)   \
+	zx_reg_sync_write(val, addr)
+
+
+
+struct zx29_board_data {
+	const char *name;
+	struct device *dev;
+
+	int codec_refclk;
+	int gpio_pwen;	
+	int gpio_pdn;
+	void __iomem *sys_base_va;
+
+};
+
+
+struct zx29_board_data *s_board = 0;
+
+//#define AON_WIFI_BT_CLK_CFG2  ((volatile unsigned int *)(ZX_TOP_CRM_BASE + 0x94))
+ /* Default ZX29s */
+static struct zx29_board_data zx29_platform_data = {
+	.codec_refclk = ZXIC_MCLK,
+};
+ static struct platform_device *zx29_snd_device;
+ 
+ static DEFINE_RAW_SPINLOCK(codec_pa_lock);
+ 
+ static int set_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol);
+ static int get_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol);
+
+
+#ifdef USE_ALSA_VOICE_FUNC
+ extern int zDrv_Audio_Printf(void *pFormat, ...);
+ extern int zDrvVp_GetVol_Wrap(void);
+ extern int zDrvVp_SetVol_Wrap(int volume);
+ extern int zDrvVp_GetPath_Wrap(void);
+ extern int zDrvVp_SetPath_Wrap(int path);
+ extern int zDrvVp_SetMute_Wrap(bool enable);
+ extern bool zDrvVp_GetMute_Wrap(void);
+ extern int zDrvVp_SetTone_Wrap(int toneNum);
+ 
+ static int vp_GetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetVol(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_GetVol(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_getTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ 
+ static int audio_GetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int audio_SetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+
+ 
+ //static const DECLARE_TLV_DB_SCALE(vp_path_tlv, 0, 300, 0);
+ 
+ static const char * const vpath_in_text[] = {
+	 "handset", "speak", "headset", "bluetooth",
+ };
+ 
+ static const char *tone_class[] = {
+	 "Lowpower", "Sms", "Callstd", "Alarm", "Calltime",
+ };
+ 
+ static const struct soc_enum vpath_in_enum =	 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(vpath_in_text), vpath_in_text); 
+ 
+ static const struct soc_enum tone_class_enum[] = {
+	 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tone_class), tone_class),
+ };
+ 
+ static const struct snd_kcontrol_new vp_snd_controls[] = {  
+	 SOC_ENUM_EXT("voice processing path select",vpath_in_enum,vp_GetPath,vp_SetPath),
+	 //SOC_SINGLE_EXT_TLV("voice processing path Volume",0, 5, 5, 0,vp_GetVol, vp_SetVol,vp_path_tlv), 
+	 SOC_SINGLE_EXT("voice processing path Volume",0, 5, 5, 0,vp_GetVol, vp_SetVol),
+	 SOC_SINGLE_EXT("voice uplink mute", 0, 1, 1, 0,vp_GetMute, vp_SetMute),
+	 SOC_ENUM_EXT("voice tone sel", tone_class_enum[0], vp_getTone, vp_SetTone),
+	 SOC_SINGLE_BOOL_EXT("path stauts dump", 0,get_path_stauts_switch, set_path_stauts_switch),
+	 SOC_ENUM_EXT("audio path select",vpath_in_enum,audio_GetPath,audio_SetPath),
+ };
+ 
+ static int curtonetype = 0;
+ static int vp_getTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 ucontrol->value.integer.value[0] = curtonetype;
+	 return 0;
+ }
+ 
+ static int vp_SetTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 int vol = 0,ret = 0, tonenum;
+	 tonenum = ucontrol->value.integer.value[0];
+	 curtonetype = tonenum;
+	 //printk("Alsa vp_SetTone tonenum=%d\n", tonenum);
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetTone_Wrap)(tonenum);
+	 if(ret < 0)
+	 {
+		 printk(KERN_ERR "vp_SetTone fail = %d\n", tonenum);
+		 return ret;
+	 }
+	 return 0;
+ }
+ 
+ static int vp_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 int enable = 0,ret = 0;
+	 enable = ucontrol->value.integer.value[0];
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetMute_Wrap)(enable);
+	 if(ret < 0)
+	 {
+	   printk(KERN_ERR "vp_SetMute fail = %d\n",enable);
+	   return ret;
+	 }
+	 return 0;
+ }
+ 
+ static int vp_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {		 
+		//ucontrol->value.integer.value[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetMute_Wrap)();
+		return 0;
+ }
+ 
+ static int vp_SetVol(struct snd_kcontrol *kcontrol,
+								struct snd_ctl_elem_value *ucontrol)
+ {
+		int vol = 0,ret = 0;
+		vol = ucontrol->value.integer.value[0];
+		//ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetVol_Wrap)(vol);
+		if(ret < 0)
+		{
+		   printk(KERN_ERR "vp_SetVol fail = %d\n",vol);
+		   return ret;
+	   }
+	 return 0;
+ }
+ static int vp_GetVol(struct snd_kcontrol *kcontrol,
+								struct snd_ctl_elem_value *ucontrol)
+ {		 
+		//ucontrol->value.integer.value[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetVol_Wrap)();
+		return 0;
+ }
+ static int vp_GetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {	 
+	 //ucontrol->value.enumerated.item[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetPath_Wrap)();
+	 return 0;
+ }
+ static int vp_SetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {
+	 int ret = 0,path = 0;
+	 unsigned long	flags;
+	 path = ucontrol->value.enumerated.item[0];
+ 
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetPath_Wrap)(path);
+	 if(ret < 0)
+	 {
+	   printk(KERN_ERR "vp_SetPath fail = %d\n",path);
+	   return ret;
+	 }
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+	 switch (path) {
+	 case 0:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+		 break;
+	 case 1:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_40, GPIO_HIGH);
+		 break;
+	 case 2:
+		 break;
+	 case 3:
+		 break;
+	 default:
+		 break;
+	 }
+#endif
+	 return 0;
+ }
+ 
+ static int curpath = 0;
+ static int audio_GetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {	 
+	 ucontrol->value.enumerated.item[0] = curpath;
+	 return 0;
+ }
+ 
+ static int audio_SetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {
+	 int ret = 0,path = 0;
+	 unsigned long	flags;
+	 
+	 path = ucontrol->value.enumerated.item[0];
+	 curpath = path;
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+	 switch (path) {
+	 case 0:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+		 break;
+	 case 1:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_40, GPIO_HIGH);
+		 break;
+	 case 2:
+		 break;
+	 case 3:
+		 break;
+	 default:
+		 break;
+	 }
+#endif
+	 return 0;
+ }
+ 
+ typedef enum
+ {
+	 VP_PATH_HANDSET	=0, 	
+	 VP_PATH_SPEAKER,		 
+	 VP_PATH_HEADSET,					  
+	 VP_PATH_BLUETOOTH, 				   
+	 VP_PATH_BLUETOOTH_NO_NR,					 
+	 VP_PATH_HSANDSPK,
+	 
+	 VP_PATH_OFF = 255, 				 
+	 
+	 MAX_VP_PATH = VP_PATH_OFF				 
+ }T_ZDrv_VpPath;
+ 
+ extern int zDrvVp_Loop(T_ZDrv_VpPath path);
+
+ 
+//#else
+ static const struct snd_kcontrol_new machine_snd_controls[] = {		 
+	 SOC_SINGLE_BOOL_EXT("path stauts dump", 0,get_path_stauts_switch, set_path_stauts_switch),
+ };
+ 
+
+ 
+ //extern int rt5670_hs_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+ 
+ int path_stauts_switch = 0;
+ static int set_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+ {
+	 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	 struct snd_soc_dapm_path *p;
+ 
+	 int path_stauts_switch = ucontrol->value.integer.value[0];
+ 
+	 
+	 if (path_stauts_switch == 1)
+	 {
+		 list_for_each_entry(p, &card->paths, list){
+			 
+		   //print_audio("Alsa	path name (%s),longname (%s),sink (%s),source (%s),connect %d \n", p->name,p->long_name,p->sink->name,p->source->name,p->connect);
+		   //printk("Alsa  path longname %s,sink %s,source %s,connect %d \n", p->long_name,p->sink->name,p->source->name,p->connect);
+ 
+		 }
+	 }
+	 return 0;
+ }
+ 
+ static int get_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+ {
+	 
+	 ucontrol->value.integer.value[0] = path_stauts_switch;
+	 return 0;
+ };
+#endif 
+ 
+#ifdef CONFIG_SND_SOC_JACK_DECTEC
+ 
+ static struct snd_soc_jack codec_headset;
+ 
+ /* Headset jack detection DAPM pins */
+ static struct snd_soc_jack_pin codec_headset_pins[] = {
+	 {
+		 .pin = "Headphone",
+		 .mask = SND_JACK_HEADPHONE,
+	 },
+ };
+ 
+#endif
+
+
+
+
+
+ static int zx29startup(struct snd_pcm_substream *substream)
+ {
+ //  int ret = 0;
+	 print_audio("Alsa	Entered func %s\n", __func__);
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx29_startup device=%d,stream=%d\n", substream->pcm->device, substream->stream);
+ 
+	 struct snd_pcm *pcmC0D0p = snd_lookup_minor_data(16, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+	 struct snd_pcm *pcmC0D1p = snd_lookup_minor_data(17, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+	 struct snd_pcm *pcmC0D2p = snd_lookup_minor_data(18, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);	 
+	 struct snd_pcm *pcmC0D3p = snd_lookup_minor_data(19, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);	
+	 if ((pcmC0D0p == NULL) || (pcmC0D1p == NULL) || (pcmC0D2p == NULL) || (pcmC0D3p == NULL))
+		 return  -EINVAL;	  
+	 if ((pcmC0D0p->streams[0].substream_opened && pcmC0D1p->streams[0].substream_opened) || 
+		 (pcmC0D0p->streams[0].substream_opened && pcmC0D2p->streams[0].substream_opened) || 
+		 (pcmC0D0p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened) || 
+		 (pcmC0D1p->streams[0].substream_opened && pcmC0D2p->streams[0].substream_opened) ||
+		 (pcmC0D1p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened) ||
+		 (pcmC0D2p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened))
+		 BUG();
+#if 0
+	 unsigned long	flags;
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 mdelay(1);  
+ 
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+	 }
+#endif
+ 
+ 
+	 return 0;
+ }
+ 
+ static void zx29_shutdown(struct snd_pcm_substream *substream)
+ {
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx297520xx_shutdown device=%d, stream=%d\n", substream->pcm->device, substream->stream);
+ //  print_audio("Alsa	Entered func %s, stream=%d\n", __func__, substream->stream);
+ 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+#endif
+	 }
+	 
+	 if (snd_soc_dai_active(cpu_dai))
+		 return;
+ 
+
+  
+ }
+ 
+ static void zx29_shutdown2(struct snd_pcm_substream *substream)
+ {
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx29_shutdown2 device=%d, stream=%d\n", substream->pcm->device, substream->stream);
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+#endif
+#ifdef USE_ALSA_VOICE_FUNC
+		 //CPPS_FUNC(cpps_callbacks, zDrvVp_Loop)(VP_PATH_OFF);
+#endif
+
+
+	 }
+ 
+	 if (snd_soc_dai_active(cpu_dai))
+		 return;
+ 
+
+ }
+ static int zx29_init_paiftx(struct snd_soc_pcm_runtime *rtd)
+ {
+	 //struct snd_soc_codec *codec = rtd->codec;
+	 //struct snd_soc_dapm_context *dapm = &codec->dapm;
+ 
+	 //snd_soc_dapm_enable_pin(dapm, "HPOL");
+	 //snd_soc_dapm_enable_pin(dapm, "HPOR");
+ 
+	 /* Other pins NC */
+ //  snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
+ 
+ //  print_audio("Alsa	Entered func %s\n", __func__);
+ 
+	 return 0;
+ }
+ static int zx29_hw_params(struct snd_pcm_substream *substream,
+										struct snd_pcm_hw_params *params)
+ {
+     print_audio("Alsa:	Entered func %s\n", __func__);
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	 int ret;
+	 int rfs = 0, frq_out = 0;	 
+	 switch (params_rate(params)) {
+	 case 8000:
+	 case 16000:
+	 case 11025:
+	 case 22050:
+	 case 24000:
+	 case 32000:
+	 case 44100:
+	 case 48000:
+		 rfs = 32;
+		 break;
+	 default:
+	 	{
+	 	    ret =  -EINVAL;
+		    print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret);	 	      
+		 	return ret;
+	 	}
+	 }
+	 
+	 frq_out = params_rate(params) * rfs * 2;
+	 
+	 /* Set the Codec DAI configuration */
+	 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+
+
+	 /* Set the AP DAI configuration */
+	 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: ap dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+
+	 ret = snd_soc_dai_set_sysclk(codec_dai, CODEC_SCLK_MCLK_ID,  ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+	  
+
+#if 0	 
+	 /* Set the Codec DAI clk */	 
+	 ret =snd_soc_dai_set_pll(codec_dai, 0, CODEC_SCLK_PLL,
+								  ZXIC_MCLK, params_rate(params)*256);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		 return ret;
+	}
+#endif	
+	
+
+	  
+	 /* Set the AP DAI clk */
+	 ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+	 //ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+ 
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: cpu dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+     print_audio("Alsa:	Entered func %s end\n", __func__);
+	 
+	 return 0;
+ }
+
+static int zx29_hw_params_lp(struct snd_pcm_substream *substream,
+									   struct snd_pcm_hw_params *params)
+{
+	print_audio("Alsa: Entered func %s\n", __func__);
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	int ret;
+	int rfs = 0, frq_out = 0;	
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+	case 11025:
+	case 22050:
+	case 24000:
+	case 32000:
+	case 44100:
+	case 48000:
+		rfs = 32;
+		break;
+	default:
+	   {
+		   ret =  -EINVAL;
+		   print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret); 			 
+		   return ret;
+	   }
+	}
+	
+	frq_out = params_rate(params) * rfs * 2;
+	
+	/* Set the Codec DAI configuration */
+	/*
+	
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							  | SND_SOC_DAIFMT_NB_NF
+							  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0){
+	   
+		print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		return ret;
+	}
+	*/ 
+
+	
+	/* Set the AP DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+							  | SND_SOC_DAIFMT_NB_NF
+							  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0){
+	   
+		print_audio("Alsa: ap dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		return ret;
+	}
+
+	/* Set the Codec DAI clk */ 	
+	/*ret =snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_BCLK1,
+								 fs*datawidth*2, 256*fs);
+	if (ret < 0){
+	   
+		print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		return ret;
+   }
+	*/
+	/*
+	ret = snd_soc_dai_set_sysclk(codec_dai, ES8312_CLKID_MCLK,ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	if (ret < 0){	   
+		print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		return ret;
+	}
+	*/
+	/* Set the AP DAI clk */
+	ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+
+	if (ret < 0){	   
+		print_audio("Alsa: cpu dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		return ret;
+	}
+	print_audio("Alsa: Entered func %s end\n", __func__);
+	
+	return 0;
+}
+
+
+ 
+
+ 
+
+ static int zx29_hw_params_voice(struct snd_pcm_substream *substream,
+										struct snd_pcm_hw_params *params)
+ {
+	 print_audio("Alsa: Entered func %s\n", __func__);
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	 int ret;
+	 int rfs = 0, frq_out = 0;	 
+	 switch (params_rate(params)) {
+	 case 8000:
+	 case 16000:
+	 case 11025:
+	 case 22050:
+	 case 24000:
+	 case 32000:
+	 case 44100:
+	 case 48000:
+		 rfs = 32;
+		 break;
+	 default:
+		{
+			ret =  -EINVAL;
+			print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret);			  
+			return ret;
+		}
+	 }
+	 
+	 frq_out = params_rate(params) * rfs * 2;
+	 
+	 /* Set the Codec DAI configuration */
+	 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+		
+		 print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+ 
+ 	 ret = snd_soc_dai_set_sysclk(codec_dai, CODEC_SCLK_MCLK_ID,  ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+
+	   
+	 
+#if 0	  
+	  /* Set the Codec DAI clk */	  
+	  ret =snd_soc_dai_set_pll(codec_dai, 0, CODEC_SCLK_PLL,
+								   ZXIC_MCLK, params_rate(params)*256);
+	  if (ret < 0){
+		 
+		  print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		  return ret;
+	  }
+#endif
+
+	 print_audio("Alsa: Entered func %s end\n", __func__);
+	 
+	 return 0;
+ }
+
+										 
+ int zx29_prepare2(struct snd_pcm_substream *substream)
+ {
+	 int path, ret;
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_Loop)(VP_PATH_SPEAKER);
+		 if (ret < 0)
+			 return -1;
+	 }
+	 
+	 return 0;
+ } 
+ #if 0
+ static void zx29_i2s_top_reg_cfg(void)
+ {
+	 unsigned int i2s_top_reg;
+	 int ret = 0;
+ 
+#ifdef CONFIG_USE_PIN_I2S0
+	 ret = gpio_request(PIN_I2S0_WS, "i2s0_ws");
+	 if (ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S0_CLK, "i2s0_clk");
+	 if (ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S0_DIN, "i2s0_din");
+	 if (ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S0_DOUT, "i2s0_dout");
+	 if (ret < 0)
+		 BUG();
+	 zx29_gpio_config(PIN_I2S0_WS, FUN_I2S0_WS);
+	 zx29_gpio_config(PIN_I2S0_CLK, FUN_I2S0_CLK);
+	 zx29_gpio_config(PIN_I2S0_DIN, FUN_I2S0_DIN);
+	 zx29_gpio_config(PIN_I2S0_DOUT, FUN_I2S0_DOUT);
+	 
+	 //top i2s1 cfg
+	 i2s_top_reg = zx_read_reg(ZX29_I2S_LOOP_CFG);
+	 i2s_top_reg &= 0xfffffff8;
+	 i2s_top_reg |= 0x00000001; //	inter arm_i2s1--top i2s1
+	 zx_write_reg(ZX29_I2S_LOOP_CFG, i2s_top_reg);
+#elif defined (CONFIG_USE_PIN_I2S1)
+	
+
+	 ret = gpio_request(PIN_I2S1_WS,"i2s1_ws");
+	 if(ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S1_CLK,"i2s1_clk");
+	 if(ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S1_DIN,"i2s1_din");
+	 if(ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S1_DOUT,"i2s1_dout");
+	 if(ret < 0)
+		 BUG();
+	 zx29_gpio_config(PIN_I2S1_WS, FUN_I2S1_WS);
+	 zx29_gpio_config(PIN_I2S1_CLK, FUN_I2S1_CLK);
+	 zx29_gpio_config(PIN_I2S1_DIN, FUN_I2S1_DIN);
+	 zx29_gpio_config(PIN_I2S1_DOUT, FUN_I2S1_DOUT);
+		 
+	 //top i2s2 cfg
+	 i2s_top_reg = zx_read_reg(ZX29_I2S_LOOP_CFG);
+	 i2s_top_reg &= 0xfff8ffff;
+	 i2s_top_reg |= 0x00010000; //	inter arm_i2s1--top i2s2
+	 zx_write_reg(ZX29_I2S_LOOP_CFG, i2s_top_reg);
+#endif
+ 
+	 // inter loop
+	 //i2s_top_reg = zx_read_reg(ZX29_I2S_LOOP_CFG);
+	 //i2s_top_reg &= 0xfffffe07;
+	 //i2s_top_reg |= 0x000000a8; //	inter arm_i2s2--afe i2s
+	 //zx_write_reg(ZX29_I2S_LOOP_CFG, i2s_top_reg);
+	 
+ //  print_audio("Alsa %s i2s loop cfg reg=%x\n",__func__, zx_read_reg(ZX29_I2S_LOOP_CFG));  
+ }
+ #endif
+ static int zx29_late_probe(struct snd_soc_card *card)
+ {
+	 //struct snd_soc_codec *codec = card->rtd[0].codec;
+	 //struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+	 int ret;
+ //  print_audio("Alsa	zx29_late_probe entry!\n");
+ 
+#ifdef CONFIG_SND_SOC_JACK_DECTEC
+	 
+	 ret = snd_soc_jack_new(codec, "Headset",
+							SND_JACK_HEADSET |SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
+							&codec_headset);
+	 if (ret)
+		 return ret;
+ 
+	 ret = snd_soc_jack_add_pins(&codec_headset,
+								 ARRAY_SIZE(codec_headset_pins),
+								 codec_headset_pins);
+	 if (ret)
+		 return ret;
+       #ifdef CONFIG_SND_SOC_codec
+	 //rt5670_hs_detect(codec, &codec_headset);
+       #endif
+#endif
+ 
+	 return 0;
+ }
+ 
+ static struct snd_soc_ops zx29_ops = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 .hw_params = zx29_hw_params,
+ };
+  static struct snd_soc_ops zx29_ops_lp = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 .hw_params = zx29_hw_params_lp,
+ };
+ static struct snd_soc_ops zx29_ops1 = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 //.hw_params = zx29_hw_params1,
+ };
+ 
+ static struct snd_soc_ops zx29_ops2 = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown2,
+	 //.hw_params = zx29_hw_params1,
+	 .prepare = zx29_prepare2,
+ };
+ static struct snd_soc_ops voice_ops = {
+	 .startup = zx29startup,
+	 .shutdown = zx29_shutdown2,
+	 .hw_params = zx29_hw_params_voice,
+	 //.prepare = zx29_prepare2,
+ };
+
+ 
+ enum {
+	 MERR_DPCM_AUDIO = 0,
+	 MERR_DPCM_DEEP_BUFFER,
+	 MERR_DPCM_COMPR,
+ };
+
+ 
+#if 0
+ 
+ static struct snd_soc_card zxic_soc_card = {
+	 .name = "zx298501_ti3100",
+	 .owner = THIS_MODULE,
+	 .dai_link = &zxic_dai_link,
+	 .num_links = ARRAY_SIZE(zxic_dai_link),
+#ifdef USE_ALSA_VOICE_FUNC
+	 .controls = vp_snd_controls,
+	 .num_controls = ARRAY_SIZE(vp_snd_controls),
+#endif
+ 
+ //  .late_probe = zx29_late_probe,
+	 
+ };
+#endif 
+ //static struct zx298501_ti3100_pdata *zx29_platform_data;
+ 
+ static int zx29_setup_pins(struct zx29_board_data *codec_pins, char *fun)
+ {
+	 int ret;
+ 
+	 //ret = gpio_request(codec_pins->codec_refclk, "codec_refclk");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio: %s pin already in use\n", fun);
+		 return ret;
+	 }
+	 //zx29_gpio_config(codec_pins->codec_refclk, GPIO17_CLK_OUT2);
+ 
+#ifdef  _USE_7520V3_PHONE_TYPE_C31F
+	 ret = gpio_request_one(ZX29_GPIO_39, GPIOF_OUT_INIT_LOW, "codec_pa");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio:  codec_pa in use\n");
+		 return ret;
+	 }
+	 
+	 ret = gpio_request_one(ZX29_GPIO_40, GPIOF_OUT_INIT_LOW, "codec_sw");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio:  codec_sw in use\n");
+		 return ret;
+	 }
+#endif
+ 
+	 return 0;
+ }
+#endif
+
+ 
+ static int zx29_remove(struct platform_device *pdev)
+ {
+	 gpio_free(zx29_platform_data.codec_refclk);
+	 platform_device_unregister(zx29_snd_device);
+	 return 0;
+ }
+ 
+
+ 
+#if  0
+
+ /*
+  * Default CFG switch settings to use this driver:
+  *	ZX29
+  */
+
+ /*
+  * Configure audio route as :-
+  * $ amixer sset 'DAC1' on,on
+  * $ amixer sset 'Right Headphone Mux' 'DAC'
+  * $ amixer sset 'Left Headphone Mux' 'DAC'
+  * $ amixer sset 'DAC1R Mixer AIF1.1' on
+  * $ amixer sset 'DAC1L Mixer AIF1.1' on
+  * $ amixer sset 'IN2L' on
+  * $ amixer sset 'IN2L PGA IN2LN' on
+  * $ amixer sset 'MIXINL IN2L' on
+  * $ amixer sset 'AIF1ADC1L Mixer ADC/DMIC' on
+  * $ amixer sset 'IN2R' on
+  * $ amixer sset 'IN2R PGA IN2RN' on
+  * $ amixer sset 'MIXINR IN2R' on
+  * $ amixer sset 'AIF1ADC1R Mixer ADC/DMIC' on
+  */
+
+/* ZX29 has a 16.934MHZ crystal attached to ti3100 */
+#define ZX29_TI3100_FREQ 16934000
+
+
+
+
+
+static int zx29_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	unsigned int pll_out;
+	int ret;
+
+	/* AIF1CLK should be >=3MHz for optimal performance */
+	if (params_width(params) == 24)
+		pll_out = params_rate(params) * 384;
+	else if (params_rate(params) == 8000 || params_rate(params) == 11025)
+		pll_out = params_rate(params) * 512;
+	else
+		pll_out = params_rate(params) * 256;
+
+	ret = snd_soc_dai_set_pll(codec_dai, AK4940_FLL1, AK4940_FLL_SRC_MCLK1,
+					ZX29_AK4940_FREQ, pll_out);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, AK4940_SYSCLK_FLL1,
+					pll_out, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * ZX29 AK4940 DAI operations.
+ */
+static struct snd_soc_ops zx29_ops = {
+	.hw_params = smdk_hw_params,
+};
+
+static int zx29_ti3100_init_paiftx(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
+
+	/* Other pins NC */
+	snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
+	snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTLN");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTLP");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTRP");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTRN");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
+	snd_soc_dapm_nc_pin(dapm, "IN1LP");
+	snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
+	snd_soc_dapm_nc_pin(dapm, "IN1RP");
+	snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
+
+	return 0;
+}
+#endif
+
+
+
+
+enum {
+	AUDIO_DL_MEDIA = 0,
+	AUDIO_DL_VOICE,
+	AUDIO_DL_2G_AND_3G_VOICE,
+	AUDIO_DL_VP_LOOP,	
+	AUDIO_DL_3G_VOICE,
+	
+	AUDIO_DL_MAX,
+};
+SND_SOC_DAILINK_DEF(dummy, \
+	DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+//SND_SOC_DAILINK_DEF(cpu_i2s0, \
+//	DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+SND_SOC_DAILINK_DEF(cpu_i2s0, \
+	DAILINK_COMP_ARRAY(COMP_CPU("1405000.i2s")));
+
+
+SND_SOC_DAILINK_DEF(voice_cpu, \
+	DAILINK_COMP_ARRAY(COMP_CPU("soc:voice_audio")));
+
+SND_SOC_DAILINK_DEF(voice_2g_3g, \
+	DAILINK_COMP_ARRAY(COMP_CPU("voice_2g_3g-dai")));
+
+SND_SOC_DAILINK_DEF(voice_3g, \
+		DAILINK_COMP_ARRAY(COMP_CPU("voice_3g-dai")));
+
+
+
+//SND_SOC_DAILINK_DEF(ti3100, \
+//	DAILINK_COMP_ARRAY(COMP_CODEC("ti3100.1-0012", "ti3100-aif")));
+SND_SOC_DAILINK_DEF(dummy_cpu, \
+		DAILINK_COMP_ARRAY(COMP_CPU("soc:zx29_snd_dummy")));
+//SND_SOC_DAILINK_DEF(dummy_platform, \
+//	DAILINK_COMP_ARRAY(COMP_PLATFORM("soc:zx29_snd_dummy")));
+
+SND_SOC_DAILINK_DEF(dummy_codec, \
+		DAILINK_COMP_ARRAY(COMP_CODEC("soc:zx29_snd_dummy", "zx29_snd_dummy_dai")));
+SND_SOC_DAILINK_DEF(max9867_codec, \
+		DAILINK_COMP_ARRAY(COMP_CODEC("max9867.1-001a", "max9867-hifi")));
+
+//SND_SOC_DAILINK_DEF(media_platform, \
+//	DAILINK_COMP_ARRAY(COMP_PLATFORM("zx29-pcm-audio")));
+SND_SOC_DAILINK_DEF(media_platform, \
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("1405000.i2s")));
+//SND_SOC_DAILINK_DEF(voice_cpu, \
+//	DAILINK_COMP_ARRAY(COMP_CPU("E1D02000.i2s")));
+
+SND_SOC_DAILINK_DEF(voice_platform, \
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("soc:voice_audio")));
+
+
+
+			
+//static struct snd_soc_dai_link zx29_dai_link[] = {
+struct snd_soc_dai_link zx29_dai_link[] = {
+ {
+	.name = "zx29_snd_dummy",//codec name
+	.stream_name = "zx29_snd_dumy",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &zx29_ops_lp,
+	.init = zx29_init_paiftx,
+	SND_SOC_DAILINK_REG(cpu_i2s0, dummy_codec, media_platform),
+	
+},
+#if 1
+{
+	.name = "media",//codec name
+	.stream_name = "MultiMedia",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &zx29_ops,
+
+ 	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(cpu_i2s0, max9867_codec, media_platform),
+
+},
+{
+	.name = "voice",//codec name
+	.stream_name = "voice",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	.init = zx29_init_paiftx,
+	
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, max9867_codec, voice_platform),
+
+},
+{
+	.name = "voice_2g3g_teak",//codec name
+	.stream_name = "voice_2g3g_teak",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, max9867_codec, voice_platform),
+
+},
+
+{
+	.name = "voice_3g",//codec name
+	.stream_name = "voice_3g",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, max9867_codec, voice_platform),
+
+},
+
+{
+	.name = "loop_test",//codec name
+	.stream_name = "loop_test",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	//.ops = &zx29_ops,
+	.ops = &voice_ops,
+
+	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, max9867_codec, dummy),
+
+},
+#endif
+
+};
+
+
+
+
+
+static struct snd_soc_card zx29_soc_card = {
+	.name = "zx29-sound-card",
+	.owner = THIS_MODULE,
+	.dai_link = zx29_dai_link,
+	.num_links = ARRAY_SIZE(zx29_dai_link),
+#ifdef USE_ALSA_VOICE_FUNC
+	 .controls = vp_snd_controls,
+	 .num_controls = ARRAY_SIZE(vp_snd_controls),
+#endif	
+};
+
+static const struct of_device_id zx29_max9867_of_match[] = {
+	{ .compatible = "zxic,zx29_max9867", .data = &zx29_platform_data },
+	{},
+};
+MODULE_DEVICE_TABLE(of, zx29_max9867_of_match);
+
+static void zx29_i2s_top_pin_cfg(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct pinctrl *p,*p2;
+	struct pinctrl_state *s,*s2;
+	int ret = 0;
+	printk("%s start n",__func__);
+
+	struct resource *res;
+	void __iomem	*reg_base;
+	unsigned int val;
+
+
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "soc_sys");
+	if (!res) {
+		dev_err(dev, "Reg region missing (%s)\n", "soc_sys");
+		//return -ENXIO;
+	}
+
+	#if 0
+	reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(reg_base )) {
+			dev_err(dev, "Reg region ioremap (%s) err=%li\n", "soc_sys",PTR_ERR(reg_base ));
+		//return PTR_ERR(reg_base );
+	}
+
+	#else
+	reg_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	#endif
+	 
+//#if 1 //CONFIG_USE_PIN_I2S0
+#ifdef 	CONFIG_USE_TOP_I2S0
+
+	dev_info(dev, "%s: arm i2s1 to top i2s0!!\n", __func__); 
+	//9300
+		 
+	//top i2s1 cfg
+	val = zx_read_reg(reg_base+ZX29_I2S_TOP_LOOP_REG);
+	val &= ~(0x7<<0);
+	val |= 0x1<<0; //	inter arm_i2s1--top i2s1
+	zx_write_reg(reg_base+ZX29_I2S_TOP_LOOP_REG, val);
+#else //(CONFIG_USE_PIN_I2S1)
+    //8501evb    	
+
+	dev_info(dev, "%s: arm i2s1 to top i2s1!\n", __func__); 
+			 
+	//top i2s2 cfg
+	val = zx_read_reg(reg_base+ZX29_I2S_TOP_LOOP_REG);
+	//val &= 0xfffffff8;
+	val &= ~(0x7<<16);	
+	val |= 0x1<<16;//	inter arm_i2s1--top i2s2
+	zx_write_reg(reg_base+ZX29_I2S_TOP_LOOP_REG, val);
+#endif
+
+	p = devm_pinctrl_get(dev);
+	if (IS_ERR(p)) {
+		dev_err(dev, "%s: pinctrl get failure ,p=0x%llx,dev=0x%llx!!\n", __func__,p,dev);
+		return;
+	}
+	
+	dev_info(dev, "%s: get pinctrl ,p=0x%llx,dev=0x%llx!!\n", __func__,p,dev); 
+
+	s = pinctrl_lookup_state(p, "top_i2s");
+	if (IS_ERR(s)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}
+	ret = pinctrl_select_state(p, s);
+	if (ret < 0) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " select state failure!!\n");
+		return;
+	}
+	dev_info(dev, "%s: set i2s0 end!\n", __func__);	
+	/*
+	p2 = devm_pinctrl_get(dev);
+	if (IS_ERR(p)) {
+		dev_err(dev, "%s: pinctrl get failure ,p=0x%llx,dev=0x%llx!!\n", __func__,p,dev);
+		return;
+	}
+    */	
+
+	s2 = pinctrl_lookup_state(p, "top_i2s1");
+	if (IS_ERR(s)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}
+	
+	ret = pinctrl_select_state(p, s2);
+	if (ret < 0) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " select state failure!!\n");
+		return;
+	}
+	dev_info(dev, "%s: set i2s1 end!\n", __func__);	
+
+
+	dev_info(dev, "%s: set pinctrl end!\n", __func__);	
+
+}
+#if 0
+static int codec_power_on(struct zx29_board_data * board,bool on_off)
+{
+	int ret = 0;
+	//struct zx29_board_data *board = dev_get_drvdata(dev);
+	struct device *dev = board->dev;
+
+	dev_info(dev, "%s:start %s board gpio_pwen=%d,gpio_pdn=%d on_off=%d\n",__func__,board->name,board->gpio_pwen,board->gpio_pdn,on_off);
+
+	if(on_off){
+
+		ret = gpio_direction_output(board->gpio_pwen, 1);	
+		if (ret < 0) {
+				dev_err(dev,"gpio_pwen %d direction fail set to 1: %d\n",board->gpio_pwen, ret);
+				return ret;
+		 }
+		
+		ret = gpio_direction_output(board->gpio_pdn, 1);	
+		if (ret < 0) {
+				dev_err(dev,"gpio_pdn %d direction fail set to 1: %d\n",board->gpio_pdn, ret);
+				return ret;
+		 }
+
+		
+	}
+	else{
+		ret = gpio_direction_output(board->gpio_pwen, 0);	
+		if (ret < 0) {
+				dev_err(dev,"gpio_pwen %d direction fail set to 0: %d\n",board->gpio_pwen, ret);
+				return ret;
+		 }
+		
+		ret = gpio_direction_output(board->gpio_pdn, 0);	
+		if (ret < 0) {
+				dev_err(dev,"gpio_pdn %d direction fail set to 0: %d\n",board->gpio_pdn, ret);
+				return ret;
+		 }
+
+	
+	}
+
+	return ret;
+
+}
+#endif
+
+
+#ifdef  CONFIG_PA_SA51034
+//sa51034
+#define SA51034_DEBUG
+
+#define SA51034_01_LATCHED_FAULT		0x01
+#define SA51034_02_STATUS_LOAD_DIAGNOSTIC      0x02
+#define SA51034_03_CONTROL			0x03
+#define SA51034_MAX_REGISTER SA51034_03_CONTROL
+
+struct sa51034_priv {
+	struct i2c_client *i2c;
+	struct regmap *regmap;
+	int pwen_gpio;//add new
+	int mute_gpio;
+	int fs;
+
+};
+static int sa51034_set_mute(struct sa51034_priv *sa51034,int mute);
+static int sa51034_get_mute(struct sa51034_priv *sa51034,int *mute); 
+
+
+
+
+struct sa51034_priv *g_sa51034 = NULL;
+/* ak4940 register cache & default register settings */
+static const struct reg_default sa51034_reg[] = {
+	{ 0x01, 0x00 },  /* SA51034_00_LATCHED_FAULT	*/
+	{ 0x02, 0x00 },  /* SA51034_01_STATUS_LOAD_DIAGNOSTIC	*/
+	{ 0x03, 0x00 },  /* SA51034_02_CONTROL			*/
+	
+};
+	
+static const char * const pa_gain_select_texts[] = {
+	"20dB", "26dB","30dB", "36dB",
+};
+static const char * const power_limit_select_texts[] = {
+	"PL-5V", "PL-5.9V","PL-7V", "PL-8.4V","PL-9.8V", "PL-11.8V","PL-14V", "PL-disV",
+};
+
+static const struct soc_enum pa_gain_enum[] = {
+	SOC_ENUM_SINGLE(SA51034_03_CONTROL, 6,
+	ARRAY_SIZE(pa_gain_select_texts), pa_gain_select_texts),
+};
+static const struct soc_enum power_limit_enum[] = {
+	SOC_ENUM_SINGLE(SA51034_03_CONTROL, 3,
+	ARRAY_SIZE(power_limit_select_texts), power_limit_select_texts),
+};
+	
+static const char * const reg_select[] = {
+	"read PA Reg 01:03",
+};
+
+static const struct soc_enum pa_enum2[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(reg_select),reg_select),
+};
+
+static int get_reg(
+	struct snd_kcontrol       *kcontrol,
+	struct snd_ctl_elem_value  *ucontrol)
+{
+	struct snd_soc_component *component; 
+	struct device *dev;    
+
+	
+
+	u32    currMode = ucontrol->value.enumerated.item[0];
+	int    i, ret;
+	int	   regs, rege;
+	unsigned int value;
+
+
+	if(g_sa51034 == NULL){
+	   pr_err("g_sa51034 null return %s\n", __func__);	  
+	   return -1;
+	}
+	dev = &g_sa51034->i2c->dev; 
+
+	component =  snd_soc_lookup_component(dev, NULL); 	
+	regs = 0x1;
+	rege = 0x4;
+
+	for (i = regs; i < rege; i++) {
+		value = snd_soc_component_read(component, i);
+		if (value < 0) {
+			pr_err("pa %s(%d),err value=%d\n", __func__, __LINE__, value);
+			return value;
+		}
+		pr_info("pa 2c_read Addr,Reg=(%x, %x)\n", i, value);
+	}
+	
+	return 0;
+}
+
+
+
+  int pa_get_enum_double(struct snd_kcontrol *kcontrol,
+	  struct snd_ctl_elem_value *ucontrol)
+  {
+	  //struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+  
+	  struct snd_soc_component *component; 
+	  struct device *dev;	 
+
+	  
+
+	  
+	  struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	  unsigned int val, item;
+	  unsigned int reg_val;
+	  int ret;
+	  if(g_sa51034 == NULL){
+ 	  	 pr_err("g_sa51034 null return %s\n", __func__);	
+ 		 return -1;
+	  }
+	  dev = &g_sa51034->i2c->dev; 
+
+	  
+	  component = snd_soc_lookup_component(dev, NULL);  
+	  reg_val = snd_soc_component_read(component, e->reg);
+
+
+	  if (reg_val < 0) {
+	  	  pr_err("pa %s(%d),err reg_val=%d\n", __func__, __LINE__, reg_val);
+		  return reg_val;
+	  }
+
+	  
+	  val = (reg_val >> e->shift_l) & e->mask;
+	  item = snd_soc_enum_val_to_item(e, val);
+	  ucontrol->value.enumerated.item[0] = item;
+	  if (e->shift_l != e->shift_r) {
+		  val = (reg_val >> e->shift_r) & e->mask;
+		  item = snd_soc_enum_val_to_item(e, val);
+		  ucontrol->value.enumerated.item[1] = item;
+	  }
+  
+	  return 0;
+  }
+
+  int pa_put_enum_double(struct snd_kcontrol *kcontrol,
+	  struct snd_ctl_elem_value *ucontrol)
+  {
+	  //struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	  
+	  struct snd_soc_component *component; 
+	  struct device *dev;	 
+	  struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	  unsigned int *item = ucontrol->value.enumerated.item;
+	  unsigned int val;
+	  unsigned int mask;
+
+	  if(g_sa51034 == NULL){
+ 	  	 pr_err("g_sa51034 null return %s\n", __func__);	
+ 		 return -1;
+	  }
+	  dev = &g_sa51034->i2c->dev; 
+	  component = snd_soc_lookup_component(dev, NULL);  
+  
+	  if (item[0] >= e->items)
+		  return -EINVAL;
+	  val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+	  mask = e->mask << e->shift_l;
+	  if (e->shift_l != e->shift_r) {
+		  if (item[1] >= e->items)
+			  return -EINVAL;
+		  val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
+		  mask |= e->mask << e->shift_r;
+	  }
+  
+	  return snd_soc_component_update_bits(component, e->reg, mask, val);
+  }
+
+
+static int pa_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	  int mute = 0,ret = 0;
+	  
+
+
+	  if(g_sa51034 == NULL){
+ 	  	 pr_err("g_sa51034 null return %s\n", __func__);	
+ 		 return -1;
+	  }	  
+	  mute = ucontrol->value.integer.value[0];
+	  ret = sa51034_set_mute(g_sa51034,mute);
+	  
+	  if(ret < 0)
+	  {
+		printk(KERN_ERR "sa51034_set_mute fail ret=%d,mute=%d\n",ret,mute);
+		return ret;
+	  }
+	  return 0;
+}
+
+static int pa_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{ 	
+	int mute = 0,ret = 0;
+	
+	if(g_sa51034 == NULL){
+		pr_err("g_sa51034 null return %s\n", __func__);    
+		return -1;
+	}
+	ret = sa51034_get_mute(g_sa51034,&mute);
+	
+	if(ret < 0)
+	{
+	  printk(KERN_ERR "sa51034_get_mute fail ret= %d\n",ret);
+	  return ret;
+	}
+	pr_info("[SA51034] %s mute gpio val=%d,integer.value[0]=%d\n", __func__, mute,ucontrol->value.integer.value[0]);
+
+	ucontrol->value.integer.value[0] = mute;
+
+	return 0;
+}
+
+
+
+
+
+const struct snd_kcontrol_new pa_controls[] =
+{
+	SOC_ENUM_EXT("PA gain", pa_gain_enum[0], pa_get_enum_double, pa_put_enum_double),
+    SOC_ENUM_EXT("Power limit", power_limit_enum[0], pa_get_enum_double, pa_put_enum_double),
+	SOC_ENUM_EXT("PA Reg Read", pa_enum2[0], get_reg, NULL),
+	SOC_SINGLE_EXT("pa mute", 0, 0, 1, 0,pa_GetMute, pa_SetMute),
+
+
+};
+	
+int pa_controls_size = sizeof(pa_controls) / sizeof(pa_controls[0]);
+
+
+
+
+static bool sa51034_volatile(struct device *dev, unsigned int reg)
+{
+	bool ret;
+
+#ifdef SA51034_DEBUG
+	ret = true;
+#else
+	ret = false;
+#endif
+
+	return ret;
+}
+
+static bool sa51034_readable(struct device *dev, unsigned int reg)
+{
+	if (reg <= SA51034_MAX_REGISTER)
+		return true;
+	else
+		return false;
+}
+
+static bool sa51034_writeable(struct device *dev, unsigned int reg)
+{
+	if (reg <= SA51034_MAX_REGISTER)
+		return true;
+	else
+		return false;
+}
+
+
+static const struct regmap_config sa51034_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = SA51034_MAX_REGISTER,
+	.volatile_reg = sa51034_volatile,
+	.writeable_reg = sa51034_writeable,
+	.readable_reg = sa51034_readable,
+
+	.reg_defaults = sa51034_reg,
+	.num_reg_defaults = ARRAY_SIZE(sa51034_reg),
+	.cache_type = REGCACHE_RBTREE,
+
+};
+
+static const struct snd_soc_component_driver pa_asoc_component = {
+	.name = "pa_component",
+
+
+	//.controls = pa_controls,
+	//.num_controls = ARRAY_SIZE(pa_controls),
+
+
+};
+
+static const struct of_device_id sa51034_i2c_dt_ids[] = {
+	{ .compatible = "sa51034"},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sa51034_i2c_dt_ids);
+static int sa51034_gpio_request(struct sa51034_priv *sa51034)
+{
+	struct device *dev;
+	struct device_node *np;
+    int ret;
+	dev = &(sa51034->i2c->dev);
+
+	np = dev->of_node;
+
+	if (!np)
+		return 0;
+
+	pr_info( "Read PDN pin from device tree\n");
+
+
+	sa51034->pwen_gpio = of_get_named_gpio(np, "sa51034,ctrl-gpio", 0);
+	if (sa51034->pwen_gpio < 0) {
+	    pr_err(  "sa51034 pwen pin of_get_named_gpio fail\n");
+		
+		sa51034->pwen_gpio = -1;
+		return -1;
+	}
+
+	if (!gpio_is_valid(sa51034->pwen_gpio)) {
+		pr_err(  "sa51034 pwen_gpio pin(%u) is invalid\n", sa51034->pwen_gpio);
+		sa51034->pwen_gpio = -1;
+		return -1;
+	}
+	sa51034->mute_gpio = of_get_named_gpio(np, "sa51034,ctrl-gpio", 1);
+	if (sa51034->mute_gpio < 0) {
+		
+	    pr_err(  "sa51034 mute_gpio pin of_get_named_gpio fail\n");
+		sa51034->mute_gpio = -1;
+		return -1;
+	}
+
+	if (!gpio_is_valid(sa51034->mute_gpio)) {
+		pr_err(  "sa51034 mute_gpio pin(%u) is invalid\n", sa51034->mute_gpio);
+		sa51034->mute_gpio = -1;
+		return -1;
+	}
+
+	
+	pr_info( "sa51034 get pwen_gpio pin(%u) mute_gpio pin(%u)\n", sa51034->pwen_gpio,sa51034->mute_gpio);
+
+	if (sa51034->pwen_gpio != -1) {
+		ret = devm_gpio_request(dev,sa51034->pwen_gpio, "sa51034 pwen");
+		if (ret < 0){
+			pr_err(  "sa51034 pwen_gpio request fail,ret=%d\n",ret);
+			return ret;			
+		}
+		pr_info("\t[sa51034] %s :pwen_gpio gpio_request ret = %d\n", __func__, ret);
+		gpio_direction_output(sa51034->pwen_gpio, 0);
+	}
+
+	
+	if (sa51034->mute_gpio != -1) {
+		ret = devm_gpio_request(dev,sa51034->mute_gpio, "sa51034 mute");
+		if (ret < 0){
+			pr_err(  "sa51034 mute_gpio request fail,ret=%d\n",ret);
+			return ret;			
+		}
+
+		pr_info("\t[AK4940] %s : mute_gpio gpio_request ret = %d\n", __func__, ret);
+		gpio_direction_output(sa51034->mute_gpio, 1);
+	}
+  
+	
+	return 0;
+}
+
+static int sa51034_set_mute(struct sa51034_priv *sa51034,int mute) 
+{
+	//struct snd_soc_component *component = dai->component;
+	//struct ak4940_priv *ak4940 = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+	//int ndt;
+
+	pr_info("[SA51034] %s mute=%d\n", __func__, mute);
+	if (sa51034->mute_gpio == -1) {
+			pr_err(  "sa51034 %s mute_gpio invalid return\n",__func__);
+			return -1;	
+	}
+
+	//ndt = 4080000 / sa51034->fs;
+	if (mute) {
+		/* SMUTE: 1 , MUTE */
+		ret = gpio_direction_output(sa51034->mute_gpio, 1);
+		//mdelay(ndt);
+	} else{
+		/* SMUTE:  0  ,NORMAL operation */
+		ret = gpio_direction_output(sa51034->mute_gpio, 0);
+		//mdelay(ndt);
+	}
+	return ret;
+}
+
+static int sa51034_get_mute(struct sa51034_priv *sa51034,int *mute) 
+{
+
+	int ret = 0;
+	if (sa51034->mute_gpio == -1) {
+			pr_err(  "sa51034 %s mute_gpio invalid return\n",__func__);
+			return -1;	
+	}
+	
+	*mute = gpio_get_value(sa51034->mute_gpio);
+	pr_info("[SA51034] %s mute gpio val=%d\n", __func__, *mute);
+	
+	return ret;
+}
+
+static int sa51034_set_pwen(struct sa51034_priv *sa51034,int en) 
+{
+	//struct snd_soc_component *component = dai->component;
+	//struct ak4940_priv *ak4940 = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+	//int ndt;
+
+	pr_info("\t[SA51034] %s en[%s]\n", __func__, en ? "ON":"OFF");
+	if (sa51034->pwen_gpio == -1) {
+			pr_err(  "sa51034 %s pwen_gpio invalid return\n",__func__);
+			return -1;	
+	}
+	//ndt = 4080000 / sa51034->fs;
+	if (en) {
+		/* SMUTE: 1 , MUTE */
+		ret = gpio_direction_output(sa51034->pwen_gpio, 1);
+		//mdelay(ndt);
+	} else{
+		/* SMUTE:  0  ,NORMAL operation */
+		ret = gpio_direction_output(sa51034->pwen_gpio, 0);
+		//mdelay(ndt);
+	}
+	return ret;
+}
+
+
+
+static int sa51034_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+	struct sa51034_priv *sa51034;
+	int ret = 0;
+	unsigned int val;
+
+	pr_info("\t[sa51034] %s(%d),i2c->addr=0x%x\n", __func__, __LINE__,i2c->addr);
+
+	sa51034 = devm_kzalloc(&i2c->dev, sizeof(struct sa51034_priv), GFP_KERNEL);
+	if (sa51034 == NULL)
+		return -ENOMEM;
+
+
+	sa51034->regmap = devm_regmap_init_i2c(i2c, &sa51034_regmap);
+
+	if (IS_ERR(sa51034->regmap)) {
+		devm_kfree(&i2c->dev, sa51034);
+		return PTR_ERR(sa51034->regmap);
+	}
+
+
+	i2c_set_clientdata(i2c, sa51034);
+	sa51034->i2c = i2c;
+	ret = devm_snd_soc_register_component(&i2c->dev, &pa_asoc_component,
+					      NULL, 0);
+	if (ret) {
+		pr_err( "pa component register failed,ret=%d\n",ret);
+		return ret;
+	}
+
+	pr_info("[sa51034] %s(%d) pa component register end,ret=0x%x\n", __func__, __LINE__,ret);
+
+	sa51034_gpio_request(sa51034);
+
+
+	sa51034_set_pwen(sa51034,1); 
+
+	//sa51034_set_mute(sa51034,0);
+
+	g_sa51034 = sa51034;
+
+	
+	pr_info("\t[sa51034] %s end\n", __func__);
+	return ret;
+}
+
+static const struct i2c_device_id sa51034_i2c_id[] = {
+
+	{ "sa51034", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, sa51034_i2c_id);
+
+static struct i2c_driver sa51034_i2c_driver = {
+	.driver = {
+		.name = "sa51034",
+		.of_match_table = of_match_ptr(sa51034_i2c_dt_ids),
+	},
+	.probe = sa51034_i2c_probe,
+	//.remove = sa51034_i2c_remove,
+	.id_table = sa51034_i2c_id,
+};
+
+static int  sa51034_init(void)
+{
+	pr_info("\t[sa51034] %s(%d)\n", __func__, __LINE__);
+
+	return i2c_add_driver(&sa51034_i2c_driver);
+}
+
+#endif
+static int zx29_audio_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device_node *np = pdev->dev.of_node;
+	struct snd_soc_card *card = &zx29_soc_card;
+	struct zx29_board_data *board;
+	const struct of_device_id *id;
+	enum of_gpio_flags flags;
+	unsigned int idx;
+
+	struct device *dev = &pdev->dev;
+	dev_info(&pdev->dev,"zx29_audio_probe start!\n");
+
+
+	card->dev = &pdev->dev;
+
+	board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
+	if (!board)
+		return -ENOMEM;
+
+	if (np) {
+		zx29_dai_link[0].cpus->dai_name = NULL;
+		zx29_dai_link[0].cpus->of_node = of_parse_phandle(np,
+				"zxic,i2s-controller", 0);
+		if (!zx29_dai_link[0].cpus->of_node) {
+			dev_err(&pdev->dev,
+			   "Property 'zxic,i2s-controller' missing or invalid\n");
+			ret = -EINVAL;
+		}
+
+		zx29_dai_link[0].platforms->name = NULL;
+		zx29_dai_link[0].platforms->of_node = zx29_dai_link[0].cpus->of_node;
+
+		
+#if 0
+		zx29_dai_link[0].codecs->of_node = of_parse_phandle(np,
+				"zxic,audio-codec", 0);
+		if (!zx29_dai_link[0].codecs->of_node) {
+			dev_err(&pdev->dev,
+				"Property 'zxic,audio-codec' missing or invalid\n");
+			return -EINVAL;
+		}
+#endif	
+	}
+	
+
+
+
+
+
+	id = of_match_device(of_match_ptr(zx29_max9867_of_match), &pdev->dev);
+	if (id)
+		*board = *((struct zx29_board_data *)id->data);
+	
+	board->name = "zx29_max9867";
+	board->dev = &pdev->dev;
+
+	//platform_set_drvdata(pdev, board);
+	s_board = board;
+
+
+#if 0
+
+	board->gpio_pwen = of_get_gpio_flags(dev->of_node, 0, &flags);
+	if (!gpio_is_valid(board->gpio_pwen)) {
+		dev_err(dev,"  gpio_pwen no found\n");
+		return -EBUSY;
+	}
+	dev_info(dev, "board->gpio_pwen=0x%x  flags = %d\n",board->gpio_pwen,flags);
+	ret = devm_gpio_request(&pdev->dev,board->gpio_pwen, "codec_pwen");
+	if (ret < 0) {
+		dev_err(dev,"gpio_pwen request error.\n");
+		return ret;
+
+	}
+
+	board->gpio_pdn = of_get_gpio_flags(dev->of_node, 1, &flags);
+	if (!gpio_is_valid(board->gpio_pdn)) {
+		dev_err(dev,"  gpio_pdn no found\n");
+		return -EBUSY;
+	}
+	dev_info(dev, "board->gpio_pdn=0x%x  flags = %d\n",board->gpio_pdn,flags);
+	ret = devm_gpio_request(&pdev->dev,board->gpio_pdn, "codec_pdn");
+	if (ret < 0) {
+		dev_err(dev,"gpio_pdn request error.\n");
+		return ret;
+
+	}
+#endif
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+	if (ret){
+		dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+	    return ret;
+	}
+	zx29_i2s_top_pin_cfg(pdev);	
+
+	
+	//codec_power_on(board,1);
+#ifdef  CONFIG_PA_SA51034
+
+	dev_info(&pdev->dev,"zx29_audio_probe start sa51034_init!\n");
+
+	ret = sa51034_init();
+	if (ret != 0) {
+
+		pr_err("sa51034_init Failed to register I2C driver: %d\n", ret);
+		//return ret;
+
+	}
+	else{
+
+		for (idx = 0; idx < ARRAY_SIZE(pa_controls); idx++) {
+			ret = snd_ctl_add(card->snd_card,
+					  snd_ctl_new1(&pa_controls[idx],
+						       NULL));
+			if (ret < 0){
+				return ret;
+			}
+		}
+
+	}
+	 ret  = 0;
+
+#endif	
+	dev_info(&pdev->dev,"zx29_audio_probe end!\n");
+
+	return ret;
+}
+
+static void zx29_audio_shutdown(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	
+	dev_info(&pdev->dev,"%s:zx29_max9867 end!\n",__func__);
+
+	return ;
+}
+static int zx29_audio_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	int ret;
+	struct device *dev = &pdev->dev;
+
+	
+	dev_info(&pdev->dev,"%s:zx29_max9867 end!\n",__func__);
+
+	return ret;
+}
+
+static int zx29_audio_resume(struct platform_device *pdev)
+{
+	int ret;
+	struct device *dev = &pdev->dev;
+
+	
+	dev_info(&pdev->dev,"%s:zx29_max9867 end!\n",__func__);
+
+	return ret;
+}
+
+
+static struct platform_driver zx29_platform_driver = {
+	.driver		= {
+		.name	= "zx29_max9867",
+		.of_match_table = of_match_ptr(zx29_max9867_of_match),
+		.pm	= &snd_soc_pm_ops,
+	},
+	.probe		= zx29_audio_probe,
+	.shutdown 	= zx29_audio_shutdown,
+	.suspend 	= zx29_audio_suspend,
+	.resume 	= zx29_audio_resume,
+};
+
+
+
+
+
+module_platform_driver(zx29_platform_driver);
+
+MODULE_DESCRIPTION("zx29 ALSA SoC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:zx29-audio-max9867");
diff --git a/upstream/linux-5.10/sound/soc/sanechips/zx29_nau8810.c b/upstream/linux-5.10/sound/soc/sanechips/zx29_nau8810.c
new file mode 100755
index 0000000..1e5777d
--- /dev/null
+++ b/upstream/linux-5.10/sound/soc/sanechips/zx29_nau8810.c
@@ -0,0 +1,2325 @@
+/*
+ * zx297520v3_nau8810.c  --  zx297520v3-nau8810 ALSA SoC Audio board driver
+ *
+ * Copyright (C) 2022, ZTE Corporation.
+ *
+ * Based on smdk_wm8994.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "../codecs/nau8810.h"
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+
+ 
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+//#include <sound/tlv.h>
+//#include <sound/soc.h>
+//#include <sound/jack.h>
+//#include <sound/zx29_snd_platform.h>
+//#include <mach/iomap.h>
+//#include <mach/board.h>
+#include <linux/of_gpio.h>
+
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+
+
+#include "i2s.h"
+
+#define ZX29_I2S_TOP_LOOP_REG	0x60
+#define NAU_CLK_ID 0
+
+#if 1
+ 
+#define  ZXIC_MCLK                    26000000
+
+#define  ZXIC_PLL_CLKIN_MCLK		  0
+
+
+#define zx_reg_sync_write(v, a) \
+        do {    \
+            iowrite32(v, a);    \
+        } while (0)
+
+#define zx_read_reg(addr) \
+    ioread32(addr)
+
+#define zx_write_reg(addr, val)   \
+	zx_reg_sync_write(val, addr)
+
+
+
+struct zx29_board_data {
+	const char *name;
+	struct device *dev;
+
+	int codec_refclk;
+	int gpio_pwen;	
+	int gpio_pdn;
+	void __iomem *sys_base_va;
+
+	struct pinctrl *p;
+	struct pinctrl_state *s;
+	struct pinctrl_state *s_sleep;
+
+};
+
+
+struct zx29_board_data *s_board = 0;
+
+//#define AON_WIFI_BT_CLK_CFG2  ((volatile unsigned int *)(ZX_TOP_CRM_BASE + 0x94))
+ /* Default ZX29s */
+static struct zx29_board_data zx29_platform_data = {
+	.codec_refclk = ZXIC_MCLK,
+};
+ static struct platform_device *zx29_snd_device;
+ 
+ static DEFINE_RAW_SPINLOCK(codec_pa_lock);
+ 
+ static int set_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol);
+ static int get_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol);
+
+
+#ifdef USE_ALSA_VOICE_FUNC
+ extern int zDrv_Audio_Printf(void *pFormat, ...);
+ extern int zDrvVp_GetVol_Wrap(void);
+ extern int zDrvVp_SetVol_Wrap(int volume);
+ extern int zDrvVp_GetPath_Wrap(void);
+ extern int zDrvVp_SetPath_Wrap(int path);
+ extern int zDrvVp_SetMute_Wrap(bool enable);
+ extern bool zDrvVp_GetMute_Wrap(void);
+ extern int zDrvVp_SetTone_Wrap(int toneNum);
+ 
+ static int vp_GetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetVol(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_GetVol(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_getTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ 
+ static int audio_GetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int audio_SetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+
+ 
+ //static const DECLARE_TLV_DB_SCALE(vp_path_tlv, 0, 300, 0);
+ 
+ static const char * const vpath_in_text[] = {
+	 "handset", "speak", "headset", "bluetooth",
+ };
+ 
+ static const char *tone_class[] = {
+	 "Lowpower", "Sms", "Callstd", "Alarm", "Calltime",
+ };
+ 
+ static const struct soc_enum vpath_in_enum =	 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(vpath_in_text), vpath_in_text); 
+ 
+ static const struct soc_enum tone_class_enum[] = {
+	 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tone_class), tone_class),
+ };
+ 
+ static const struct snd_kcontrol_new vp_snd_controls[] = {  
+	 SOC_ENUM_EXT("voice processing path select",vpath_in_enum,vp_GetPath,vp_SetPath),
+	 //SOC_SINGLE_EXT_TLV("voice processing path Volume",0, 5, 5, 0,vp_GetVol, vp_SetVol,vp_path_tlv), 
+	 SOC_SINGLE_EXT("voice processing path Volume",0, 5, 5, 0,vp_GetVol, vp_SetVol),
+	 SOC_SINGLE_EXT("voice uplink mute", 0, 1, 1, 0,vp_GetMute, vp_SetMute),
+	 SOC_ENUM_EXT("voice tone sel", tone_class_enum[0], vp_getTone, vp_SetTone),
+	 SOC_SINGLE_BOOL_EXT("path stauts dump", 0,get_path_stauts_switch, set_path_stauts_switch),
+	 SOC_ENUM_EXT("audio path select",vpath_in_enum,audio_GetPath,audio_SetPath),
+ };
+ 
+ static int curtonetype = 0;
+ static int vp_getTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 ucontrol->value.integer.value[0] = curtonetype;
+	 return 0;
+ }
+ 
+ static int vp_SetTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 int vol = 0,ret = 0, tonenum;
+	 tonenum = ucontrol->value.integer.value[0];
+	 curtonetype = tonenum;
+	 //printk("Alsa vp_SetTone tonenum=%d\n", tonenum);
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetTone_Wrap)(tonenum);
+	 if(ret < 0)
+	 {
+		 printk(KERN_ERR "vp_SetTone fail = %d\n", tonenum);
+		 return ret;
+	 }
+	 return 0;
+ }
+ 
+ static int vp_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 int enable = 0,ret = 0;
+	 enable = ucontrol->value.integer.value[0];
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetMute_Wrap)(enable);
+	 if(ret < 0)
+	 {
+	   printk(KERN_ERR "vp_SetMute fail = %d\n",enable);
+	   return ret;
+	 }
+	 return 0;
+ }
+ 
+ static int vp_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {		 
+		//ucontrol->value.integer.value[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetMute_Wrap)();
+		return 0;
+ }
+ 
+ static int vp_SetVol(struct snd_kcontrol *kcontrol,
+								struct snd_ctl_elem_value *ucontrol)
+ {
+		int vol = 0,ret = 0;
+		vol = ucontrol->value.integer.value[0];
+		//ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetVol_Wrap)(vol);
+		if(ret < 0)
+		{
+		   printk(KERN_ERR "vp_SetVol fail = %d\n",vol);
+		   return ret;
+	   }
+	 return 0;
+ }
+ static int vp_GetVol(struct snd_kcontrol *kcontrol,
+								struct snd_ctl_elem_value *ucontrol)
+ {		 
+		//ucontrol->value.integer.value[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetVol_Wrap)();
+		return 0;
+ }
+ static int vp_GetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {	 
+	 //ucontrol->value.enumerated.item[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetPath_Wrap)();
+	 return 0;
+ }
+ static int vp_SetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {
+	 int ret = 0,path = 0;
+	 unsigned long	flags;
+	 path = ucontrol->value.enumerated.item[0];
+ 
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetPath_Wrap)(path);
+	 if(ret < 0)
+	 {
+	   printk(KERN_ERR "vp_SetPath fail = %d\n",path);
+	   return ret;
+	 }
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+	 switch (path) {
+	 case 0:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+		 break;
+	 case 1:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_40, GPIO_HIGH);
+		 break;
+	 case 2:
+		 break;
+	 case 3:
+		 break;
+	 default:
+		 break;
+	 }
+#endif
+	 return 0;
+ }
+ 
+ static int curpath = 0;
+ static int audio_GetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {	 
+	 ucontrol->value.enumerated.item[0] = curpath;
+	 return 0;
+ }
+ 
+ static int audio_SetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {
+	 int ret = 0,path = 0;
+	 unsigned long	flags;
+	 
+	 path = ucontrol->value.enumerated.item[0];
+	 curpath = path;
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+	 switch (path) {
+	 case 0:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+		 break;
+	 case 1:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_40, GPIO_HIGH);
+		 break;
+	 case 2:
+		 break;
+	 case 3:
+		 break;
+	 default:
+		 break;
+	 }
+#endif
+	 return 0;
+ }
+ 
+ typedef enum
+ {
+	 VP_PATH_HANDSET	=0, 	
+	 VP_PATH_SPEAKER,		 
+	 VP_PATH_HEADSET,					  
+	 VP_PATH_BLUETOOTH, 				   
+	 VP_PATH_BLUETOOTH_NO_NR,					 
+	 VP_PATH_HSANDSPK,
+	 
+	 VP_PATH_OFF = 255, 				 
+	 
+	 MAX_VP_PATH = VP_PATH_OFF				 
+ }T_ZDrv_VpPath;
+ 
+ extern int zDrvVp_Loop(T_ZDrv_VpPath path);
+
+ 
+//#else
+ static const struct snd_kcontrol_new machine_snd_controls[] = {		 
+	 SOC_SINGLE_BOOL_EXT("path stauts dump", 0,get_path_stauts_switch, set_path_stauts_switch),
+ };
+ 
+
+ 
+ //extern int rt5670_hs_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+ 
+ int path_stauts_switch = 0;
+ static int set_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+ {
+	 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	 struct snd_soc_dapm_path *p;
+ 
+	 int path_stauts_switch = ucontrol->value.integer.value[0];
+ 
+	 
+	 if (path_stauts_switch == 1)
+	 {
+		 list_for_each_entry(p, &card->paths, list){
+			 
+		   //print_audio("Alsa	path name (%s),longname (%s),sink (%s),source (%s),connect %d \n", p->name,p->long_name,p->sink->name,p->source->name,p->connect);
+		   //printk("Alsa  path longname %s,sink %s,source %s,connect %d \n", p->long_name,p->sink->name,p->source->name,p->connect);
+ 
+		 }
+	 }
+	 return 0;
+ }
+ 
+ static int get_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+ {
+	 
+	 ucontrol->value.integer.value[0] = path_stauts_switch;
+	 return 0;
+ };
+#endif 
+ 
+#ifdef CONFIG_SND_SOC_JACK_DECTEC
+ 
+ static struct snd_soc_jack codec_headset;
+ 
+ /* Headset jack detection DAPM pins */
+ static struct snd_soc_jack_pin codec_headset_pins[] = {
+	 {
+		 .pin = "Headphone",
+		 .mask = SND_JACK_HEADPHONE,
+	 },
+ };
+ 
+#endif
+
+
+
+
+
+ static int zx29startup(struct snd_pcm_substream *substream)
+ {
+ //  int ret = 0;
+	 print_audio("Alsa	Entered func %s\n", __func__);
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx29_startup device=%d,stream=%d\n", substream->pcm->device, substream->stream);
+ 
+	 struct snd_pcm *pcmC0D0p = snd_lookup_minor_data(16, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+	 struct snd_pcm *pcmC0D1p = snd_lookup_minor_data(17, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+	 struct snd_pcm *pcmC0D2p = snd_lookup_minor_data(18, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);	 
+	 struct snd_pcm *pcmC0D3p = snd_lookup_minor_data(19, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);	
+	 if ((pcmC0D0p == NULL) || (pcmC0D1p == NULL) || (pcmC0D2p == NULL) || (pcmC0D3p == NULL))
+		 return  -EINVAL;	  
+	 if ((pcmC0D0p->streams[0].substream_opened && pcmC0D1p->streams[0].substream_opened) || 
+		 (pcmC0D0p->streams[0].substream_opened && pcmC0D2p->streams[0].substream_opened) || 
+		 (pcmC0D0p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened) || 
+		 (pcmC0D1p->streams[0].substream_opened && pcmC0D2p->streams[0].substream_opened) ||
+		 (pcmC0D1p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened) ||
+		 (pcmC0D2p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened))
+		 BUG();
+#if 0
+	 unsigned long	flags;
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 mdelay(1);  
+ 
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+	 }
+#endif
+ 
+ 
+	 return 0;
+ }
+ 
+ static void zx29_shutdown(struct snd_pcm_substream *substream)
+ {
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx297520xx_shutdown device=%d, stream=%d\n", substream->pcm->device, substream->stream);
+ //  print_audio("Alsa	Entered func %s, stream=%d\n", __func__, substream->stream);
+ 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+#endif
+	 }
+	 
+	 if (snd_soc_dai_active(cpu_dai))
+		 return;
+ 
+
+  
+ }
+ 
+ static void zx29_shutdown2(struct snd_pcm_substream *substream)
+ {
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx29_shutdown2 device=%d, stream=%d\n", substream->pcm->device, substream->stream);
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+#endif
+#ifdef USE_ALSA_VOICE_FUNC
+		 //CPPS_FUNC(cpps_callbacks, zDrvVp_Loop)(VP_PATH_OFF);
+#endif
+
+
+	 }
+ 
+	 if (snd_soc_dai_active(cpu_dai))
+		 return;
+ 
+
+ }
+ static int zx29_init_paiftx(struct snd_soc_pcm_runtime *rtd)
+ {
+	 //struct snd_soc_codec *codec = rtd->codec;
+	 //struct snd_soc_dapm_context *dapm = &codec->dapm;
+ 
+	 //snd_soc_dapm_enable_pin(dapm, "HPOL");
+	 //snd_soc_dapm_enable_pin(dapm, "HPOR");
+ 
+	 /* Other pins NC */
+ //  snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
+ 
+ //  print_audio("Alsa	Entered func %s\n", __func__);
+ 
+	 return 0;
+ }
+ static int zx29_hw_params(struct snd_pcm_substream *substream,
+										struct snd_pcm_hw_params *params)
+ {
+     print_audio("Alsa:	Entered func %s\n", __func__);
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	 int ret;
+	 int rfs = 0, frq_out = 0;	 
+	 switch (params_rate(params)) {
+	 case 8000:
+	 case 16000:
+	 case 11025:
+	 case 22050:
+	 case 24000:
+	 case 32000:
+	 case 44100:
+	 case 48000:
+		 rfs = 32;
+		 break;
+	 default:
+	 	{
+	 	    ret =  -EINVAL;
+		    print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret);	 	      
+		 	return ret;
+	 	}
+	 }
+	 
+	 frq_out = params_rate(params) * rfs * 2;
+	 
+	 /* Set the Codec DAI configuration */
+	 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+
+
+	 /* Set the AP DAI configuration */
+	 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: ap dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+
+	 ret = snd_soc_dai_set_sysclk(codec_dai, NAU8810_SCLK_PLL,  ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+	  
+
+	 
+	 /* Set the Codec DAI clk */	 
+	 ret =snd_soc_dai_set_pll(codec_dai, 0, NAU8810_SCLK_PLL,
+								  ZXIC_MCLK, params_rate(params)*256);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		 return ret;
+	}
+	
+	
+
+	  
+	 /* Set the AP DAI clk */
+	 ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+	 //ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+ 
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: cpu dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+     print_audio("Alsa:	Entered func %s end\n", __func__);
+	 
+	 return 0;
+ }
+
+static int zx29_hw_params_lp(struct snd_pcm_substream *substream,
+									   struct snd_pcm_hw_params *params)
+{
+	print_audio("Alsa: Entered func %s\n", __func__);
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	int ret;
+	int rfs = 0, frq_out = 0;	
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+	case 11025:
+	case 22050:
+	case 24000:
+	case 32000:
+	case 44100:
+	case 48000:
+		rfs = 32;
+		break;
+	default:
+	   {
+		   ret =  -EINVAL;
+		   print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret); 			 
+		   return ret;
+	   }
+	}
+	
+	frq_out = params_rate(params) * rfs * 2;
+	
+	/* Set the Codec DAI configuration */
+	/*
+	
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							  | SND_SOC_DAIFMT_NB_NF
+							  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0){
+	   
+		print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		return ret;
+	}
+	*/ 
+
+	
+	/* Set the AP DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+							  | SND_SOC_DAIFMT_NB_NF
+							  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0){
+	   
+		print_audio("Alsa: ap dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		return ret;
+	}
+
+	/* Set the Codec DAI clk */ 	
+	/*ret =snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_BCLK1,
+								 fs*datawidth*2, 256*fs);
+	if (ret < 0){
+	   
+		print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		return ret;
+   }
+	*/
+	/*
+	ret = snd_soc_dai_set_sysclk(codec_dai, ES8312_CLKID_MCLK,ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	if (ret < 0){	   
+		print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		return ret;
+	}
+	*/
+	/* Set the AP DAI clk */
+	ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+
+	if (ret < 0){	   
+		print_audio("Alsa: cpu dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		return ret;
+	}
+	print_audio("Alsa: Entered func %s end\n", __func__);
+	
+	return 0;
+}
+
+
+ 
+
+ 
+
+ static int zx29_hw_params_voice(struct snd_pcm_substream *substream,
+										struct snd_pcm_hw_params *params)
+ {
+	 print_audio("Alsa: Entered func %s\n", __func__);
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	 int ret;
+	 int rfs = 0, frq_out = 0;	 
+	 switch (params_rate(params)) {
+	 case 8000:
+	 case 16000:
+	 case 11025:
+	 case 22050:
+	 case 24000:
+	 case 32000:
+	 case 44100:
+	 case 48000:
+		 rfs = 32;
+		 break;
+	 default:
+		{
+			ret =  -EINVAL;
+			print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret);			  
+			return ret;
+		}
+	 }
+	 
+	 frq_out = params_rate(params) * rfs * 2;
+	 
+	 /* Set the Codec DAI configuration */
+	 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+		
+		 print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+ 
+ 	 ret = snd_soc_dai_set_sysclk(codec_dai, NAU8810_SCLK_MCLK,  ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+	
+	  ret = snd_soc_dai_set_sysclk(codec_dai, NAU8810_SCLK_PLL,  ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	  if (ret < 0){ 	 
+		  print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		  return ret;
+	  }
+	   
+	 
+	  
+	  /* Set the Codec DAI clk */	  
+	  ret =snd_soc_dai_set_pll(codec_dai, 0, NAU8810_SCLK_PLL,
+								   ZXIC_MCLK, params_rate(params)*256);
+	  if (ret < 0){
+		 
+		  print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		  return ret;
+	 }
+
+
+	 print_audio("Alsa: Entered func %s end\n", __func__);
+	 
+	 return 0;
+ }
+
+ static int zx29_hw_params_tdm(struct snd_pcm_substream *substream,
+										struct snd_pcm_hw_params *params)
+ {
+     print_audio("Alsa:	Entered func %s\n", __func__);
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	 int ret;
+	 int rfs = 0, frq_out = 0;	 
+	 switch (params_rate(params)) {
+	 case 8000:
+	 case 16000:
+	 case 11025:
+	 case 22050:
+	 case 24000:
+	 case 32000:
+	 case 44100:
+	 case 48000:
+		 rfs = 32;
+		 break;
+	 default:
+	 	{
+	 	    ret =  -EINVAL;
+		    print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret);	 	      
+		 	return ret;
+	 	}
+	 }
+	 
+	 //frq_out = params_rate(params) * rfs * 2;
+	 
+	 /* Set the Codec DAI configuration */
+	 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+
+
+	 /* Set the AP DAI configuration */
+	 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: ap dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+
+	 ret = snd_soc_dai_set_sysclk(codec_dai, NAU8810_SCLK_MCLK,  params_rate(params)*256, SND_SOC_CLOCK_IN);
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+	  
+
+
+	  
+	 /* Set the AP DAI clk */
+	 //ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+	 ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_104M, SND_SOC_CLOCK_IN);
+	 //ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_122M88, SND_SOC_CLOCK_IN);
+ 
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: cpu dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+     print_audio("Alsa:	Entered func %s end\n", __func__);
+	 
+	 return 0;
+ }
+
+static int zx29_hw_params_lp_tdm(struct snd_pcm_substream *substream,
+									   struct snd_pcm_hw_params *params)
+{
+	print_audio("Alsa: Entered func %s\n", __func__);
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	int ret;
+	int rfs = 0, frq_out = 0;	
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+	case 11025:
+	case 22050:
+	case 24000:
+	case 32000:
+	case 44100:
+	case 48000:
+		rfs = 32;
+		break;
+	default:
+	   {
+		   ret =  -EINVAL;
+		   print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret); 			 
+		   return ret;
+	   }
+	}
+	
+	//frq_out = params_rate(params) * rfs * 2;
+	
+	/* Set the Codec DAI configuration */
+	/*
+	
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							  | SND_SOC_DAIFMT_NB_NF
+							  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0){
+	   
+		print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		return ret;
+	}
+	*/ 
+
+	
+	/* Set the AP DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A
+							  | SND_SOC_DAIFMT_NB_NF
+							  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0){
+	   
+		print_audio("Alsa: ap dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		return ret;
+	}
+
+	/* Set the Codec DAI clk */ 	
+	/*ret =snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_BCLK1,
+								 fs*datawidth*2, 256*fs);
+	if (ret < 0){
+	   
+		print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		return ret;
+   }
+	*/
+	/*
+	ret = snd_soc_dai_set_sysclk(codec_dai, ES8312_CLKID_MCLK,ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	if (ret < 0){	   
+		print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		return ret;
+	}
+	*/
+	/* Set the AP DAI clk */
+	//ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+	ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_104M, SND_SOC_CLOCK_IN);
+	
+	//ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_122M88, SND_SOC_CLOCK_IN);	
+
+	if (ret < 0){	   
+		print_audio("Alsa: cpu dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		return ret;
+	}
+	print_audio("Alsa: Entered func %s end\n", __func__);
+	
+	return 0;
+}
+
+
+ 
+
+ 
+
+ static int zx29_hw_params_voice_tdm(struct snd_pcm_substream *substream,
+										struct snd_pcm_hw_params *params)
+ {
+	 print_audio("Alsa: Entered func %s\n", __func__);
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	 int ret;
+	 int rfs = 0, frq_out = 0;	 
+	 switch (params_rate(params)) {
+	 case 8000:
+	 case 16000:
+	 case 11025:
+	 case 22050:
+	 case 24000:
+	 case 32000:
+	 case 44100:
+	 case 48000:
+		 rfs = 32;
+		 break;
+	 default:
+		{
+			ret =  -EINVAL;
+			print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret);			  
+			return ret;
+		}
+	 }
+	 
+	 frq_out = params_rate(params) * rfs * 2;
+	 
+	 /* Set the Codec DAI configuration */
+	 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+		
+		 print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+ 
+	
+	  //ret = snd_soc_dai_set_sysclk(codec_dai, NAU8810_SCLK_PLL,  ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	 
+	  ret = snd_soc_dai_set_sysclk(codec_dai, NAU8810_SCLK_MCLK,  params_rate(params)*256, SND_SOC_CLOCK_IN);
+	  if (ret < 0){ 	 
+		  print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		  return ret;
+	  }
+	
+	  
+	
+
+
+	 print_audio("Alsa: Entered func %s end\n", __func__);
+	 
+	 return 0;
+ }										 
+ int zx29_prepare2(struct snd_pcm_substream *substream)
+ {
+	 int path, ret;
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_Loop)(VP_PATH_SPEAKER);
+		 if (ret < 0)
+			 return -1;
+	 }
+	 
+	 return 0;
+ } 
+ 
+ static int zx29_late_probe(struct snd_soc_card *card)
+ {
+	 //struct snd_soc_codec *codec = card->rtd[0].codec;
+	 //struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+	 int ret;
+ //  print_audio("Alsa	zx29_late_probe entry!\n");
+ 
+#ifdef CONFIG_SND_SOC_JACK_DECTEC
+	 
+	 ret = snd_soc_jack_new(codec, "Headset",
+							SND_JACK_HEADSET |SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
+							&codec_headset);
+	 if (ret)
+		 return ret;
+ 
+	 ret = snd_soc_jack_add_pins(&codec_headset,
+								 ARRAY_SIZE(codec_headset_pins),
+								 codec_headset_pins);
+	 if (ret)
+		 return ret;
+       #ifdef CONFIG_SND_SOC_codec
+	 //rt5670_hs_detect(codec, &codec_headset);
+       #endif
+#endif
+ 
+	 return 0;
+ }
+ 
+ static struct snd_soc_ops zx29_ops = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+#ifdef CONFIG_USE_TOP_TDM	
+	.hw_params = zx29_hw_params_tdm,
+#else
+	.hw_params = zx29_hw_params,
+#endif	 
+	 
+ };
+  static struct snd_soc_ops zx29_ops_lp = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,	 
+#ifdef CONFIG_USE_TOP_TDM	
+	.hw_params = zx29_hw_params_lp_tdm,
+#else
+	.hw_params = zx29_hw_params_lp,
+#endif		 
+ };
+ static struct snd_soc_ops zx29_ops1 = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 //.hw_params = zx29_hw_params1,
+ };
+ 
+ static struct snd_soc_ops zx29_ops2 = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown2,
+	 //.hw_params = zx29_hw_params1,
+	 .prepare = zx29_prepare2,
+ };
+ static struct snd_soc_ops voice_ops = {
+	 .startup = zx29startup,
+	 .shutdown = zx29_shutdown2,
+#ifdef CONFIG_USE_TOP_TDM	
+	.hw_params = zx29_hw_params_voice_tdm,
+#else
+	.hw_params = zx29_hw_params_voice,
+#endif	 
+	 
+	 //.prepare = zx29_prepare2,
+ };
+
+ 
+ enum {
+	 MERR_DPCM_AUDIO = 0,
+	 MERR_DPCM_DEEP_BUFFER,
+	 MERR_DPCM_COMPR,
+ };
+
+ 
+#if 0
+ 
+ static struct snd_soc_card zxic_soc_card = {
+	 .name = "zx29_nau8810",
+	 .owner = THIS_MODULE,
+	 .dai_link = &zxic_dai_link,
+	 .num_links = ARRAY_SIZE(zxic_dai_link),
+#ifdef USE_ALSA_VOICE_FUNC
+	 .controls = vp_snd_controls,
+	 .num_controls = ARRAY_SIZE(vp_snd_controls),
+#endif
+ 
+ //  .late_probe = zx29_late_probe,
+	 
+ };
+#endif 
+ 
+ static int zx29_setup_pins(struct zx29_board_data *codec_pins, char *fun)
+ {
+	 int ret;
+ 
+	 //ret = gpio_request(codec_pins->codec_refclk, "codec_refclk");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio: %s pin already in use\n", fun);
+		 return ret;
+	 }
+	 //zx29_gpio_config(codec_pins->codec_refclk, GPIO17_CLK_OUT2);
+ 
+#ifdef  _USE_7520V3_PHONE_TYPE_C31F
+	 ret = gpio_request_one(ZX29_GPIO_39, GPIOF_OUT_INIT_LOW, "codec_pa");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio:  codec_pa in use\n");
+		 return ret;
+	 }
+	 
+	 ret = gpio_request_one(ZX29_GPIO_40, GPIOF_OUT_INIT_LOW, "codec_sw");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio:  codec_sw in use\n");
+		 return ret;
+	 }
+#endif
+ 
+	 return 0;
+ }
+#endif
+
+ 
+ static int zx29_remove(struct platform_device *pdev)
+ {
+	 gpio_free(zx29_platform_data.codec_refclk);
+	 platform_device_unregister(zx29_snd_device);
+	 return 0;
+ }
+ 
+
+ 
+#if  0
+
+ /*
+  * Default CFG switch settings to use this driver:
+  *	ZX29
+  */
+
+ /*
+  * Configure audio route as :-
+  * $ amixer sset 'DAC1' on,on
+  * $ amixer sset 'Right Headphone Mux' 'DAC'
+  * $ amixer sset 'Left Headphone Mux' 'DAC'
+  * $ amixer sset 'DAC1R Mixer AIF1.1' on
+  * $ amixer sset 'DAC1L Mixer AIF1.1' on
+  * $ amixer sset 'IN2L' on
+  * $ amixer sset 'IN2L PGA IN2LN' on
+  * $ amixer sset 'MIXINL IN2L' on
+  * $ amixer sset 'AIF1ADC1L Mixer ADC/DMIC' on
+  * $ amixer sset 'IN2R' on
+  * $ amixer sset 'IN2R PGA IN2RN' on
+  * $ amixer sset 'MIXINR IN2R' on
+  * $ amixer sset 'AIF1ADC1R Mixer ADC/DMIC' on
+  */
+
+/* ZX29 has a 16.934MHZ crystal attached to nau8810 */
+#define ZX29_CODEC_FREQ 16934000
+
+
+
+
+
+static int zx29_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	unsigned int pll_out;
+	int ret;
+
+	/* AIF1CLK should be >=3MHz for optimal performance */
+	if (params_width(params) == 24)
+		pll_out = params_rate(params) * 384;
+	else if (params_rate(params) == 8000 || params_rate(params) == 11025)
+		pll_out = params_rate(params) * 512;
+	else
+		pll_out = params_rate(params) * 256;
+
+	ret = snd_soc_dai_set_pll(codec_dai, AK4940_FLL1, AK4940_FLL_SRC_MCLK1,
+					ZX29_AK4940_FREQ, pll_out);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, AK4940_SYSCLK_FLL1,
+					pll_out, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * ZX29 AK4940 DAI operations.
+ */
+static struct snd_soc_ops zx29_ops = {
+	.hw_params = smdk_hw_params,
+};
+
+static int zx29_codec_init_paiftx(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
+
+	/* Other pins NC */
+	snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
+	snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTLN");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTLP");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTRP");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTRN");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
+	snd_soc_dapm_nc_pin(dapm, "IN1LP");
+	snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
+	snd_soc_dapm_nc_pin(dapm, "IN1RP");
+	snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
+
+	return 0;
+}
+#endif
+
+
+
+
+enum {
+	AUDIO_DL_MEDIA = 0,
+	AUDIO_DL_VOICE,
+	AUDIO_DL_2G_AND_3G_VOICE,
+	AUDIO_DL_VP_LOOP,	
+	AUDIO_DL_3G_VOICE,
+	
+	AUDIO_DL_MAX,
+};
+SND_SOC_DAILINK_DEF(dummy, \
+	DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+//SND_SOC_DAILINK_DEF(cpu_i2s0, \
+//	DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+SND_SOC_DAILINK_DEF(cpu_i2s0, \
+	DAILINK_COMP_ARRAY(COMP_CPU("1405000.i2s")));
+
+SND_SOC_DAILINK_DEF(cpu_tdm, \
+	DAILINK_COMP_ARRAY(COMP_CPU("1412000.tdm")));
+
+
+SND_SOC_DAILINK_DEF(voice_cpu, \
+	DAILINK_COMP_ARRAY(COMP_CPU("soc:voice_audio")));
+
+SND_SOC_DAILINK_DEF(voice_2g_3g, \
+	DAILINK_COMP_ARRAY(COMP_CPU("voice_2g_3g-dai")));
+
+SND_SOC_DAILINK_DEF(voice_3g, \
+		DAILINK_COMP_ARRAY(COMP_CPU("voice_3g-dai")));
+
+
+
+//SND_SOC_DAILINK_DEF(nau8810, \
+//	DAILINK_COMP_ARRAY(COMP_CODEC("nau8810.1-0012", "nau8810-aif")));
+SND_SOC_DAILINK_DEF(dummy_cpu, \
+		DAILINK_COMP_ARRAY(COMP_CPU("soc:zx29_snd_dummy")));
+//SND_SOC_DAILINK_DEF(dummy_platform, \
+//	DAILINK_COMP_ARRAY(COMP_PLATFORM("soc:zx29_snd_dummy")));
+
+SND_SOC_DAILINK_DEF(dummy_codec, \
+		DAILINK_COMP_ARRAY(COMP_CODEC("soc:zx29_snd_dummy", "zx29_snd_dummy_dai")));
+SND_SOC_DAILINK_DEF(nau8810_codec, \
+		DAILINK_COMP_ARRAY(COMP_CODEC("nau8810.1-001a", "nau8810-hifi")));
+
+//SND_SOC_DAILINK_DEF(media_platform, \
+//	DAILINK_COMP_ARRAY(COMP_PLATFORM("zx29-pcm-audio")));
+SND_SOC_DAILINK_DEF(media_platform, \
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("1405000.i2s")));
+
+	SND_SOC_DAILINK_DEF(media_platform_tdm, \
+		DAILINK_COMP_ARRAY(COMP_PLATFORM("1412000.tdm")));
+
+//SND_SOC_DAILINK_DEF(voice_cpu, \
+//	DAILINK_COMP_ARRAY(COMP_CPU("E1D02000.i2s")));
+
+SND_SOC_DAILINK_DEF(voice_platform, \
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("soc:voice_audio")));
+
+
+
+			
+//static struct snd_soc_dai_link zx29_dai_link[] = {
+struct snd_soc_dai_link zx29_dai_link[] = {
+ {
+	.name = "zx29_snd_dummy",//codec name
+	.stream_name = "zx29_snd_dumy",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &zx29_ops_lp,
+	.init = zx29_init_paiftx,
+#ifdef CONFIG_USE_TOP_TDM	
+	SND_SOC_DAILINK_REG(cpu_tdm, dummy_codec, media_platform_tdm),
+#else
+	SND_SOC_DAILINK_REG(cpu_i2s0, dummy_codec, media_platform),
+
+#endif
+	
+},
+#if 1
+{
+	.name = "media",//codec name
+	.stream_name = "MultiMedia",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &zx29_ops,
+
+ 	.init = zx29_init_paiftx,
+	
+#ifdef CONFIG_USE_TOP_TDM	
+	SND_SOC_DAILINK_REG(cpu_tdm, nau8810_codec, media_platform_tdm),
+#else
+	SND_SOC_DAILINK_REG(cpu_i2s0, nau8810_codec, media_platform),
+#endif
+
+},
+{
+	.name = "voice",//codec name
+	.stream_name = "voice",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	.init = zx29_init_paiftx,
+	
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, nau8810_codec, voice_platform),
+
+},
+{
+	.name = "voice_2g3g_teak",//codec name
+	.stream_name = "voice_2g3g_teak",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, nau8810_codec, voice_platform),
+
+},
+
+{
+	.name = "voice_3g",//codec name
+	.stream_name = "voice_3g",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, nau8810_codec, voice_platform),
+
+},
+
+{
+	.name = "loop_test",//codec name
+	.stream_name = "loop_test",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	//.ops = &zx29_ops,
+	.ops = &voice_ops,
+
+	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, nau8810_codec, dummy),
+
+},
+#endif
+
+};
+
+
+
+
+
+static struct snd_soc_card zx29_soc_card = {
+	.name = "zx29-sound-card",
+	.owner = THIS_MODULE,
+	.dai_link = zx29_dai_link,
+	.num_links = ARRAY_SIZE(zx29_dai_link),
+#ifdef USE_ALSA_VOICE_FUNC
+	 .controls = vp_snd_controls,
+	 .num_controls = ARRAY_SIZE(vp_snd_controls),
+#endif	
+};
+
+static const struct of_device_id zx29_nau8810_of_match[] = {
+	{ .compatible = "zxic,zx29_nau8810", .data = &zx29_platform_data },
+	{},
+};
+MODULE_DEVICE_TABLE(of, zx29_nau8810_of_match);
+
+static void zx29_i2s_top_pin_cfg(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct pinctrl *p;
+	struct pinctrl_state *s;
+	struct pinctrl_state *s_sleep;
+	int ret = 0;
+	printk("%s start \n",__func__);
+
+	struct resource *res;
+	void __iomem	*reg_base;
+	unsigned int val;
+	
+	struct zx29_board_data *info = s_board;
+
+	pr_info("%s: board name(%s)!\n", __func__,info->name); 
+
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "soc_sys");
+	if (!res) {
+		dev_err(dev, "Reg region missing (%s)\n", "soc_sys");
+		//return -ENXIO;
+	}
+
+	#if 0
+	reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(reg_base )) {
+			dev_err(dev, "Reg region ioremap (%s) err=%li\n", "soc_sys",PTR_ERR(reg_base ));
+		//return PTR_ERR(reg_base );
+	}
+
+	#else
+	reg_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	#endif
+	 
+//#if 1 //CONFIG_USE_PIN_I2S0
+#if defined(CONFIG_USE_TOP_I2S0)
+
+	dev_info(dev, "%s: arm i2s1 to top i2s0!!\n", __func__); 
+	//9300
+		 
+	//top i2s1 cfg
+	val = zx_read_reg(reg_base+ZX29_I2S_TOP_LOOP_REG);
+	val &= ~(0x7<<0);
+	val |= 0x1<<0; //	inter arm_i2s1--top i2s1
+	zx_write_reg(reg_base+ZX29_I2S_TOP_LOOP_REG, val);
+#elif defined(CONFIG_USE_TOP_I2S1)//defined(CONFIG_USE_PIN_I2S1)
+    //8501evb    	
+
+	dev_info(dev, "%s: arm i2s1 to top i2s1!\n", __func__); 
+			 
+	//top i2s2 cfg
+	val = zx_read_reg(reg_base+ZX29_I2S_TOP_LOOP_REG);
+	//val &= 0xfffffff8;
+	val &= ~(0x7<<16);	
+	val |= 0x1<<16;//	inter arm_i2s1--top i2s2
+	zx_write_reg(reg_base+ZX29_I2S_TOP_LOOP_REG, val);
+#endif
+
+	p = devm_pinctrl_get(dev);
+	if (IS_ERR(p)) {
+		dev_err(dev, "%s: pinctrl get failure ,p=0x%llx,dev=0x%llx!!\n", __func__,p,dev);
+		return;
+	}
+	
+	dev_info(dev, "%s: get pinctrl ,p=0x%llx,dev=0x%llx!!\n", __func__,p,dev); 
+#if defined(CONFIG_USE_TOP_I2S0)
+	dev_info(dev, "%s: top_i2s0 pinctrl sel!!\n", __func__); 
+
+	s = pinctrl_lookup_state(p, "top_i2s0");
+	if (IS_ERR(s)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}
+
+	dev_info(dev, "%s: get  top_i2s sleep pinctrl sel!!\n", __func__); 
+
+	s_sleep = pinctrl_lookup_state(p, "topi2s0_sleep");
+	if (IS_ERR(s_sleep)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}
+
+	
+	
+#elif defined(CONFIG_USE_TOP_I2S1)
+	dev_info(dev, "%s: top_i2s1 pinctrl sel!!\n", __func__); 
+
+	s = pinctrl_lookup_state(p, "top_i2s1");
+	if (IS_ERR(s)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}
+	dev_info(dev, "%s: get  top_i2s sleep pinctrl sel!!\n", __func__); 
+
+	s_sleep = pinctrl_lookup_state(p, "topi2s1_sleep");
+	if (IS_ERR(s_sleep)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}	
+
+#elif defined(CONFIG_USE_TOP_TDM)
+	dev_info(dev, "%s: top_tdm pinctrl sel!!\n", __func__); 
+	s = pinctrl_lookup_state(p, "top_tdm");
+	if (IS_ERR(s)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}
+	dev_info(dev, "%s: get  top_i2s sleep pinctrl sel!!\n", __func__); 
+
+	s_sleep = pinctrl_lookup_state(p, "toptdm_sleep");
+	if (IS_ERR(s_sleep)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}
+
+#else
+	dev_info(dev, "%s: default top_i2s pinctrl sel!!\n", __func__); 
+
+	s = pinctrl_lookup_state(p, "top_i2s0");
+	if (IS_ERR(s)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}
+
+	dev_info(dev, "%s: get  top_i2s sleep pinctrl sel!!\n", __func__); 
+
+	s_sleep = pinctrl_lookup_state(p, "topi2s0_sleep");
+	if (IS_ERR(s_sleep)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}
+
+#endif
+	if(info != NULL){
+
+		info->p = p;
+		info->s = s;
+		info->s_sleep = s_sleep;
+	}
+
+	ret = pinctrl_select_state(p, s);
+	if (ret < 0) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " select state failure!!\n");
+		return;
+	}
+	dev_info(dev, "%s: set pinctrl end!\n", __func__);	
+
+}
+
+
+#ifdef  CONFIG_PA_SA51034
+//sa51034
+#define SA51034_DEBUG
+
+#define SA51034_01_LATCHED_FAULT		0x01
+#define SA51034_02_STATUS_LOAD_DIAGNOSTIC      0x02
+#define SA51034_03_CONTROL			0x03
+#define SA51034_MAX_REGISTER SA51034_03_CONTROL
+
+struct sa51034_priv {
+	struct i2c_client *i2c;
+	struct regmap *regmap;
+	int pwen_gpio;//add new
+	int mute_gpio;
+	int fs;
+
+};
+static int sa51034_set_mute(struct sa51034_priv *sa51034,int mute);
+static int sa51034_get_mute(struct sa51034_priv *sa51034,int *mute); 
+
+
+
+
+struct sa51034_priv *g_sa51034 = NULL;
+/* ak4940 register cache & default register settings */
+static const struct reg_default sa51034_reg[] = {
+	{ 0x01, 0x00 },  /* SA51034_00_LATCHED_FAULT	*/
+	{ 0x02, 0x00 },  /* SA51034_01_STATUS_LOAD_DIAGNOSTIC	*/
+	{ 0x03, 0x00 },  /* SA51034_02_CONTROL			*/
+	
+};
+	
+static const char * const pa_gain_select_texts[] = {
+	"20dB", "26dB","30dB", "36dB",
+};
+static const char * const power_limit_select_texts[] = {
+	"PL-5V", "PL-5.9V","PL-7V", "PL-8.4V","PL-9.8V", "PL-11.8V","PL-14V", "PL-disV",
+};
+
+static const struct soc_enum pa_gain_enum[] = {
+	SOC_ENUM_SINGLE(SA51034_03_CONTROL, 6,
+	ARRAY_SIZE(pa_gain_select_texts), pa_gain_select_texts),
+};
+static const struct soc_enum power_limit_enum[] = {
+	SOC_ENUM_SINGLE(SA51034_03_CONTROL, 3,
+	ARRAY_SIZE(power_limit_select_texts), power_limit_select_texts),
+};
+	
+static const char * const reg_select[] = {
+	"read PA Reg 01:03",
+};
+
+static const struct soc_enum pa_enum2[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(reg_select),reg_select),
+};
+
+static int get_reg(
+	struct snd_kcontrol       *kcontrol,
+	struct snd_ctl_elem_value  *ucontrol)
+{
+	struct snd_soc_component *component; 
+	struct device *dev;    
+
+	
+
+	u32    currMode = ucontrol->value.enumerated.item[0];
+	int    i, ret;
+	int	   regs, rege;
+	unsigned int value;
+
+
+	if(g_sa51034 == NULL){
+	   pr_err("g_sa51034 null return %s\n", __func__);	  
+	   return -1;
+	}
+	dev = &g_sa51034->i2c->dev; 
+
+	component =  snd_soc_lookup_component(dev, NULL); 	
+	regs = 0x1;
+	rege = 0x4;
+
+	for (i = regs; i < rege; i++) {
+		value = snd_soc_component_read(component, i);
+		if (value < 0) {
+			pr_err("pa %s(%d),err value=%d\n", __func__, __LINE__, value);
+			return value;
+		}
+		pr_info("pa 2c_read Addr,Reg=(%x, %x)\n", i, value);
+	}
+	
+	return 0;
+}
+
+
+
+  int pa_get_enum_double(struct snd_kcontrol *kcontrol,
+	  struct snd_ctl_elem_value *ucontrol)
+  {
+	  //struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+  
+	  struct snd_soc_component *component; 
+	  struct device *dev;	 
+
+	  
+
+	  
+	  struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	  unsigned int val, item;
+	  unsigned int reg_val;
+	  int ret;
+	  if(g_sa51034 == NULL){
+ 	  	 pr_err("g_sa51034 null return %s\n", __func__);	
+ 		 return -1;
+	  }
+	  dev = &g_sa51034->i2c->dev; 
+
+	  
+	  component = snd_soc_lookup_component(dev, NULL);  
+	  reg_val = snd_soc_component_read(component, e->reg);
+
+
+	  if (reg_val < 0) {
+	  	  pr_err("pa %s(%d),err reg_val=%d\n", __func__, __LINE__, reg_val);
+		  return reg_val;
+	  }
+
+	  
+	  val = (reg_val >> e->shift_l) & e->mask;
+	  item = snd_soc_enum_val_to_item(e, val);
+	  ucontrol->value.enumerated.item[0] = item;
+	  if (e->shift_l != e->shift_r) {
+		  val = (reg_val >> e->shift_r) & e->mask;
+		  item = snd_soc_enum_val_to_item(e, val);
+		  ucontrol->value.enumerated.item[1] = item;
+	  }
+  
+	  return 0;
+  }
+
+  int pa_put_enum_double(struct snd_kcontrol *kcontrol,
+	  struct snd_ctl_elem_value *ucontrol)
+  {
+	  //struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	  
+	  struct snd_soc_component *component; 
+	  struct device *dev;	 
+	  struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	  unsigned int *item = ucontrol->value.enumerated.item;
+	  unsigned int val;
+	  unsigned int mask;
+
+	  if(g_sa51034 == NULL){
+ 	  	 pr_err("g_sa51034 null return %s\n", __func__);	
+ 		 return -1;
+	  }
+	  dev = &g_sa51034->i2c->dev; 
+	  component = snd_soc_lookup_component(dev, NULL);  
+  
+	  if (item[0] >= e->items)
+		  return -EINVAL;
+	  val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+	  mask = e->mask << e->shift_l;
+	  if (e->shift_l != e->shift_r) {
+		  if (item[1] >= e->items)
+			  return -EINVAL;
+		  val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
+		  mask |= e->mask << e->shift_r;
+	  }
+  
+	  return snd_soc_component_update_bits(component, e->reg, mask, val);
+  }
+
+
+static int pa_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	  int mute = 0,ret = 0;
+	  
+
+
+	  if(g_sa51034 == NULL){
+ 	  	 pr_err("g_sa51034 null return %s\n", __func__);	
+ 		 return -1;
+	  }	  
+	  mute = ucontrol->value.integer.value[0];
+	  ret = sa51034_set_mute(g_sa51034,mute);
+	  
+	  if(ret < 0)
+	  {
+		printk(KERN_ERR "sa51034_set_mute fail ret=%d,mute=%d\n",ret,mute);
+		return ret;
+	  }
+	  return 0;
+}
+
+static int pa_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{ 	
+	int mute = 0,ret = 0;
+	
+	if(g_sa51034 == NULL){
+		pr_err("g_sa51034 null return %s\n", __func__);    
+		return -1;
+	}
+	ret = sa51034_get_mute(g_sa51034,&mute);
+	
+	if(ret < 0)
+	{
+	  printk(KERN_ERR "sa51034_get_mute fail ret= %d\n",ret);
+	  return ret;
+	}
+	pr_info("[SA51034] %s mute gpio val=%d,integer.value[0]=%d\n", __func__, mute,ucontrol->value.integer.value[0]);
+
+	ucontrol->value.integer.value[0] = mute;
+
+	return 0;
+}
+
+
+
+
+
+const struct snd_kcontrol_new pa_controls[] =
+{
+	SOC_ENUM_EXT("PA gain", pa_gain_enum[0], pa_get_enum_double, pa_put_enum_double),
+    SOC_ENUM_EXT("Power limit", power_limit_enum[0], pa_get_enum_double, pa_put_enum_double),
+	SOC_ENUM_EXT("PA Reg Read", pa_enum2[0], get_reg, NULL),
+	SOC_SINGLE_EXT("pa mute", 0, 0, 1, 0,pa_GetMute, pa_SetMute),
+
+
+};
+	
+int pa_controls_size = sizeof(pa_controls) / sizeof(pa_controls[0]);
+
+
+
+
+static bool sa51034_volatile(struct device *dev, unsigned int reg)
+{
+	bool ret;
+
+#ifdef SA51034_DEBUG
+	ret = true;
+#else
+	ret = false;
+#endif
+
+	return ret;
+}
+
+static bool sa51034_readable(struct device *dev, unsigned int reg)
+{
+	if (reg <= SA51034_MAX_REGISTER)
+		return true;
+	else
+		return false;
+}
+
+static bool sa51034_writeable(struct device *dev, unsigned int reg)
+{
+	if (reg <= SA51034_MAX_REGISTER)
+		return true;
+	else
+		return false;
+}
+
+
+static const struct regmap_config sa51034_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = SA51034_MAX_REGISTER,
+	.volatile_reg = sa51034_volatile,
+	.writeable_reg = sa51034_writeable,
+	.readable_reg = sa51034_readable,
+
+	.reg_defaults = sa51034_reg,
+	.num_reg_defaults = ARRAY_SIZE(sa51034_reg),
+	.cache_type = REGCACHE_RBTREE,
+
+};
+
+static const struct snd_soc_component_driver pa_asoc_component = {
+	.name = "pa_component",
+
+
+	//.controls = pa_controls,
+	//.num_controls = ARRAY_SIZE(pa_controls),
+
+
+};
+
+static const struct of_device_id sa51034_i2c_dt_ids[] = {
+	{ .compatible = "sa51034"},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sa51034_i2c_dt_ids);
+static int sa51034_gpio_request(struct sa51034_priv *sa51034)
+{
+	struct device *dev;
+	struct device_node *np;
+    int ret;
+	dev = &(sa51034->i2c->dev);
+
+	np = dev->of_node;
+
+	if (!np)
+		return 0;
+
+	pr_info( "Read PDN pin from device tree\n");
+
+
+	sa51034->pwen_gpio = of_get_named_gpio(np, "sa51034,ctrl-gpio", 0);
+	if (sa51034->pwen_gpio < 0) {
+	    pr_err(  "sa51034 pwen pin of_get_named_gpio fail\n");
+		
+		sa51034->pwen_gpio = -1;
+		return -1;
+	}
+
+	if (!gpio_is_valid(sa51034->pwen_gpio)) {
+		pr_err(  "sa51034 pwen_gpio pin(%u) is invalid\n", sa51034->pwen_gpio);
+		sa51034->pwen_gpio = -1;
+		return -1;
+	}
+	sa51034->mute_gpio = of_get_named_gpio(np, "sa51034,ctrl-gpio", 1);
+	if (sa51034->mute_gpio < 0) {
+		
+	    pr_err(  "sa51034 mute_gpio pin of_get_named_gpio fail\n");
+		sa51034->mute_gpio = -1;
+		return -1;
+	}
+
+	if (!gpio_is_valid(sa51034->mute_gpio)) {
+		pr_err(  "sa51034 mute_gpio pin(%u) is invalid\n", sa51034->mute_gpio);
+		sa51034->mute_gpio = -1;
+		return -1;
+	}
+
+	
+	pr_info( "sa51034 get pwen_gpio pin(%u) mute_gpio pin(%u)\n", sa51034->pwen_gpio,sa51034->mute_gpio);
+
+	if (sa51034->pwen_gpio != -1) {
+		ret = devm_gpio_request(dev,sa51034->pwen_gpio, "sa51034 pwen");
+		if (ret < 0){
+			pr_err(  "sa51034 pwen_gpio request fail,ret=%d\n",ret);
+			return ret;			
+		}
+		pr_info("\t[sa51034] %s :pwen_gpio gpio_request ret = %d\n", __func__, ret);
+		gpio_direction_output(sa51034->pwen_gpio, 0);
+	}
+
+	
+	if (sa51034->mute_gpio != -1) {
+		ret = devm_gpio_request(dev,sa51034->mute_gpio, "sa51034 mute");
+		if (ret < 0){
+			pr_err(  "sa51034 mute_gpio request fail,ret=%d\n",ret);
+			return ret;			
+		}
+
+		pr_info("\t[AK4940] %s : mute_gpio gpio_request ret = %d\n", __func__, ret);
+		gpio_direction_output(sa51034->mute_gpio, 1);
+	}
+  
+	
+	return 0;
+}
+
+static int sa51034_set_mute(struct sa51034_priv *sa51034,int mute) 
+{
+	//struct snd_soc_component *component = dai->component;
+	//struct ak4940_priv *ak4940 = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+	//int ndt;
+
+	pr_info("[SA51034] %s mute=%d\n", __func__, mute);
+	if (sa51034->mute_gpio == -1) {
+			pr_err(  "sa51034 %s mute_gpio invalid return\n",__func__);
+			return -1;	
+	}
+
+	//ndt = 4080000 / sa51034->fs;
+	if (mute) {
+		/* SMUTE: 1 , MUTE */
+		ret = gpio_direction_output(sa51034->mute_gpio, 1);
+		//mdelay(ndt);
+	} else{
+		/* SMUTE:  0  ,NORMAL operation */
+		ret = gpio_direction_output(sa51034->mute_gpio, 0);
+		//mdelay(ndt);
+	}
+	return ret;
+}
+
+static int sa51034_get_mute(struct sa51034_priv *sa51034,int *mute) 
+{
+
+	int ret = 0;
+	if (sa51034->mute_gpio == -1) {
+			pr_err(  "sa51034 %s mute_gpio invalid return\n",__func__);
+			return -1;	
+	}
+	
+	*mute = gpio_get_value(sa51034->mute_gpio);
+	pr_info("[SA51034] %s mute gpio val=%d\n", __func__, *mute);
+	
+	return ret;
+}
+
+static int sa51034_set_pwen(struct sa51034_priv *sa51034,int en) 
+{
+	//struct snd_soc_component *component = dai->component;
+	//struct ak4940_priv *ak4940 = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+	//int ndt;
+
+	pr_info("\t[SA51034] %s en[%s]\n", __func__, en ? "ON":"OFF");
+	if (sa51034->pwen_gpio == -1) {
+			pr_err(  "sa51034 %s pwen_gpio invalid return\n",__func__);
+			return -1;	
+	}
+	//ndt = 4080000 / sa51034->fs;
+	if (en) {
+		/* SMUTE: 1 , MUTE */
+		ret = gpio_direction_output(sa51034->pwen_gpio, 1);
+		//mdelay(ndt);
+	} else{
+		/* SMUTE:  0  ,NORMAL operation */
+		ret = gpio_direction_output(sa51034->pwen_gpio, 0);
+		//mdelay(ndt);
+	}
+	return ret;
+}
+
+
+
+static int sa51034_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+	struct sa51034_priv *sa51034;
+	int ret = 0;
+	unsigned int val;
+
+	pr_info("\t[sa51034] %s(%d),i2c->addr=0x%x\n", __func__, __LINE__,i2c->addr);
+
+	sa51034 = devm_kzalloc(&i2c->dev, sizeof(struct sa51034_priv), GFP_KERNEL);
+	if (sa51034 == NULL)
+		return -ENOMEM;
+
+
+	sa51034->regmap = devm_regmap_init_i2c(i2c, &sa51034_regmap);
+
+	if (IS_ERR(sa51034->regmap)) {
+		devm_kfree(&i2c->dev, sa51034);
+		return PTR_ERR(sa51034->regmap);
+	}
+
+
+	i2c_set_clientdata(i2c, sa51034);
+	sa51034->i2c = i2c;
+	ret = devm_snd_soc_register_component(&i2c->dev, &pa_asoc_component,
+					      NULL, 0);
+	if (ret) {
+		pr_err( "pa component register failed,ret=%d\n",ret);
+		return ret;
+	}
+
+	pr_info("[sa51034] %s(%d) pa component register end,ret=0x%x\n", __func__, __LINE__,ret);
+
+	sa51034_gpio_request(sa51034);
+
+
+	sa51034_set_pwen(sa51034,1); 
+
+	//sa51034_set_mute(sa51034,0);
+
+	g_sa51034 = sa51034;
+
+	
+	pr_info("\t[sa51034] %s end\n", __func__);
+	return ret;
+}
+
+static const struct i2c_device_id sa51034_i2c_id[] = {
+
+	{ "sa51034", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, sa51034_i2c_id);
+
+static struct i2c_driver sa51034_i2c_driver = {
+	.driver = {
+		.name = "sa51034",
+		.of_match_table = of_match_ptr(sa51034_i2c_dt_ids),
+	},
+	.probe = sa51034_i2c_probe,
+	//.remove = sa51034_i2c_remove,
+	.id_table = sa51034_i2c_id,
+};
+
+static int  sa51034_init(void)
+{
+	pr_info("\t[sa51034] %s(%d)\n", __func__, __LINE__);
+
+	return i2c_add_driver(&sa51034_i2c_driver);
+}
+
+#endif
+static int zx29_audio_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device_node *np = pdev->dev.of_node;
+	struct snd_soc_card *card = &zx29_soc_card;
+	struct zx29_board_data *board;
+	const struct of_device_id *id;
+	enum of_gpio_flags flags;
+	unsigned int idx;
+
+	struct device *dev = &pdev->dev;
+	dev_info(&pdev->dev,"zx29_audio_probe start!\n");
+
+
+	card->dev = &pdev->dev;
+
+	board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
+	if (!board)
+		return -ENOMEM;
+/*
+	if (np) {
+		zx29_dai_link[0].cpus->dai_name = NULL;
+		zx29_dai_link[0].cpus->of_node = of_parse_phandle(np,
+				"zxic,i2s-controller", 0);
+		if (!zx29_dai_link[0].cpus->of_node) {
+			dev_err(&pdev->dev,
+			   "Property 'zxic,i2s-controller' missing or invalid\n");
+			ret = -EINVAL;
+		}
+
+		zx29_dai_link[0].platforms->name = NULL;
+		zx29_dai_link[0].platforms->of_node = zx29_dai_link[0].cpus->of_node;
+
+		
+#if 0
+		zx29_dai_link[0].codecs->of_node = of_parse_phandle(np,
+				"zxic,audio-codec", 0);
+		if (!zx29_dai_link[0].codecs->of_node) {
+			dev_err(&pdev->dev,
+				"Property 'zxic,audio-codec' missing or invalid\n");
+			return -EINVAL;
+		}
+#endif	
+	}
+	
+*/
+
+
+
+
+	id = of_match_device(of_match_ptr(zx29_nau8810_of_match), &pdev->dev);
+	if (id)
+		*board = *((struct zx29_board_data *)id->data);
+	
+	board->name = "zx29_nau8810";
+	board->dev = &pdev->dev;
+
+	//platform_set_drvdata(pdev, board);
+	s_board = board;
+
+
+#if 0
+
+	board->gpio_pwen = of_get_gpio_flags(dev->of_node, 0, &flags);
+	if (!gpio_is_valid(board->gpio_pwen)) {
+		dev_err(dev,"  gpio_pwen no found\n");
+		return -EBUSY;
+	}
+	dev_info(dev, "board->gpio_pwen=0x%x  flags = %d\n",board->gpio_pwen,flags);
+	ret = devm_gpio_request(&pdev->dev,board->gpio_pwen, "codec_pwen");
+	if (ret < 0) {
+		dev_err(dev,"gpio_pwen request error.\n");
+		return ret;
+
+	}
+
+	board->gpio_pdn = of_get_gpio_flags(dev->of_node, 1, &flags);
+	if (!gpio_is_valid(board->gpio_pdn)) {
+		dev_err(dev,"  gpio_pdn no found\n");
+		return -EBUSY;
+	}
+	dev_info(dev, "board->gpio_pdn=0x%x  flags = %d\n",board->gpio_pdn,flags);
+	ret = devm_gpio_request(&pdev->dev,board->gpio_pdn, "codec_pdn");
+	if (ret < 0) {
+		dev_err(dev,"gpio_pdn request error.\n");
+		return ret;
+
+	}
+#endif
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+	if (ret){
+		dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+	    return ret;
+	}
+	zx29_i2s_top_pin_cfg(pdev);	
+
+	
+	//codec_power_on(board,1);
+#ifdef  CONFIG_PA_SA51034
+
+	dev_info(&pdev->dev,"zx29_audio_probe start sa51034_init!\n");
+
+	ret = sa51034_init();
+	if (ret != 0) {
+
+		pr_err("sa51034_init Failed to register I2C driver: %d\n", ret);
+		//return ret;
+
+	}
+	else{
+
+		for (idx = 0; idx < ARRAY_SIZE(pa_controls); idx++) {
+			ret = snd_ctl_add(card->snd_card,
+					  snd_ctl_new1(&pa_controls[idx],
+						       NULL));
+			if (ret < 0){
+				return ret;
+			}
+		}
+
+	}
+	 ret  = 0;
+
+#endif	
+	dev_info(&pdev->dev,"zx29_audio_probe end!\n");
+
+	return ret;
+}
+
+
+#ifdef CONFIG_PM
+static int zx29_audio_suspend(struct platform_device * pdev, pm_message_t state)
+{
+	pr_info("%s: start!\n",__func__);
+
+    //pinctrl_pm_select_sleep_state(&pdev->dev);
+	return 0;
+}
+
+static int zx29_audio_resume(struct platform_device *pdev)
+{
+	pr_info("%s: start!\n",__func__);
+
+    //pinctrl_pm_select_default_state(&pdev->dev);
+
+	return 0;
+}
+
+int zx29_snd_soc_suspend(struct device *dev)
+{
+
+	int ret = 0;
+	struct zx29_board_data *info = s_board;
+
+	pr_info("%s: start!\n",__func__);
+
+    //pinctrl_pm_select_sleep_state(dev);
+    if((info->p != NULL)&&(info->s_sleep != NULL)){
+		ret = pinctrl_select_state(info->p, info->s_sleep);
+		if (ret < 0) {
+			//devm_pinctrl_put(info->p);
+			dev_err(dev, " select state failure!!\n");
+			//return;
+		}
+		dev_info(dev, "%s: set pinctrl sleep end!\n", __func__);	
+    }
+	return snd_soc_suspend(dev);
+
+}
+int zx29_snd_soc_resume(struct device *dev)
+{
+	int ret = 0;
+	struct zx29_board_data *info = s_board;
+
+	pr_info("%s: start!\n",__func__);
+
+    //pinctrl_pm_select_default_state(dev);
+    if((info->p != NULL)&&(info->s != NULL)){
+		ret = pinctrl_select_state(info->p, info->s);
+		if (ret < 0) {
+			//devm_pinctrl_put(info->p);
+			dev_err(dev, " select state failure!!\n");
+			//return;
+		}
+		dev_info(dev, "%s: set pinctrl active end!\n", __func__);	
+    }
+
+
+	return snd_soc_resume(dev);
+
+}
+
+#else
+static int zx29_audio_suspend(struct platform_device * pdev, pm_message_t state)
+{
+
+	return 0;
+}
+
+static int zx29_audio_resume(struct platform_device *pdev)
+{
+
+
+	return 0;
+}
+
+int zx29_snd_soc_suspend(struct device *dev)
+{
+
+
+	return snd_soc_suspend(dev);
+
+}
+int zx29_snd_soc_resume(struct device *dev)
+{
+
+
+	return snd_soc_resume(dev);
+
+}
+
+
+#endif
+
+
+struct dev_pm_ops zx29_snd_soc_pm_ops = {
+	.suspend = zx29_snd_soc_suspend,
+	.resume = zx29_snd_soc_resume,
+	.freeze = snd_soc_suspend,
+	.thaw = snd_soc_resume,
+	.poweroff = snd_soc_poweroff,
+	.restore = snd_soc_resume,
+};
+
+
+
+static struct platform_driver zx29_platform_driver = {
+	.driver		= {
+		.name	= "zx29_nau8810",
+		.of_match_table = of_match_ptr(zx29_nau8810_of_match),
+		//.pm	= &snd_soc_pm_ops,
+		.pm	= &zx29_snd_soc_pm_ops,
+	},
+	.probe		= zx29_audio_probe,
+	//.remove 	= zx29_remove,
+};
+
+
+#if 0
+static int zx29_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	print_audio("Alsa  zx297520xx SoC Audio driver\n");
+
+	zx29_platform_data = pdev->dev.platform_data;
+	if (zx29_platform_data == NULL) {
+		printk(KERN_ERR "Alsa  zx297520xx SoC Audio: unable to find platform data\n");
+		return -ENODEV;
+	}
+
+	if (zx297520xx_setup_pins(zx29_platform_data, "codec") < 0)
+		return -EBUSY;
+
+	zx29_i2s_top_reg_cfg();
+
+	zx29_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!zx29_snd_device) {
+		printk(KERN_ERR "Alsa  zx297520xx SoC Audio: Unable to register\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(zx29_snd_device, &zxic_soc_card);
+//	platform_device_add_data(zx29xx_nau8810_snd_device, &zx29xx_nau8810, sizeof(zx29xx_nau8810));
+	ret = platform_device_add(zx29_snd_device);
+	if (ret) {
+		printk(KERN_ERR "Alsa  zx29 SoC Audio: Unable to add\n");
+		platform_device_put(zx29_snd_device);
+	}
+
+	return ret;
+}
+#endif
+
+
+
+module_platform_driver(zx29_platform_driver);
+
+MODULE_DESCRIPTION("zx29 ALSA SoC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:zx29-audio-nau8810");
diff --git a/upstream/linux-5.10/sound/soc/sanechips/zx29_ti3100.c b/upstream/linux-5.10/sound/soc/sanechips/zx29_ti3100.c
new file mode 100755
index 0000000..9959350
--- /dev/null
+++ b/upstream/linux-5.10/sound/soc/sanechips/zx29_ti3100.c
@@ -0,0 +1,2011 @@
+/*
+ * zx297520v3_es8312.c  --  zx298501-ti3100 ALSA SoC Audio board driver
+ *
+ * Copyright (C) 2022, ZTE Corporation.
+ *
+ * Based on smdk_wm8994.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "../codecs/tlv320aic31xx.h"
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+
+ 
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+//#include <sound/tlv.h>
+//#include <sound/soc.h>
+//#include <sound/jack.h>
+//#include <sound/zx29_snd_platform.h>
+//#include <mach/iomap.h>
+//#include <mach/board.h>
+#include <linux/of_gpio.h>
+
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+
+
+#include "i2s.h"
+
+#define ZX29_I2S_TOP_LOOP_REG	0x60
+
+
+#if 1
+ 
+#define  ZXIC_MCLK                    26000000
+#define  ZX29_TI3100_FREQ   26000000
+
+#define  ZXIC_PLL_CLKIN_MCLK		  0
+
+
+#define zx_reg_sync_write(v, a) \
+        do {    \
+            iowrite32(v, a);    \
+        } while (0)
+
+#define zx_read_reg(addr) \
+    ioread32(addr)
+
+#define zx_write_reg(addr, val)   \
+	zx_reg_sync_write(val, addr)
+
+
+
+struct zx29_board_data {
+	const char *name;
+	struct device *dev;
+
+	int codec_refclk;
+	int gpio_pwen;	
+	int gpio_pdn;
+	void __iomem *sys_base_va;
+
+};
+
+
+struct zx29_board_data *s_board = 0;
+
+//#define AON_WIFI_BT_CLK_CFG2  ((volatile unsigned int *)(ZX_TOP_CRM_BASE + 0x94))
+ /* Default ZX29s */
+static struct zx29_board_data zx29_platform_data = {
+	.codec_refclk = ZX29_TI3100_FREQ,
+};
+ static struct platform_device *zx29_snd_device;
+ 
+ static DEFINE_RAW_SPINLOCK(codec_pa_lock);
+ 
+ static int set_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol);
+ static int get_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol);
+
+
+#ifdef USE_ALSA_VOICE_FUNC
+ extern int zDrv_Audio_Printf(void *pFormat, ...);
+ extern int zDrvVp_GetVol_Wrap(void);
+ extern int zDrvVp_SetVol_Wrap(int volume);
+ extern int zDrvVp_GetPath_Wrap(void);
+ extern int zDrvVp_SetPath_Wrap(int path);
+ extern int zDrvVp_SetMute_Wrap(bool enable);
+ extern bool zDrvVp_GetMute_Wrap(void);
+ extern int zDrvVp_SetTone_Wrap(int toneNum);
+ 
+ static int vp_GetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetVol(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_GetVol(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_SetTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int vp_getTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ 
+ static int audio_GetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+ static int audio_SetPath(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+
+ 
+ //static const DECLARE_TLV_DB_SCALE(vp_path_tlv, 0, 300, 0);
+ 
+ static const char * const vpath_in_text[] = {
+	 "handset", "speak", "headset", "bluetooth",
+ };
+ 
+ static const char *tone_class[] = {
+	 "Lowpower", "Sms", "Callstd", "Alarm", "Calltime",
+ };
+ 
+ static const struct soc_enum vpath_in_enum =	 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(vpath_in_text), vpath_in_text); 
+ 
+ static const struct soc_enum tone_class_enum[] = {
+	 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tone_class), tone_class),
+ };
+ 
+ static const struct snd_kcontrol_new vp_snd_controls[] = {  
+	 SOC_ENUM_EXT("voice processing path select",vpath_in_enum,vp_GetPath,vp_SetPath),
+	 //SOC_SINGLE_EXT_TLV("voice processing path Volume",0, 5, 5, 0,vp_GetVol, vp_SetVol,vp_path_tlv), 
+	 SOC_SINGLE_EXT("voice processing path Volume",0, 5, 5, 0,vp_GetVol, vp_SetVol),
+	 SOC_SINGLE_EXT("voice uplink mute", 0, 1, 1, 0,vp_GetMute, vp_SetMute),
+	 SOC_ENUM_EXT("voice tone sel", tone_class_enum[0], vp_getTone, vp_SetTone),
+	 SOC_SINGLE_BOOL_EXT("path stauts dump", 0,get_path_stauts_switch, set_path_stauts_switch),
+	 SOC_ENUM_EXT("audio path select",vpath_in_enum,audio_GetPath,audio_SetPath),
+ };
+ 
+ static int curtonetype = 0;
+ static int vp_getTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 ucontrol->value.integer.value[0] = curtonetype;
+	 return 0;
+ }
+ 
+ static int vp_SetTone(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 int vol = 0,ret = 0, tonenum;
+	 tonenum = ucontrol->value.integer.value[0];
+	 curtonetype = tonenum;
+	 //printk("Alsa vp_SetTone tonenum=%d\n", tonenum);
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetTone_Wrap)(tonenum);
+	 if(ret < 0)
+	 {
+		 printk(KERN_ERR "vp_SetTone fail = %d\n", tonenum);
+		 return ret;
+	 }
+	 return 0;
+ }
+ 
+ static int vp_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+	 int enable = 0,ret = 0;
+	 enable = ucontrol->value.integer.value[0];
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetMute_Wrap)(enable);
+	 if(ret < 0)
+	 {
+	   printk(KERN_ERR "vp_SetMute fail = %d\n",enable);
+	   return ret;
+	 }
+	 return 0;
+ }
+ 
+ static int vp_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {		 
+		//ucontrol->value.integer.value[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetMute_Wrap)();
+		return 0;
+ }
+ 
+ static int vp_SetVol(struct snd_kcontrol *kcontrol,
+								struct snd_ctl_elem_value *ucontrol)
+ {
+		int vol = 0,ret = 0;
+		vol = ucontrol->value.integer.value[0];
+		//ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetVol_Wrap)(vol);
+		if(ret < 0)
+		{
+		   printk(KERN_ERR "vp_SetVol fail = %d\n",vol);
+		   return ret;
+	   }
+	 return 0;
+ }
+ static int vp_GetVol(struct snd_kcontrol *kcontrol,
+								struct snd_ctl_elem_value *ucontrol)
+ {		 
+		//ucontrol->value.integer.value[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetVol_Wrap)();
+		return 0;
+ }
+ static int vp_GetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {	 
+	 //ucontrol->value.enumerated.item[0] = CPPS_FUNC(cpps_callbacks, zDrvVp_GetPath_Wrap)();
+	 return 0;
+ }
+ static int vp_SetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {
+	 int ret = 0,path = 0;
+	 unsigned long	flags;
+	 path = ucontrol->value.enumerated.item[0];
+ 
+	 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_SetPath_Wrap)(path);
+	 if(ret < 0)
+	 {
+	   printk(KERN_ERR "vp_SetPath fail = %d\n",path);
+	   return ret;
+	 }
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+	 switch (path) {
+	 case 0:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+		 break;
+	 case 1:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_40, GPIO_HIGH);
+		 break;
+	 case 2:
+		 break;
+	 case 3:
+		 break;
+	 default:
+		 break;
+	 }
+#endif
+	 return 0;
+ }
+ 
+ static int curpath = 0;
+ static int audio_GetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {	 
+	 ucontrol->value.enumerated.item[0] = curpath;
+	 return 0;
+ }
+ 
+ static int audio_SetPath(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+ {
+	 int ret = 0,path = 0;
+	 unsigned long	flags;
+	 
+	 path = ucontrol->value.enumerated.item[0];
+	 curpath = path;
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+	 switch (path) {
+	 case 0:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+		 break;
+	 case 1:
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_39, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_40, GPIO_HIGH);
+		 break;
+	 case 2:
+		 break;
+	 case 3:
+		 break;
+	 default:
+		 break;
+	 }
+#endif
+	 return 0;
+ }
+ 
+ typedef enum
+ {
+	 VP_PATH_HANDSET	=0, 	
+	 VP_PATH_SPEAKER,		 
+	 VP_PATH_HEADSET,					  
+	 VP_PATH_BLUETOOTH, 				   
+	 VP_PATH_BLUETOOTH_NO_NR,					 
+	 VP_PATH_HSANDSPK,
+	 
+	 VP_PATH_OFF = 255, 				 
+	 
+	 MAX_VP_PATH = VP_PATH_OFF				 
+ }T_ZDrv_VpPath;
+ 
+ extern int zDrvVp_Loop(T_ZDrv_VpPath path);
+
+ 
+//#else
+ static const struct snd_kcontrol_new machine_snd_controls[] = {		 
+	 SOC_SINGLE_BOOL_EXT("path stauts dump", 0,get_path_stauts_switch, set_path_stauts_switch),
+ };
+ 
+
+ 
+ //extern int rt5670_hs_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+ 
+ int path_stauts_switch = 0;
+ static int set_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+ {
+	 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	 struct snd_soc_dapm_path *p;
+ 
+	 int path_stauts_switch = ucontrol->value.integer.value[0];
+ 
+	 
+	 if (path_stauts_switch == 1)
+	 {
+		 list_for_each_entry(p, &card->paths, list){
+			 
+		   //print_audio("Alsa	path name (%s),longname (%s),sink (%s),source (%s),connect %d \n", p->name,p->long_name,p->sink->name,p->source->name,p->connect);
+		   //printk("Alsa  path longname %s,sink %s,source %s,connect %d \n", p->long_name,p->sink->name,p->source->name,p->connect);
+ 
+		 }
+	 }
+	 return 0;
+ }
+ 
+ static int get_path_stauts_switch(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+ {
+	 
+	 ucontrol->value.integer.value[0] = path_stauts_switch;
+	 return 0;
+ };
+#endif 
+ 
+#ifdef CONFIG_SND_SOC_JACK_DECTEC
+ 
+ static struct snd_soc_jack codec_headset;
+ 
+ /* Headset jack detection DAPM pins */
+ static struct snd_soc_jack_pin codec_headset_pins[] = {
+	 {
+		 .pin = "Headphone",
+		 .mask = SND_JACK_HEADPHONE,
+	 },
+ };
+ 
+#endif
+
+
+
+
+
+ static int zx29startup(struct snd_pcm_substream *substream)
+ {
+ //  int ret = 0;
+	 print_audio("Alsa	Entered func %s\n", __func__);
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx29_startup device=%d,stream=%d\n", substream->pcm->device, substream->stream);
+ 
+	 struct snd_pcm *pcmC0D0p = snd_lookup_minor_data(16, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+	 struct snd_pcm *pcmC0D1p = snd_lookup_minor_data(17, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+	 struct snd_pcm *pcmC0D2p = snd_lookup_minor_data(18, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);	 
+	 struct snd_pcm *pcmC0D3p = snd_lookup_minor_data(19, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);	
+	 if ((pcmC0D0p == NULL) || (pcmC0D1p == NULL) || (pcmC0D2p == NULL) || (pcmC0D3p == NULL))
+		 return  -EINVAL;	  
+	 if ((pcmC0D0p->streams[0].substream_opened && pcmC0D1p->streams[0].substream_opened) || 
+		 (pcmC0D0p->streams[0].substream_opened && pcmC0D2p->streams[0].substream_opened) || 
+		 (pcmC0D0p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened) || 
+		 (pcmC0D1p->streams[0].substream_opened && pcmC0D2p->streams[0].substream_opened) ||
+		 (pcmC0D1p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened) ||
+		 (pcmC0D2p->streams[0].substream_opened && pcmC0D3p->streams[0].substream_opened))
+		 BUG();
+#if 0
+	 unsigned long	flags;
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 mdelay(1);  
+ 
+		 raw_spin_lock_irqsave(&codec_pa_lock, flags);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 udelay(2);  
+		 gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
+		 udelay(2);
+		 gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
+		 raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
+	 }
+#endif
+ 
+ 
+	 return 0;
+ }
+ 
+ static void zx29_shutdown(struct snd_pcm_substream *substream)
+ {
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx297520xx_shutdown device=%d, stream=%d\n", substream->pcm->device, substream->stream);
+ //  print_audio("Alsa	Entered func %s, stream=%d\n", __func__, substream->stream);
+ 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+#endif
+	 }
+	 
+	 if (snd_soc_dai_active(cpu_dai))
+		 return;
+ 
+
+  
+ }
+ 
+ static void zx29_shutdown2(struct snd_pcm_substream *substream)
+ {
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 //CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa: zx29_shutdown2 device=%d, stream=%d\n", substream->pcm->device, substream->stream);
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#ifdef _USE_7520V3_PHONE_TYPE_C31F
+		 gpio_set_value(ZX29_GPIO_39, GPIO_LOW);
+		 mdelay(1);  
+		 gpio_set_value(ZX29_GPIO_40, GPIO_LOW);
+#endif
+#ifdef USE_ALSA_VOICE_FUNC
+		 //CPPS_FUNC(cpps_callbacks, zDrvVp_Loop)(VP_PATH_OFF);
+#endif
+
+
+	 }
+ 
+	 if (snd_soc_dai_active(cpu_dai))
+		 return;
+ 
+
+ }
+ static int zx29_init_paiftx(struct snd_soc_pcm_runtime *rtd)
+ {
+	 //struct snd_soc_codec *codec = rtd->codec;
+	 //struct snd_soc_dapm_context *dapm = &codec->dapm;
+ 
+	 //snd_soc_dapm_enable_pin(dapm, "HPOL");
+	 //snd_soc_dapm_enable_pin(dapm, "HPOR");
+ 
+	 /* Other pins NC */
+ //  snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
+ 
+ //  print_audio("Alsa	Entered func %s\n", __func__);
+ 
+	 return 0;
+ }
+ static int zx29_hw_params(struct snd_pcm_substream *substream,
+										struct snd_pcm_hw_params *params)
+ {
+     print_audio("Alsa:	Entered func %s\n", __func__);
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	 int ret;
+	 int rfs = 0, frq_out = 0;	 
+	 switch (params_rate(params)) {
+	 case 8000:
+	 case 16000:
+	 case 11025:
+	 case 22050:
+	 case 24000:
+	 case 32000:
+	 case 44100:
+	 case 48000:
+		 rfs = 32;
+		 break;
+	 default:
+	 	{
+	 	    ret =  -EINVAL;
+		    print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret);	 	      
+		 	return ret;
+	 	}
+	 }
+	 
+	 frq_out = params_rate(params) * rfs * 2;
+	 
+	 /* Set the Codec DAI configuration */
+	 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+
+
+	 /* Set the AP DAI configuration */
+	 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: ap dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+ 
+	 /* Set the Codec DAI clk */	 
+	 /*ret =snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_BCLK1,
+								  fs*datawidth*2, 256*fs);
+	 if (ret < 0){
+	 	
+	 	 print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		 return ret;
+	}
+	*/
+	
+	 ret = snd_soc_dai_set_sysclk(codec_dai, AIC31XX_PLL_CLKIN_MCLK,  ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+	  
+	  
+	 /* Set the AP DAI clk */
+	 ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+	 //ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+ 
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: cpu dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+     print_audio("Alsa:	Entered func %s end\n", __func__);
+	 
+	 return 0;
+ }
+
+static int zx29_hw_params_lp(struct snd_pcm_substream *substream,
+									   struct snd_pcm_hw_params *params)
+{
+	print_audio("Alsa: Entered func %s\n", __func__);
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	int ret;
+	int rfs = 0, frq_out = 0;	
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+	case 11025:
+	case 22050:
+	case 24000:
+	case 32000:
+	case 44100:
+	case 48000:
+		rfs = 32;
+		break;
+	default:
+	   {
+		   ret =  -EINVAL;
+		   print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret); 			 
+		   return ret;
+	   }
+	}
+	
+	frq_out = params_rate(params) * rfs * 2;
+	
+	/* Set the Codec DAI configuration */
+	/*
+	
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							  | SND_SOC_DAIFMT_NB_NF
+							  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0){
+	   
+		print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		return ret;
+	}
+	*/ 
+
+	
+	/* Set the AP DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+							  | SND_SOC_DAIFMT_NB_NF
+							  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0){
+	   
+		print_audio("Alsa: ap dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		return ret;
+	}
+
+	/* Set the Codec DAI clk */ 	
+	/*ret =snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_BCLK1,
+								 fs*datawidth*2, 256*fs);
+	if (ret < 0){
+	   
+		print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		return ret;
+   }
+	*/
+	/*
+	ret = snd_soc_dai_set_sysclk(codec_dai, ES8312_CLKID_MCLK,ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	if (ret < 0){	   
+		print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		return ret;
+	}
+	*/
+	/* Set the AP DAI clk */
+	ret = snd_soc_dai_set_sysclk(cpu_dai, ZX29_I2S_WCLK_SEL,ZX29_I2S_WCLK_FREQ_26M, SND_SOC_CLOCK_IN);
+
+	if (ret < 0){	   
+		print_audio("Alsa: cpu dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		return ret;
+	}
+	print_audio("Alsa: Entered func %s end\n", __func__);
+	
+	return 0;
+}
+
+
+ 
+
+ 
+
+ static int zx29_hw_params_voice(struct snd_pcm_substream *substream,
+										struct snd_pcm_hw_params *params)
+ {
+	 print_audio("Alsa: Entered func %s\n", __func__);
+	 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	 int ret;
+	 int rfs = 0, frq_out = 0;	 
+	 switch (params_rate(params)) {
+	 case 8000:
+	 case 16000:
+	 case 11025:
+	 case 22050:
+	 case 24000:
+	 case 32000:
+	 case 44100:
+	 case 48000:
+		 rfs = 32;
+		 break;
+	 default:
+		{
+			ret =  -EINVAL;
+			print_audio("Alsa: rate=%d not support,ret=%d!\n", params_rate(params),ret);			  
+			return ret;
+		}
+	 }
+	 
+	 frq_out = params_rate(params) * rfs * 2;
+	 
+	 /* Set the Codec DAI configuration */
+	 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+							   | SND_SOC_DAIFMT_NB_NF
+							   | SND_SOC_DAIFMT_CBS_CFS);
+	 if (ret < 0){
+		
+		 print_audio("Alsa: codec dai snd_soc_dai_set_fmt fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+ 
+ 	 ret = snd_soc_dai_set_sysclk(codec_dai, AIC31XX_PLL_CLKIN_MCLK,  ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	 if (ret < 0){	 	
+	 	 print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+	
+
+	 /* Set the Codec DAI clk */	 
+	 /*ret =snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_BCLK1,
+								  fs*datawidth*2, 256*fs);
+	 if (ret < 0){
+		
+		 print_audio("Alsa: codec dai clk snd_soc_dai_set_pll fail,ret=%d!\n",ret);
+		 return ret;
+	}
+	
+	 
+	 ret = snd_soc_dai_set_sysclk(codec_dai, AK4940_CLKID_BCLK,ZXIC_MCLK, SND_SOC_CLOCK_IN);
+	 if (ret < 0){		
+		 print_audio("Alsa: codec dai snd_soc_dai_set_sysclk fail,ret=%d!\n",ret);
+		 return ret;
+	 }
+	 
+	 */
+
+	 print_audio("Alsa: Entered func %s end\n", __func__);
+	 
+	 return 0;
+ }
+
+										 
+ int zx29_prepare2(struct snd_pcm_substream *substream)
+ {
+	 int path, ret;
+	 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		 //ret = CPPS_FUNC(cpps_callbacks, zDrvVp_Loop)(VP_PATH_SPEAKER);
+		 if (ret < 0)
+			 return -1;
+	 }
+	 
+	 return 0;
+ } 
+ static void zx29_i2s_top_reg_cfg(void)
+ {
+	 unsigned int i2s_top_reg;
+	 int ret = 0;
+ 
+#ifdef CONFIG_USE_PIN_I2S0
+	 ret = gpio_request(PIN_I2S0_WS, "i2s0_ws");
+	 if (ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S0_CLK, "i2s0_clk");
+	 if (ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S0_DIN, "i2s0_din");
+	 if (ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S0_DOUT, "i2s0_dout");
+	 if (ret < 0)
+		 BUG();
+	 zx29_gpio_config(PIN_I2S0_WS, FUN_I2S0_WS);
+	 zx29_gpio_config(PIN_I2S0_CLK, FUN_I2S0_CLK);
+	 zx29_gpio_config(PIN_I2S0_DIN, FUN_I2S0_DIN);
+	 zx29_gpio_config(PIN_I2S0_DOUT, FUN_I2S0_DOUT);
+	 
+	 //top i2s1 cfg
+	 i2s_top_reg = zx_read_reg(ZX29_I2S_LOOP_CFG);
+	 i2s_top_reg &= 0xfffffff8;
+	 i2s_top_reg |= 0x00000001; //	inter arm_i2s1--top i2s1
+	 zx_write_reg(ZX29_I2S_LOOP_CFG, i2s_top_reg);
+#elif defined (CONFIG_USE_PIN_I2S1)
+	
+
+	 ret = gpio_request(PIN_I2S1_WS,"i2s1_ws");
+	 if(ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S1_CLK,"i2s1_clk");
+	 if(ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S1_DIN,"i2s1_din");
+	 if(ret < 0)
+		 BUG();
+	 ret = gpio_request(PIN_I2S1_DOUT,"i2s1_dout");
+	 if(ret < 0)
+		 BUG();
+	 zx29_gpio_config(PIN_I2S1_WS, FUN_I2S1_WS);
+	 zx29_gpio_config(PIN_I2S1_CLK, FUN_I2S1_CLK);
+	 zx29_gpio_config(PIN_I2S1_DIN, FUN_I2S1_DIN);
+	 zx29_gpio_config(PIN_I2S1_DOUT, FUN_I2S1_DOUT);
+		 
+	 //top i2s2 cfg
+	 i2s_top_reg = zx_read_reg(ZX29_I2S_LOOP_CFG);
+	 i2s_top_reg &= 0xfff8ffff;
+	 i2s_top_reg |= 0x00010000; //	inter arm_i2s1--top i2s2
+	 zx_write_reg(ZX29_I2S_LOOP_CFG, i2s_top_reg);
+#endif
+ 
+	 // inter loop
+	 //i2s_top_reg = zx_read_reg(ZX29_I2S_LOOP_CFG);
+	 //i2s_top_reg &= 0xfffffe07;
+	 //i2s_top_reg |= 0x000000a8; //	inter arm_i2s2--afe i2s
+	 //zx_write_reg(ZX29_I2S_LOOP_CFG, i2s_top_reg);
+	 
+ //  print_audio("Alsa %s i2s loop cfg reg=%x\n",__func__, zx_read_reg(ZX29_I2S_LOOP_CFG));  
+ }
+ 
+ static int zx29_late_probe(struct snd_soc_card *card)
+ {
+	 //struct snd_soc_codec *codec = card->rtd[0].codec;
+	 //struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+	 int ret;
+ //  print_audio("Alsa	zx29_late_probe entry!\n");
+ 
+#ifdef CONFIG_SND_SOC_JACK_DECTEC
+	 
+	 ret = snd_soc_jack_new(codec, "Headset",
+							SND_JACK_HEADSET |SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
+							&codec_headset);
+	 if (ret)
+		 return ret;
+ 
+	 ret = snd_soc_jack_add_pins(&codec_headset,
+								 ARRAY_SIZE(codec_headset_pins),
+								 codec_headset_pins);
+	 if (ret)
+		 return ret;
+       #ifdef CONFIG_SND_SOC_codec
+	 //rt5670_hs_detect(codec, &codec_headset);
+       #endif
+#endif
+ 
+	 return 0;
+ }
+ 
+ static struct snd_soc_ops zx29_ops = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 .hw_params = zx29_hw_params,
+ };
+  static struct snd_soc_ops zx29_ops_lp = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 .hw_params = zx29_hw_params_lp,
+ };
+ static struct snd_soc_ops zx29_ops1 = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown,
+	 //.hw_params = zx29_hw_params1,
+ };
+ 
+ static struct snd_soc_ops zx29_ops2 = {
+	 //.startup = zx29_startup,
+	 .shutdown = zx29_shutdown2,
+	 //.hw_params = zx29_hw_params1,
+	 .prepare = zx29_prepare2,
+ };
+ static struct snd_soc_ops voice_ops = {
+	 .startup = zx29startup,
+	 .shutdown = zx29_shutdown2,
+	 .hw_params = zx29_hw_params_voice,
+	 //.prepare = zx29_prepare2,
+ };
+
+ 
+ enum {
+	 MERR_DPCM_AUDIO = 0,
+	 MERR_DPCM_DEEP_BUFFER,
+	 MERR_DPCM_COMPR,
+ };
+
+ 
+#if 0
+ 
+ static struct snd_soc_card zxic_soc_card = {
+	 .name = "zx298501_ti3100",
+	 .owner = THIS_MODULE,
+	 .dai_link = &zxic_dai_link,
+	 .num_links = ARRAY_SIZE(zxic_dai_link),
+#ifdef USE_ALSA_VOICE_FUNC
+	 .controls = vp_snd_controls,
+	 .num_controls = ARRAY_SIZE(vp_snd_controls),
+#endif
+ 
+ //  .late_probe = zx29_late_probe,
+	 
+ };
+#endif 
+ //static struct zx298501_ti3100_pdata *zx29_platform_data;
+ 
+ static int zx29_setup_pins(struct zx29_board_data *codec_pins, char *fun)
+ {
+	 int ret;
+ 
+	 //ret = gpio_request(codec_pins->codec_refclk, "codec_refclk");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio: %s pin already in use\n", fun);
+		 return ret;
+	 }
+	 //zx29_gpio_config(codec_pins->codec_refclk, GPIO17_CLK_OUT2);
+ 
+#ifdef  _USE_7520V3_PHONE_TYPE_C31F
+	 ret = gpio_request_one(ZX29_GPIO_39, GPIOF_OUT_INIT_LOW, "codec_pa");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio:  codec_pa in use\n");
+		 return ret;
+	 }
+	 
+	 ret = gpio_request_one(ZX29_GPIO_40, GPIOF_OUT_INIT_LOW, "codec_sw");
+	 if (ret < 0) {
+		 printk(KERN_ERR "zx297520xx SoC Audio:  codec_sw in use\n");
+		 return ret;
+	 }
+#endif
+ 
+	 return 0;
+ }
+#endif
+
+ 
+ static int zx29_remove(struct platform_device *pdev)
+ {
+	 gpio_free(zx29_platform_data.codec_refclk);
+	 platform_device_unregister(zx29_snd_device);
+	 return 0;
+ }
+ 
+
+ 
+#if  0
+
+ /*
+  * Default CFG switch settings to use this driver:
+  *	ZX29
+  */
+
+ /*
+  * Configure audio route as :-
+  * $ amixer sset 'DAC1' on,on
+  * $ amixer sset 'Right Headphone Mux' 'DAC'
+  * $ amixer sset 'Left Headphone Mux' 'DAC'
+  * $ amixer sset 'DAC1R Mixer AIF1.1' on
+  * $ amixer sset 'DAC1L Mixer AIF1.1' on
+  * $ amixer sset 'IN2L' on
+  * $ amixer sset 'IN2L PGA IN2LN' on
+  * $ amixer sset 'MIXINL IN2L' on
+  * $ amixer sset 'AIF1ADC1L Mixer ADC/DMIC' on
+  * $ amixer sset 'IN2R' on
+  * $ amixer sset 'IN2R PGA IN2RN' on
+  * $ amixer sset 'MIXINR IN2R' on
+  * $ amixer sset 'AIF1ADC1R Mixer ADC/DMIC' on
+  */
+
+/* ZX29 has a 16.934MHZ crystal attached to ti3100 */
+#define ZX29_TI3100_FREQ 16934000
+
+
+
+
+
+static int zx29_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	unsigned int pll_out;
+	int ret;
+
+	/* AIF1CLK should be >=3MHz for optimal performance */
+	if (params_width(params) == 24)
+		pll_out = params_rate(params) * 384;
+	else if (params_rate(params) == 8000 || params_rate(params) == 11025)
+		pll_out = params_rate(params) * 512;
+	else
+		pll_out = params_rate(params) * 256;
+
+	ret = snd_soc_dai_set_pll(codec_dai, AK4940_FLL1, AK4940_FLL_SRC_MCLK1,
+					ZX29_AK4940_FREQ, pll_out);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, AK4940_SYSCLK_FLL1,
+					pll_out, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * ZX29 AK4940 DAI operations.
+ */
+static struct snd_soc_ops zx29_ops = {
+	.hw_params = smdk_hw_params,
+};
+
+static int zx29_ti3100_init_paiftx(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
+
+	/* Other pins NC */
+	snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
+	snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTLN");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTLP");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTRP");
+	snd_soc_dapm_nc_pin(dapm, "SPKOUTRN");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
+	snd_soc_dapm_nc_pin(dapm, "IN1LP");
+	snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
+	snd_soc_dapm_nc_pin(dapm, "IN1RP");
+	snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
+
+	return 0;
+}
+#endif
+
+
+
+
+enum {
+	AUDIO_DL_MEDIA = 0,
+	AUDIO_DL_VOICE,
+	AUDIO_DL_2G_AND_3G_VOICE,
+	AUDIO_DL_VP_LOOP,	
+	AUDIO_DL_3G_VOICE,
+	
+	AUDIO_DL_MAX,
+};
+SND_SOC_DAILINK_DEF(dummy, \
+	DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+//SND_SOC_DAILINK_DEF(cpu_i2s0, \
+//	DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
+SND_SOC_DAILINK_DEF(cpu_i2s0, \
+	DAILINK_COMP_ARRAY(COMP_CPU("1405000.i2s")));
+
+
+SND_SOC_DAILINK_DEF(voice_cpu, \
+	DAILINK_COMP_ARRAY(COMP_CPU("soc:voice_audio")));
+
+SND_SOC_DAILINK_DEF(voice_2g_3g, \
+	DAILINK_COMP_ARRAY(COMP_CPU("voice_2g_3g-dai")));
+
+SND_SOC_DAILINK_DEF(voice_3g, \
+		DAILINK_COMP_ARRAY(COMP_CPU("voice_3g-dai")));
+
+
+
+//SND_SOC_DAILINK_DEF(ti3100, \
+//	DAILINK_COMP_ARRAY(COMP_CODEC("ti3100.1-0012", "ti3100-aif")));
+SND_SOC_DAILINK_DEF(dummy_cpu, \
+		DAILINK_COMP_ARRAY(COMP_CPU("soc:zx29_snd_dummy")));
+//SND_SOC_DAILINK_DEF(dummy_platform, \
+//	DAILINK_COMP_ARRAY(COMP_PLATFORM("soc:zx29_snd_dummy")));
+
+SND_SOC_DAILINK_DEF(dummy_codec, \
+		DAILINK_COMP_ARRAY(COMP_CODEC("soc:zx29_snd_dummy", "zx29_snd_dummy_dai")));
+
+#if defined(CONFIG_SND_SOC_ZX29_TI3104)
+SND_SOC_DAILINK_DEF(codec, \
+		DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic3x-codec.1-0018", "tlv320aic3x-hifi")));
+
+#elif defined(CONFIG_SND_SOC_ZX29_TI3100)
+SND_SOC_DAILINK_DEF(codec, \
+		DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic31xx-codec.1-0018", "tlv320aic31xx-hifi")));
+#else
+
+SND_SOC_DAILINK_DEF(codec, \
+		DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic31xx-codec.1-0018", "tlv320aic31xx-hifi")));
+
+#endif
+
+
+//SND_SOC_DAILINK_DEF(media_platform, \
+//	DAILINK_COMP_ARRAY(COMP_PLATFORM("zx29-pcm-audio")));
+SND_SOC_DAILINK_DEF(media_platform, \
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("1405000.i2s")));
+//SND_SOC_DAILINK_DEF(voice_cpu, \
+//	DAILINK_COMP_ARRAY(COMP_CPU("E1D02000.i2s")));
+
+SND_SOC_DAILINK_DEF(voice_platform, \
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("soc:voice_audio")));
+
+			
+//static struct snd_soc_dai_link zx29_dai_link[] = {
+struct snd_soc_dai_link zx29_dai_link[] = {
+ {
+	.name = "zx29_snd_dummy",//codec name
+	.stream_name = "zx29_snd_dumy",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &zx29_ops_lp,
+	.init = zx29_init_paiftx,
+	SND_SOC_DAILINK_REG(cpu_i2s0, dummy_codec, media_platform),
+	
+},
+{
+	.name = "media",//codec name
+	.stream_name = "MultiMedia",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &zx29_ops,
+
+ 	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(cpu_i2s0, codec, media_platform),
+
+},
+{
+	.name = "voice",//codec name
+	.stream_name = "voice",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	.init = zx29_init_paiftx,
+	
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, codec, voice_platform),
+
+},
+{
+	.name = "voice_2g3g_teak",//codec name
+	.stream_name = "voice_2g3g_teak",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, codec, voice_platform),
+
+},
+
+{
+	.name = "voice_3g",//codec name
+	.stream_name = "voice_3g",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	.ops = &voice_ops,
+
+	.init = zx29_init_paiftx,
+	
+
+	SND_SOC_DAILINK_REG(voice_cpu, codec, voice_platform),
+
+},
+
+{
+	.name = "loop_test",//codec name
+	.stream_name = "loop_test",
+	//.nonatomic = true,
+	//.dynamic = 1,
+	//.dpcm_playback = 1,
+	//.ops = &zx29_ops,
+	.ops = &voice_ops,
+
+	.init = zx29_init_paiftx,
+	
+
+	//SND_SOC_DAILINK_REG(cpu_i2s0, codec, dummy),
+	SND_SOC_DAILINK_REG(voice_cpu, codec, dummy),
+
+},
+
+};
+
+
+
+static struct snd_soc_card zx29_soc_card = {
+	.name = "zx29-sound-card",
+	.owner = THIS_MODULE,
+	.dai_link = zx29_dai_link,
+	.num_links = ARRAY_SIZE(zx29_dai_link),
+#ifdef USE_ALSA_VOICE_FUNC
+	 .controls = vp_snd_controls,
+	 .num_controls = ARRAY_SIZE(vp_snd_controls),
+#endif	
+};
+
+static const struct of_device_id zx29_ti3100_of_match[] = {
+	{ .compatible = "zxic,zx29_ti3100", .data = &zx29_platform_data },
+	{ .compatible = "zxic,zx29_ti3104", .data = &zx29_platform_data },
+	{},
+};
+MODULE_DEVICE_TABLE(of, zx29_ti3100_of_match);
+
+static void zx29_i2s_top_pin_cfg(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct pinctrl *p;
+	struct pinctrl_state *s;
+	int ret = 0;
+
+
+	struct resource *res;
+	void __iomem	*reg_base;
+	unsigned int val;
+
+
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "soc_sys");
+	if (!res) {
+		dev_err(dev, "Reg region missing (%s)\n", "soc_sys");
+		//return -ENXIO;
+	}
+
+	#if 0
+	reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(reg_base )) {
+			dev_err(dev, "Reg region ioremap (%s) err=%li\n", "soc_sys",PTR_ERR(reg_base ));
+		//return PTR_ERR(reg_base );
+	}
+
+	#else
+	reg_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	#endif
+	 
+//#if 1 //CONFIG_USE_PIN_I2S0
+#ifdef 	CONFIG_USE_TOP_I2S0
+
+	dev_info(dev, "%s: arm i2s1 to top i2s0!!\n", __func__); 
+	//9300
+		 
+	//top i2s1 cfg
+	val = zx_read_reg(reg_base+ZX29_I2S_TOP_LOOP_REG);
+	val &= ~(0x7<<0);
+	val |= 0x1<<0; //	inter arm_i2s1--top i2s1
+	zx_write_reg(reg_base+ZX29_I2S_TOP_LOOP_REG, val);
+#else //(CONFIG_USE_PIN_I2S1)
+    //8501evb    	
+
+	dev_info(dev, "%s: arm i2s1 to top i2s1!\n", __func__); 
+			 
+	//top i2s2 cfg
+	val = zx_read_reg(reg_base+ZX29_I2S_TOP_LOOP_REG);
+	//val &= 0xfffffff8;
+	val &= ~(0x7<<16);	
+	val |= 0x1<<16;//	inter arm_i2s1--top i2s2
+	zx_write_reg(reg_base+ZX29_I2S_TOP_LOOP_REG, val);
+#endif
+
+	p = devm_pinctrl_get(dev);
+	if (IS_ERR(p)) {
+		dev_err(dev, "%s: pinctrl get failure ,p=0x%llx,dev=0x%llx!!\n", __func__,p,dev);
+		return;
+	}
+	
+	dev_info(dev, "%s: get pinctrl ,p=0x%llx,dev=0x%llx!!\n", __func__,p,dev); 
+
+	s = pinctrl_lookup_state(p, "top_i2s");
+	if (IS_ERR(s)) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " get state failure!!\n");
+		return;
+	}
+	ret = pinctrl_select_state(p, s);
+	if (ret < 0) {
+		devm_pinctrl_put(p);
+		dev_err(dev, " select state failure!!\n");
+		return;
+	}
+	dev_info(dev, "%s: set pinctrl end!\n", __func__);	
+
+}
+#if 0
+static int codec_power_on(struct zx29_board_data * board,bool on_off)
+{
+	int ret = 0;
+	//struct zx29_board_data *board = dev_get_drvdata(dev);
+	struct device *dev = board->dev;
+
+	dev_info(dev, "%s:start %s board gpio_pwen=%d,gpio_pdn=%d on_off=%d\n",__func__,board->name,board->gpio_pwen,board->gpio_pdn,on_off);
+
+	if(on_off){
+
+		ret = gpio_direction_output(board->gpio_pwen, 1);	
+		if (ret < 0) {
+				dev_err(dev,"gpio_pwen %d direction fail set to 1: %d\n",board->gpio_pwen, ret);
+				return ret;
+		 }
+		
+		ret = gpio_direction_output(board->gpio_pdn, 1);	
+		if (ret < 0) {
+				dev_err(dev,"gpio_pdn %d direction fail set to 1: %d\n",board->gpio_pdn, ret);
+				return ret;
+		 }
+
+		
+	}
+	else{
+		ret = gpio_direction_output(board->gpio_pwen, 0);	
+		if (ret < 0) {
+				dev_err(dev,"gpio_pwen %d direction fail set to 0: %d\n",board->gpio_pwen, ret);
+				return ret;
+		 }
+		
+		ret = gpio_direction_output(board->gpio_pdn, 0);	
+		if (ret < 0) {
+				dev_err(dev,"gpio_pdn %d direction fail set to 0: %d\n",board->gpio_pdn, ret);
+				return ret;
+		 }
+
+	
+	}
+
+	return ret;
+
+}
+#endif
+
+
+#ifdef  CONFIG_PA_SA51034
+//sa51034
+#define SA51034_DEBUG
+
+#define SA51034_01_LATCHED_FAULT		0x01
+#define SA51034_02_STATUS_LOAD_DIAGNOSTIC      0x02
+#define SA51034_03_CONTROL			0x03
+#define SA51034_MAX_REGISTER SA51034_03_CONTROL
+
+struct sa51034_priv {
+	struct i2c_client *i2c;
+	struct regmap *regmap;
+	int pwen_gpio;//add new
+	int mute_gpio;
+	int fs;
+
+};
+static int sa51034_set_mute(struct sa51034_priv *sa51034,int mute);
+static int sa51034_get_mute(struct sa51034_priv *sa51034,int *mute); 
+
+
+
+
+struct sa51034_priv *g_sa51034 = NULL;
+/* ak4940 register cache & default register settings */
+static const struct reg_default sa51034_reg[] = {
+	{ 0x01, 0x00 },  /* SA51034_00_LATCHED_FAULT	*/
+	{ 0x02, 0x00 },  /* SA51034_01_STATUS_LOAD_DIAGNOSTIC	*/
+	{ 0x03, 0x00 },  /* SA51034_02_CONTROL			*/
+	
+};
+	
+static const char * const pa_gain_select_texts[] = {
+	"20dB", "26dB","30dB", "36dB",
+};
+static const char * const power_limit_select_texts[] = {
+	"PL-5V", "PL-5.9V","PL-7V", "PL-8.4V","PL-9.8V", "PL-11.8V","PL-14V", "PL-disV",
+};
+
+static const struct soc_enum pa_gain_enum[] = {
+	SOC_ENUM_SINGLE(SA51034_03_CONTROL, 6,
+	ARRAY_SIZE(pa_gain_select_texts), pa_gain_select_texts),
+};
+static const struct soc_enum power_limit_enum[] = {
+	SOC_ENUM_SINGLE(SA51034_03_CONTROL, 3,
+	ARRAY_SIZE(power_limit_select_texts), power_limit_select_texts),
+};
+	
+static const char * const reg_select[] = {
+	"read PA Reg 01:03",
+};
+
+static const struct soc_enum pa_enum2[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(reg_select),reg_select),
+};
+
+static int get_reg(
+	struct snd_kcontrol       *kcontrol,
+	struct snd_ctl_elem_value  *ucontrol)
+{
+	struct snd_soc_component *component; 
+	struct device *dev;    
+
+	
+
+	u32    currMode = ucontrol->value.enumerated.item[0];
+	int    i, ret;
+	int	   regs, rege;
+	unsigned int value;
+
+
+	if(g_sa51034 == NULL){
+	   pr_err("g_sa51034 null return %s\n", __func__);	  
+	   return -1;
+	}
+	dev = &g_sa51034->i2c->dev; 
+
+	component =  snd_soc_lookup_component(dev, NULL); 	
+	regs = 0x1;
+	rege = 0x4;
+
+	for (i = regs; i < rege; i++) {
+		value = snd_soc_component_read(component, i);
+		if (value < 0) {
+			pr_err("pa %s(%d),err value=%d\n", __func__, __LINE__, value);
+			return value;
+		}
+		pr_info("pa 2c_read Addr,Reg=(%x, %x)\n", i, value);
+	}
+	
+	return 0;
+}
+
+
+
+  int pa_get_enum_double(struct snd_kcontrol *kcontrol,
+	  struct snd_ctl_elem_value *ucontrol)
+  {
+	  //struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+  
+	  struct snd_soc_component *component; 
+	  struct device *dev;	 
+
+	  
+
+	  
+	  struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	  unsigned int val, item;
+	  unsigned int reg_val;
+	  int ret;
+	  if(g_sa51034 == NULL){
+ 	  	 pr_err("g_sa51034 null return %s\n", __func__);	
+ 		 return -1;
+	  }
+	  dev = &g_sa51034->i2c->dev; 
+
+	  
+	  component = snd_soc_lookup_component(dev, NULL);  
+	  reg_val = snd_soc_component_read(component, e->reg);
+
+
+	  if (reg_val < 0) {
+	  	  pr_err("pa %s(%d),err reg_val=%d\n", __func__, __LINE__, reg_val);
+		  return reg_val;
+	  }
+
+	  
+	  val = (reg_val >> e->shift_l) & e->mask;
+	  item = snd_soc_enum_val_to_item(e, val);
+	  ucontrol->value.enumerated.item[0] = item;
+	  if (e->shift_l != e->shift_r) {
+		  val = (reg_val >> e->shift_r) & e->mask;
+		  item = snd_soc_enum_val_to_item(e, val);
+		  ucontrol->value.enumerated.item[1] = item;
+	  }
+  
+	  return 0;
+  }
+
+  int pa_put_enum_double(struct snd_kcontrol *kcontrol,
+	  struct snd_ctl_elem_value *ucontrol)
+  {
+	  //struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	  
+	  struct snd_soc_component *component; 
+	  struct device *dev;	 
+	  struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	  unsigned int *item = ucontrol->value.enumerated.item;
+	  unsigned int val;
+	  unsigned int mask;
+
+	  if(g_sa51034 == NULL){
+ 	  	 pr_err("g_sa51034 null return %s\n", __func__);	
+ 		 return -1;
+	  }
+	  dev = &g_sa51034->i2c->dev; 
+	  component = snd_soc_lookup_component(dev, NULL);  
+  
+	  if (item[0] >= e->items)
+		  return -EINVAL;
+	  val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+	  mask = e->mask << e->shift_l;
+	  if (e->shift_l != e->shift_r) {
+		  if (item[1] >= e->items)
+			  return -EINVAL;
+		  val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
+		  mask |= e->mask << e->shift_r;
+	  }
+  
+	  return snd_soc_component_update_bits(component, e->reg, mask, val);
+  }
+
+
+static int pa_SetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	  int mute = 0,ret = 0;
+	  
+
+
+	  if(g_sa51034 == NULL){
+ 	  	 pr_err("g_sa51034 null return %s\n", __func__);	
+ 		 return -1;
+	  }	  
+	  mute = ucontrol->value.integer.value[0];
+	  ret = sa51034_set_mute(g_sa51034,mute);
+	  
+	  if(ret < 0)
+	  {
+		printk(KERN_ERR "sa51034_set_mute fail ret=%d,mute=%d\n",ret,mute);
+		return ret;
+	  }
+	  return 0;
+}
+
+static int pa_GetMute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{ 	
+	int mute = 0,ret = 0;
+	
+	if(g_sa51034 == NULL){
+		pr_err("g_sa51034 null return %s\n", __func__);    
+		return -1;
+	}
+	ret = sa51034_get_mute(g_sa51034,&mute);
+	
+	if(ret < 0)
+	{
+	  printk(KERN_ERR "sa51034_get_mute fail ret= %d\n",ret);
+	  return ret;
+	}
+	pr_info("[SA51034] %s mute gpio val=%d,integer.value[0]=%d\n", __func__, mute,ucontrol->value.integer.value[0]);
+
+	ucontrol->value.integer.value[0] = mute;
+
+	return 0;
+}
+
+
+
+
+
+const struct snd_kcontrol_new pa_controls[] =
+{
+	SOC_ENUM_EXT("PA gain", pa_gain_enum[0], pa_get_enum_double, pa_put_enum_double),
+    SOC_ENUM_EXT("Power limit", power_limit_enum[0], pa_get_enum_double, pa_put_enum_double),
+	SOC_ENUM_EXT("PA Reg Read", pa_enum2[0], get_reg, NULL),
+	SOC_SINGLE_EXT("pa mute", 0, 0, 1, 0,pa_GetMute, pa_SetMute),
+
+
+};
+	
+int pa_controls_size = sizeof(pa_controls) / sizeof(pa_controls[0]);
+
+
+
+
+static bool sa51034_volatile(struct device *dev, unsigned int reg)
+{
+	bool ret;
+
+#ifdef SA51034_DEBUG
+	ret = true;
+#else
+	ret = false;
+#endif
+
+	return ret;
+}
+
+static bool sa51034_readable(struct device *dev, unsigned int reg)
+{
+	if (reg <= SA51034_MAX_REGISTER)
+		return true;
+	else
+		return false;
+}
+
+static bool sa51034_writeable(struct device *dev, unsigned int reg)
+{
+	if (reg <= SA51034_MAX_REGISTER)
+		return true;
+	else
+		return false;
+}
+
+
+static const struct regmap_config sa51034_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = SA51034_MAX_REGISTER,
+	.volatile_reg = sa51034_volatile,
+	.writeable_reg = sa51034_writeable,
+	.readable_reg = sa51034_readable,
+
+	.reg_defaults = sa51034_reg,
+	.num_reg_defaults = ARRAY_SIZE(sa51034_reg),
+	.cache_type = REGCACHE_RBTREE,
+
+};
+
+static const struct snd_soc_component_driver pa_asoc_component = {
+	.name = "pa_component",
+
+
+	//.controls = pa_controls,
+	//.num_controls = ARRAY_SIZE(pa_controls),
+
+
+};
+
+static const struct of_device_id sa51034_i2c_dt_ids[] = {
+	{ .compatible = "sa51034"},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sa51034_i2c_dt_ids);
+static int sa51034_gpio_request(struct sa51034_priv *sa51034)
+{
+	struct device *dev;
+	struct device_node *np;
+    int ret;
+	dev = &(sa51034->i2c->dev);
+
+	np = dev->of_node;
+
+	if (!np)
+		return 0;
+
+	pr_info( "Read PDN pin from device tree\n");
+
+
+	sa51034->pwen_gpio = of_get_named_gpio(np, "sa51034,ctrl-gpio", 0);
+	if (sa51034->pwen_gpio < 0) {
+	    pr_err(  "sa51034 pwen pin of_get_named_gpio fail\n");
+		
+		sa51034->pwen_gpio = -1;
+		return -1;
+	}
+
+	if (!gpio_is_valid(sa51034->pwen_gpio)) {
+		pr_err(  "sa51034 pwen_gpio pin(%u) is invalid\n", sa51034->pwen_gpio);
+		sa51034->pwen_gpio = -1;
+		return -1;
+	}
+	sa51034->mute_gpio = of_get_named_gpio(np, "sa51034,ctrl-gpio", 1);
+	if (sa51034->mute_gpio < 0) {
+		
+	    pr_err(  "sa51034 mute_gpio pin of_get_named_gpio fail\n");
+		sa51034->mute_gpio = -1;
+		return -1;
+	}
+
+	if (!gpio_is_valid(sa51034->mute_gpio)) {
+		pr_err(  "sa51034 mute_gpio pin(%u) is invalid\n", sa51034->mute_gpio);
+		sa51034->mute_gpio = -1;
+		return -1;
+	}
+
+	
+	pr_info( "sa51034 get pwen_gpio pin(%u) mute_gpio pin(%u)\n", sa51034->pwen_gpio,sa51034->mute_gpio);
+
+	if (sa51034->pwen_gpio != -1) {
+		ret = devm_gpio_request(dev,sa51034->pwen_gpio, "sa51034 pwen");
+		if (ret < 0){
+			pr_err(  "sa51034 pwen_gpio request fail,ret=%d\n",ret);
+			return ret;			
+		}
+		pr_info("\t[sa51034] %s :pwen_gpio gpio_request ret = %d\n", __func__, ret);
+		gpio_direction_output(sa51034->pwen_gpio, 0);
+	}
+
+	
+	if (sa51034->mute_gpio != -1) {
+		ret = devm_gpio_request(dev,sa51034->mute_gpio, "sa51034 mute");
+		if (ret < 0){
+			pr_err(  "sa51034 mute_gpio request fail,ret=%d\n",ret);
+			return ret;			
+		}
+
+		pr_info("\t[AK4940] %s : mute_gpio gpio_request ret = %d\n", __func__, ret);
+		gpio_direction_output(sa51034->mute_gpio, 1);
+	}
+  
+	
+	return 0;
+}
+
+static int sa51034_set_mute(struct sa51034_priv *sa51034,int mute) 
+{
+	//struct snd_soc_component *component = dai->component;
+	//struct ak4940_priv *ak4940 = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+	//int ndt;
+
+	pr_info("[SA51034] %s mute=%d\n", __func__, mute);
+	if (sa51034->mute_gpio == -1) {
+			pr_err(  "sa51034 %s mute_gpio invalid return\n",__func__);
+			return -1;	
+	}
+
+	//ndt = 4080000 / sa51034->fs;
+	if (mute) {
+		/* SMUTE: 1 , MUTE */
+		ret = gpio_direction_output(sa51034->mute_gpio, 1);
+		//mdelay(ndt);
+	} else{
+		/* SMUTE:  0  ,NORMAL operation */
+		ret = gpio_direction_output(sa51034->mute_gpio, 0);
+		//mdelay(ndt);
+	}
+	return ret;
+}
+
+static int sa51034_get_mute(struct sa51034_priv *sa51034,int *mute) 
+{
+
+	int ret = 0;
+	if (sa51034->mute_gpio == -1) {
+			pr_err(  "sa51034 %s mute_gpio invalid return\n",__func__);
+			return -1;	
+	}
+	
+	*mute = gpio_get_value(sa51034->mute_gpio);
+	pr_info("[SA51034] %s mute gpio val=%d\n", __func__, *mute);
+	
+	return ret;
+}
+
+static int sa51034_set_pwen(struct sa51034_priv *sa51034,int en) 
+{
+	//struct snd_soc_component *component = dai->component;
+	//struct ak4940_priv *ak4940 = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+	//int ndt;
+
+	pr_info("\t[SA51034] %s en[%s]\n", __func__, en ? "ON":"OFF");
+	if (sa51034->pwen_gpio == -1) {
+			pr_err(  "sa51034 %s pwen_gpio invalid return\n",__func__);
+			return -1;	
+	}
+	//ndt = 4080000 / sa51034->fs;
+	if (en) {
+		/* SMUTE: 1 , MUTE */
+		ret = gpio_direction_output(sa51034->pwen_gpio, 1);
+		//mdelay(ndt);
+	} else{
+		/* SMUTE:  0  ,NORMAL operation */
+		ret = gpio_direction_output(sa51034->pwen_gpio, 0);
+		//mdelay(ndt);
+	}
+	return ret;
+}
+
+
+
+static int sa51034_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+	struct sa51034_priv *sa51034;
+	int ret = 0;
+	unsigned int val;
+
+	pr_info("\t[sa51034] %s(%d),i2c->addr=0x%x\n", __func__, __LINE__,i2c->addr);
+
+	sa51034 = devm_kzalloc(&i2c->dev, sizeof(struct sa51034_priv), GFP_KERNEL);
+	if (sa51034 == NULL)
+		return -ENOMEM;
+
+
+	sa51034->regmap = devm_regmap_init_i2c(i2c, &sa51034_regmap);
+
+	if (IS_ERR(sa51034->regmap)) {
+		devm_kfree(&i2c->dev, sa51034);
+		return PTR_ERR(sa51034->regmap);
+	}
+
+
+	i2c_set_clientdata(i2c, sa51034);
+	sa51034->i2c = i2c;
+	ret = devm_snd_soc_register_component(&i2c->dev, &pa_asoc_component,
+					      NULL, 0);
+	if (ret) {
+		pr_err( "pa component register failed,ret=%d\n",ret);
+		return ret;
+	}
+
+	pr_info("[sa51034] %s(%d) pa component register end,ret=0x%x\n", __func__, __LINE__,ret);
+
+	sa51034_gpio_request(sa51034);
+
+
+	sa51034_set_pwen(sa51034,1); 
+
+	//sa51034_set_mute(sa51034,0);
+
+	g_sa51034 = sa51034;
+
+	
+	pr_info("\t[sa51034] %s end\n", __func__);
+	return ret;
+}
+
+static const struct i2c_device_id sa51034_i2c_id[] = {
+
+	{ "sa51034", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, sa51034_i2c_id);
+
+static struct i2c_driver sa51034_i2c_driver = {
+	.driver = {
+		.name = "sa51034",
+		.of_match_table = of_match_ptr(sa51034_i2c_dt_ids),
+	},
+	.probe = sa51034_i2c_probe,
+	//.remove = sa51034_i2c_remove,
+	.id_table = sa51034_i2c_id,
+};
+
+static int  sa51034_init(void)
+{
+	pr_info("\t[sa51034] %s(%d)\n", __func__, __LINE__);
+
+	return i2c_add_driver(&sa51034_i2c_driver);
+}
+
+#endif
+static int zx29_audio_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device_node *np = pdev->dev.of_node;
+	struct snd_soc_card *card = &zx29_soc_card;
+	struct zx29_board_data *board;
+	const struct of_device_id *id;
+	enum of_gpio_flags flags;
+	unsigned int idx;
+
+	struct device *dev = &pdev->dev;
+	dev_info(&pdev->dev,"zx29_audio_probe start!\n");
+
+	card->dev = &pdev->dev;
+
+	board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
+	if (!board)
+		return -ENOMEM;
+
+	if (np) {
+		zx29_dai_link[0].cpus->dai_name = NULL;
+		zx29_dai_link[0].cpus->of_node = of_parse_phandle(np,
+				"zxic,i2s-controller", 0);
+		if (!zx29_dai_link[0].cpus->of_node) {
+			dev_err(&pdev->dev,
+			   "Property 'zxic,i2s-controller' missing or invalid\n");
+			ret = -EINVAL;
+		}
+
+		zx29_dai_link[0].platforms->name = NULL;
+		zx29_dai_link[0].platforms->of_node = zx29_dai_link[0].cpus->of_node;
+
+		
+#if 0
+		zx29_dai_link[0].codecs->of_node = of_parse_phandle(np,
+				"zxic,audio-codec", 0);
+		if (!zx29_dai_link[0].codecs->of_node) {
+			dev_err(&pdev->dev,
+				"Property 'zxic,audio-codec' missing or invalid\n");
+			return -EINVAL;
+		}
+#endif	
+	}
+	
+
+
+
+
+
+	id = of_match_device(of_match_ptr(zx29_ti3100_of_match), &pdev->dev);
+	if (id)
+		*board = *((struct zx29_board_data *)id->data);
+	
+#if defined(CONFIG_SND_SOC_ZX29_TI3104)
+		board->name = "zx29_ti3104";
+#elif defined(CONFIG_SND_SOC_ZX29_TI3100)	
+		board->name = "zx29_ti3100";
+#else
+		board->name = "zx29_ti3100";
+
+#endif	
+	board->dev = &pdev->dev;
+
+	//platform_set_drvdata(pdev, board);
+	s_board = board;
+
+
+#if 0
+
+	board->gpio_pwen = of_get_gpio_flags(dev->of_node, 0, &flags);
+	if (!gpio_is_valid(board->gpio_pwen)) {
+		dev_err(dev,"  gpio_pwen no found\n");
+		return -EBUSY;
+	}
+	dev_info(dev, "board->gpio_pwen=0x%x  flags = %d\n",board->gpio_pwen,flags);
+	ret = devm_gpio_request(&pdev->dev,board->gpio_pwen, "codec_pwen");
+	if (ret < 0) {
+		dev_err(dev,"gpio_pwen request error.\n");
+		return ret;
+
+	}
+
+	board->gpio_pdn = of_get_gpio_flags(dev->of_node, 1, &flags);
+	if (!gpio_is_valid(board->gpio_pdn)) {
+		dev_err(dev,"  gpio_pdn no found\n");
+		return -EBUSY;
+	}
+	dev_info(dev, "board->gpio_pdn=0x%x  flags = %d\n",board->gpio_pdn,flags);
+	ret = devm_gpio_request(&pdev->dev,board->gpio_pdn, "codec_pdn");
+	if (ret < 0) {
+		dev_err(dev,"gpio_pdn request error.\n");
+		return ret;
+
+	}
+#endif
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+	if (ret){
+		dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+	    return ret;
+	}
+	zx29_i2s_top_pin_cfg(pdev);	
+
+	
+	//codec_power_on(board,1);
+#ifdef  CONFIG_PA_SA51034
+
+	dev_info(&pdev->dev,"zx29_audio_probe start sa51034_init!\n");
+
+	ret = sa51034_init();
+	if (ret != 0) {
+
+		pr_err("sa51034_init Failed to register I2C driver: %d\n", ret);
+		//return ret;
+
+	}
+	else{
+
+		for (idx = 0; idx < ARRAY_SIZE(pa_controls); idx++) {
+			ret = snd_ctl_add(card->snd_card,
+					  snd_ctl_new1(&pa_controls[idx],
+						       NULL));
+			if (ret < 0){
+				return ret;
+			}
+		}
+
+	}
+	 ret  = 0;
+
+#endif	
+	dev_info(&pdev->dev,"zx29_audio_probe end!\n");
+
+	return ret;
+}
+
+static struct platform_driver zx29_platform_driver = {
+	.driver		= {
+#if defined(CONFIG_SND_SOC_ZX29_TI3104)
+		.name	= "zx29_ti3104",
+#elif defined(CONFIG_SND_SOC_ZX29_TI3100)	
+		.name	= "zx29_ti3100",
+#else
+		.name	= "zx29_ti3100",
+
+#endif
+		.of_match_table = of_match_ptr(zx29_ti3100_of_match),
+		.pm	= &snd_soc_pm_ops,
+	},
+	.probe		= zx29_audio_probe,
+	//.remove 	= zx29_remove,
+};
+
+
+#if 0
+static int zx29_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	print_audio("Alsa  zx297520xx SoC Audio driver\n");
+
+	zx29_platform_data = pdev->dev.platform_data;
+	if (zx29_platform_data == NULL) {
+		printk(KERN_ERR "Alsa  zx297520xx SoC Audio: unable to find platform data\n");
+		return -ENODEV;
+	}
+
+	if (zx297520xx_setup_pins(zx29_platform_data, "codec") < 0)
+		return -EBUSY;
+
+	zx29_i2s_top_reg_cfg();
+
+	zx29_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!zx29_snd_device) {
+		printk(KERN_ERR "Alsa  zx297520xx SoC Audio: Unable to register\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(zx29_snd_device, &zxic_soc_card);
+//	platform_device_add_data(zx29xx_ti3100_snd_device, &zx29xx_ti3100, sizeof(zx29xx_ti3100));
+	ret = platform_device_add(zx29_snd_device);
+	if (ret) {
+		printk(KERN_ERR "Alsa  zx29 SoC Audio: Unable to add\n");
+		platform_device_put(zx29_snd_device);
+	}
+
+	return ret;
+}
+#endif
+
+
+
+module_platform_driver(zx29_platform_driver);
+
+MODULE_DESCRIPTION("zx29 ALSA SoC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:zx29-audio-ti3100");