[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 = ®ulator_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,®_val);
+ if (ret) {
+ return -EIO;
+ }
+ reg_val = reg_val&(~(1<<5));
+ ret = zx234290_i2c_write_simple(reg_addr, ®_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|0xff;
+ ret += zx234290_i2c_write_simple(0xf, ®);
+ ret = zx234290_i2c_read_simple(0xe, ®);
+ reg = reg|0x3;
+ ret += zx234290_i2c_write_simple(0xe, ®);
+ 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, ®);
+ 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, ®);
+ 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, ®);
+ 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, ®);
+ 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, ®_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, §ion, &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, §ion,
+ &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, §ion,
+ &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, ®, &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, ®, &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");