Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:pzskc383
kernel
linux-2.6.34-moorestown-aava-specific-changes.p...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File linux-2.6.34-moorestown-aava-specific-changes.patch of Package kernel
Index: linux-2.6.33/drivers/misc/mrst_test_ipc/ipc_module.c =================================================================== --- linux-2.6.33.orig/drivers/misc/mrst_test_ipc/ipc_module.c +++ linux-2.6.33/drivers/misc/mrst_test_ipc/ipc_module.c @@ -44,8 +44,13 @@ #include <linux/uaccess.h> #include <linux/time.h> + + #include <asm/ipc_defs.h> +#include <linux/device.h> +#include <linux/ipc_module.h> + static u32 major; #define MAX_FW_SIZE 264192 @@ -53,9 +58,11 @@ int init_ipc_driver(void); int ipc_ioctl(struct inode *inode, struct file *filp, u32 cmd, unsigned long arg); const struct file_operations ipc_fops = { +owner:THIS_MODULE, ioctl:ipc_ioctl, }; +static struct class *mid_ipc_class; int ipc_ioctl(struct inode *inode, struct file *filp, u32 cmd, unsigned long arg) @@ -71,6 +78,18 @@ int ipc_ioctl(struct inode *inode, struc u8 *fw_buf = NULL ; switch (cmd) { + case IPC_IOC_PMIC_REG_READ: + cmd = IPC_PMIC_REGISTER_READ; + break; + case IPC_IOC_PMIC_REG_WRITE: + cmd = IPC_PMIC_REGISTER_WRITE; + break; + default: + printk(KERN_INFO "ioctl <UNRECOGNIZED> received\n"); + break; + } + + switch (cmd) { case IPC_PMIC_REGISTER_READ: { printk(KERN_INFO @@ -169,6 +188,8 @@ int ipc_ioctl(struct inode *inode, struc static int __init ipc_module_init(void) { + struct device *dev; + printk(KERN_INFO "Init ipc_module\n"); major = register_chrdev(0, "mid_ipc", &ipc_fops); @@ -177,6 +198,23 @@ static int __init ipc_module_init(void) return major; } + mid_ipc_class = class_create(THIS_MODULE, "mid_ipc"); + if (IS_ERR(mid_ipc_class)) { + unregister_chrdev(major, "mid_ipc"); + return PTR_ERR(mid_ipc_class); + } + + dev = device_create(mid_ipc_class, + NULL, + MKDEV(major, 0), + NULL, + "mid_ipc" ); + if (IS_ERR(dev)) { + class_destroy(mid_ipc_class); + unregister_chrdev(major, "mid_ipc"); + return PTR_ERR(dev); + } + init_ipc_driver ( ) ; return SUCCESS; @@ -184,6 +222,8 @@ static int __init ipc_module_init(void) static void __exit ipc_module_exit(void) { + device_destroy(mid_ipc_class, MKDEV(major, 0)); + class_destroy(mid_ipc_class); unregister_chrdev(major, "mid_ipc"); } Index: linux-2.6.33/include/linux/Kbuild =================================================================== --- linux-2.6.33.orig/include/linux/Kbuild +++ linux-2.6.33/include/linux/Kbuild @@ -385,3 +385,5 @@ unifdef-y += xfrm.h objhdr-y += version.h header-y += wimax.h header-y += wimax/ +header-y += ipc_module.h + Index: linux-2.6.33/drivers/input/touchscreen/Kconfig =================================================================== --- linux-2.6.33.orig/drivers/input/touchscreen/Kconfig +++ linux-2.6.33/drivers/input/touchscreen/Kconfig @@ -586,4 +586,11 @@ config TOUCHSCREEN_MRSTOUCH Say Y here if you have a Intel Moorstown based Touchscreen If unsure, say N. +config TOUCHSCREEN_CY8CTMG110 + tristate "cy8ctmg110 touchscreen" + depends on I2C + default y + help + Say Y here if you have a cy8ctmg110 touchscreen capasitive touchscreen + If unsure, say N. endif Index: linux-2.6.33/drivers/input/touchscreen/Makefile =================================================================== --- linux-2.6.33.orig/drivers/input/touchscreen/Makefile +++ linux-2.6.33/drivers/input/touchscreen/Makefile @@ -1,5 +1,5 @@ # -# Makefile for the touchscreen drivers. +# Makefile for the touchscreen drivers.mororor # # Each configuration option enables a list of files. @@ -46,4 +46,5 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTON obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_MRSTOUCH) += mrstouch.o +obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o Index: linux-2.6.33/drivers/input/touchscreen/cy8ctmg110_ts.c =================================================================== --- /dev/null +++ linux-2.6.33/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -0,0 +1,651 @@ +/* + * cy8ctmg110_ts.c Driver for cypress touch screen controller + * Copyright (c) 2009 Aava Mobile + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <asm/io.h> +#include <linux/i2c.h> +#include <linux/timer.h> +#include <linux/gpio.h> +#include <linux/hrtimer.h> + +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <asm/ioctl.h> +#include <asm/ipc_defs.h> +#include <asm/uaccess.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <asm/ioctl.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/miscdevice.h> +#include <linux/module.h> + + +#define CY8CTMG110_DRIVER_NAME "cy8ctmg110" +/*#define MOORESTOWN_CDK*/ +/*#define CY8CTMG110_DEBUG_INFO*/ +/*#define POLL_TOUCH_EVENTS*/ + + +/*HW definations*/ +#define CY8CTMG110_RESET_PIN_GPIO 43 +#define CY8CTMG110_IRQ_PIN_GPIO 59 +#define CY8CTMG110_I2C_ADDR 0x38 +#define CY8CTMG110_I2C_ADDR_EXT 0x39 +#define CY8CTMG110_I2C_ADDR_ 0x2 /*i2c address first sample*/ +#define CY8CTMG110_I2C_ADDR__ 53 /*i2c address to FW where irq support missing*/ +#define CY8CTMG110_TOUCH_IRQ 21 +#define CY8CTMG110_TOUCH_LENGHT 9787 +#define CY8CTMG110_SCREEN_LENGHT 8424 + + +/*Touch coordinates*/ +#define CY8CTMG110_X_MIN 0 +#define CY8CTMG110_Y_MIN 0 +#define CY8CTMG110_X_MAX 864 +#define CY8CTMG110_Y_MAX 480 + + +/*cy8ctmg110 registers defination*/ +#define CY8CTMG110_TOUCH_WAKEUP_TIME 0 +#define CY8CTMG110_TOUCH_SLEEP_TIME 2 +#define CY8CTMG110_TOUCH_X1 3 +#define CY8CTMG110_TOUCH_Y1 5 +#define CY8CTMG110_TOUCH_X2 7 +#define CY8CTMG110_TOUCH_Y2 9 +#define CY8CTMG110_FINGERS 11 +#define CY8CTMG110_GESTURE 12 +#define CY8CTMG110_REG_MAX 13 + +#ifdef POLL_TOUCH_EVENTS + #define CY8CTMG110_POLL_TIMER_DELAY 1000*1000*100 + #define TOUCH_MAX_I2C_FAILS 50 +#endif + +/* Scale factors for coordinates */ +#define X_SCALE_FACTOR 9387/8424 +#define Y_SCALE_FACTOR 97/100 + +/* For tracing */ +static u16 g_y_trace_coord = 0; + +/* + * The touch position structure. + */ +struct ts_event { + int x1; + int y1; + int x2; + int y2; + bool event_sended; +}; + +/* + * The touch driver structure. + */ +struct cy8ctmg110 { + struct input_dev *input; + char phys[32]; + struct ts_event tc; + struct i2c_client *client; + bool pending; + spinlock_t lock; + bool initController; + bool sleepmode; +#ifdef POLL_TOUCH_EVENTS + struct hrtimer timer; + int i2c_fail_count; +#endif +}; + +/* + * cy8ctmg110_poweroff is the routine that is called when touch hardware + * will powered off + */ +static void cy8ctmg110_power(bool poweron) +{ +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_poweroff\n"); +#endif + if (poweron) + gpio_direction_output(CY8CTMG110_RESET_PIN_GPIO, 0); + else + gpio_direction_output(CY8CTMG110_RESET_PIN_GPIO, 1); +} +/* + * cy8ctmg110_write_req write regs to the i2c devices + * + */ +static int cy8ctmg110_write_req(struct cy8ctmg110 *tsc,unsigned char reg,unsigned char len,unsigned char *value) +{ + struct i2c_client *client = tsc->client; + unsigned int ret; + unsigned char i2c_data[]={0,0,0,0,0,0}; +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_init_req:\n"); +#endif + + i2c_data[0]=reg; + memcpy(i2c_data+1,value,len); + + { + struct i2c_msg msg[] = { + { client->addr, 0, len+1, i2c_data }, + }; + + ret = i2c_transfer(client->adapter, msg, 1); + + if (ret != 1) { + printk("cy8ctmg110 touch : i2c write data cmd failed \n"); + return ret; + } + } + + return 0; +} +/* + * cy8ctmg110_read_req read regs from i2c devise + * + */ +static int cy8ctmg110_read_req(struct cy8ctmg110 *tsc,unsigned char *i2c_data,unsigned char len ,unsigned char cmd) +{ + struct i2c_client *client = tsc->client; + unsigned int ret; + unsigned char regs_cmd[2]={0,0}; + + regs_cmd[0]=cmd; + + /* first write slave position to i2c devices*/ + { + struct i2c_msg msg1[] = { + { client->addr, 0, 1, regs_cmd }, + }; + + ret = i2c_transfer(client->adapter, msg1, 1); + + if (ret != 1) { +#ifdef POLL_TOUCH_EVENTS + tsc->i2c_fail_count++; +#endif + return ret; + } + } + + + /* Second read data from position*/ + { + struct i2c_msg msg2[] = { + { client->addr, I2C_M_RD, len, i2c_data }, + }; + + ret = i2c_transfer(client->adapter, msg2, 1); + + + if (ret != 1) { +#ifdef POLL_TOUCH_EVENTS + tsc->i2c_fail_count++; +#endif + return ret; + } + } + return 0; +} +/* + * cy8ctmg110_send_event delevery touch event to the userpace + * function use normal input interface + */ +static void cy8ctmg110_send_event(void *tsc) +{ + struct cy8ctmg110 *ts = tsc; + struct input_dev *input = ts->input; + u16 x, y; + u16 x2, y2; +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_send_event\n"); +#endif + x = ts->tc.x1; + y = ts->tc.y1; + + if(ts->tc.event_sended == false){ + input_report_key(input, BTN_TOUCH, 1); + ts->pending = true; + x2 = (u16)(y*X_SCALE_FACTOR); + y2 = (u16)(x*Y_SCALE_FACTOR); + input_report_abs(input, ABS_X, x2); + input_report_abs(input, ABS_Y, y2); + input_sync(input); + if(g_y_trace_coord) + printk("cy8ctmg110 touch position X:%d (was = %d) Y:%d (was = %d)\n", x2, y, y2, x); + } + +} + +/* + * cy8ctmg110_touch_pos check touch position from i2c devices + * + */ +static int cy8ctmg110_touch_pos(struct cy8ctmg110 *tsc) +{ + unsigned char reg_p[CY8CTMG110_REG_MAX]; + + memset(reg_p,0,CY8CTMG110_REG_MAX); + + /*Reading coordinates*/ + if (cy8ctmg110_read_req(tsc,reg_p,9,CY8CTMG110_TOUCH_X1)==0){ + int x = 0,y = 0; + y = reg_p[2]<<8 | reg_p[3]; + x = reg_p[0]<<8 | reg_p[1]; +#ifdef CY8CTMG110_DEBUG_INFO + printk("X:%d Y:%d (0x%x 0x%x 0x%x 0x%x)\n",x,y,reg_p[0],reg_p[1],reg_p[2],reg_p[3]); +#endif + /*number of touch*/ + if (reg_p[8]==0){ + if (tsc->pending == true){ + struct input_dev *input = tsc->input; + + input_report_key(input, BTN_TOUCH, 0); + tsc->tc.event_sended = true; +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_send_event ts->pending = true;\n"); +#endif + tsc->pending = false; + } + } + else if (tsc->tc.x1 != x || tsc->tc.y1 != y){ + tsc->tc.y1 = y; + tsc->tc.x1 = x; + tsc->tc.event_sended = false; + cy8ctmg110_send_event(tsc); +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110 touch position X:%d Y:%d\n",x,y); +#endif + } + } + else{ +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110 i2c reading error\n"); +#endif + } + + return 0; +} + +#ifdef POLL_TOUCH_EVENTS +/* + * if interup is'n in use the touch positions can reads by polling + * + */ +static enum hrtimer_restart cy8ctmg110_timer(struct hrtimer *handle) +{ + struct cy8ctmg110 *ts = container_of(handle, struct cy8ctmg110, timer); + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_timer\n"); +#endif + + cy8ctmg110_touch_pos(ts); + + if (ts->i2c_fail_count<TOUCH_MAX_I2C_FAILS) + hrtimer_start(&ts->timer, ktime_set(0, CY8CTMG110_POLL_TIMER_DELAY), + HRTIMER_MODE_REL); + + spin_unlock_irqrestore(&ts->lock, flags); + + return HRTIMER_NORESTART; +} +#endif +/* + * cy8ctmg110_init_controller set init value to touchcontroller + * + */ +static bool cy8ctmg110_set_sleepmode(struct cy8ctmg110 *ts) +{ + unsigned char reg_p[3]; + + if(ts->sleepmode==true){ + reg_p[0] = 0x00; reg_p[1] =0xff; reg_p[2] =5; + }else{ + reg_p[0] = 0x10;reg_p[1] =0xff;reg_p[2] =0; + } + + if (cy8ctmg110_write_req(ts,CY8CTMG110_TOUCH_WAKEUP_TIME,3,reg_p)){ + return false; + } + ts->initController = true; + + return true; +} + + + + +/* + * cy8ctmg110_irq_handler irq handling function + * + */ +static irqreturn_t cy8ctmg110_irq_handler(int irq, void *dev_id) +{ + struct cy8ctmg110 * tsc = (struct cy8ctmg110 *)dev_id; + +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_irq_handler\n"); +#endif + if (tsc->initController == false){ + if (cy8ctmg110_set_sleepmode(tsc) == true) + tsc->initController = true; + } + else + cy8ctmg110_touch_pos(tsc); + +#ifdef POLL_TOUCH_EVENTS + /*if interrupt supported in the touch controller + timer polling need to stop*/ + tsc->i2c_fail_count = TOUCH_MAX_I2C_FAILS; +#endif + return IRQ_HANDLED; +} + + +static int cy8ctmg110_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cy8ctmg110 *ts; + struct input_dev *input_dev; + int err; + client->irq = CY8CTMG110_TOUCH_IRQ; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + ts = kzalloc(sizeof(struct cy8ctmg110), GFP_KERNEL); + input_dev = input_allocate_device(); + + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + i2c_set_clientdata(client, ts); + + ts->input = input_dev; + ts->pending = false; + ts->sleepmode = false; + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + input_dev->name = CY8CTMG110_DRIVER_NAME" Touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + + spin_lock_init(&ts->lock); + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | + BIT_MASK(EV_REL) | BIT_MASK(EV_ABS); + + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_capability(input_dev, EV_KEY, KEY_F); + + + input_set_abs_params(input_dev, ABS_X, CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_Y, CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 0, 0); + + err = gpio_request(CY8CTMG110_RESET_PIN_GPIO, NULL); + + if (err) { + printk("GPIO pin %d failed to request.\n", CY8CTMG110_RESET_PIN_GPIO); + goto err_free_thread; + } + + cy8ctmg110_power(true); + + + ts->initController = false; +#ifdef POLL_TOUCH_EVENTS + ts->i2c_fail_count = 0; + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = cy8ctmg110_timer; + + hrtimer_start(&ts->timer, ktime_set(10, 0), + HRTIMER_MODE_REL); +#endif + err = gpio_request(CY8CTMG110_IRQ_PIN_GPIO, "touch_irq_key"); + + if (err < 0) { + printk("cy8ctmg110 gpio-keys: failed to request GPIO %d," + " error %d\n",CY8CTMG110_IRQ_PIN_GPIO, err); + goto err_free_thread; + } + + err= gpio_direction_input(CY8CTMG110_IRQ_PIN_GPIO); + + if (err < 0) { + pr_err("cy8ctmg110 gpio-keys: failed to configure input" + " direction for GPIO %d, error %d\n",CY8CTMG110_IRQ_PIN_GPIO, err); + gpio_free(CY8CTMG110_IRQ_PIN_GPIO); + goto err_free_thread; + } + + client->irq = gpio_to_irq(CY8CTMG110_IRQ_PIN_GPIO); + + if (client->irq < 0) { + err = client->irq; + pr_err("cy8ctmg110 gpio-keys: Unable to get irq number" + " for GPIO %d, error %d\n", + CY8CTMG110_IRQ_PIN_GPIO, err); + gpio_free(CY8CTMG110_IRQ_PIN_GPIO); + goto err_free_thread; + } + + err = request_irq(client->irq, cy8ctmg110_irq_handler, + IRQF_TRIGGER_RISING | IRQF_SHARED, + "touch_reset_key", + ts); + + if (err < 0) { + dev_err(&client->dev, "cy8ctmg110 irq %d busy? error %d\n", client->irq,err); + goto err_free_thread; + } + + err = input_register_device(input_dev); + + if (err) + goto err_free_irq; + + return 0; + + err_free_irq: + printk("cy8ctmg110 err_free_irq\n"); + free_irq(client->irq, ts); + err_free_thread: + printk("cy8ctmg110 err_free_thread\n"); + err_free_mem: + printk("cy8ctmg110 err_free_mem\n"); + input_free_device(input_dev); + kfree(ts); + + return err; +} +/* + * cy8ctmg110_suspend + * + */ +static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg) +{ + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} +/* + * cy8ctmg110_resume + * + */ +static int cy8ctmg110_resume(struct i2c_client *client) +{ + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + return 0; +} +/* + * cy8ctmg110_remove + * + */ +static int cy8ctmg110_remove(struct i2c_client *client) +{ + struct cy8ctmg110 *ts = i2c_get_clientdata(client); + +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_remove\n"); +#endif + + cy8ctmg110_power(false); +#ifdef POLL_TOUCH_EVENTS + hrtimer_cancel(&ts->timer); +#endif + + free_irq(client->irq, ts); + input_unregister_device(ts->input); + kfree(ts); + + return 0; +} + +static struct i2c_device_id cy8ctmg110_idtable[] = { + { CY8CTMG110_DRIVER_NAME, 1 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable); + +static struct i2c_driver cy8ctmg110_driver = { + .driver = { + .owner = THIS_MODULE, + .name = CY8CTMG110_DRIVER_NAME, + .bus = &i2c_bus_type, + }, + .id_table = cy8ctmg110_idtable, + .probe = cy8ctmg110_probe, + .remove = cy8ctmg110_remove, + .suspend = cy8ctmg110_suspend, + .resume = cy8ctmg110_resume, +}; + +static int __init cy8ctmg110_init(void) +{ + printk("cy8ctmg110_init\n"); + + return i2c_add_driver(&cy8ctmg110_driver); +} + +static void __exit cy8ctmg110_exit(void) +{ +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_exit\n"); +#endif + i2c_del_driver(&cy8ctmg110_driver); +} + +module_init(cy8ctmg110_init); +module_exit(cy8ctmg110_exit); + + +struct i2c_board_info __initdata koski_i2c_board_info2[] = { + { + I2C_BOARD_INFO(CY8CTMG110_DRIVER_NAME, CY8CTMG110_I2C_ADDR), + .irq = CY8CTMG110_TOUCH_IRQ + }, +}; + + +static int __init koski_i2c_init(void) +{ + printk("init koski board\n"); + +#ifdef MOORESTOWN_CDK + /*init koski i2c*/ + i2c_register_board_info(1, koski_i2c_board_info2, + ARRAY_SIZE(koski_i2c_board_info2)); +#else + /*init koski i2c*/ + i2c_register_board_info(0, koski_i2c_board_info2, + ARRAY_SIZE(koski_i2c_board_info2)); +#endif + return 0; +} + +module_init(koski_i2c_init); + +MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>"); +MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver"); +MODULE_LICENSE("GPL v2"); + +// Temporary access from sysfs begin +static ssize_t fun_enable_coord_traces(struct class *class, const char *buf, size_t len) +{ + ssize_t status; + unsigned long value; + + status = strict_strtoul(buf, 0, &value); + printk("!!! enable_coord_traces = %d\n", (int)value); + + g_y_trace_coord = value; + + return len; +} + + +static struct class_attribute tiki_class_attrs[] = { + __ATTR(enable_coord_traces, 0644, NULL, fun_enable_coord_traces), + __ATTR_NULL, +}; + +static struct class tiki_class = { + .name = "tiki", + .owner = THIS_MODULE, + + .class_attrs = tiki_class_attrs, +}; + +static int __init tiki_sysfs_init(void) +{ + int status; + + status = class_register(&tiki_class); + if (status < 0) + return status; + + return status; +} +postcore_initcall(tiki_sysfs_init); +// Temporary access from sysfs end + Index: linux-2.6.33/drivers/gpio/gpiolib.c =================================================================== --- linux-2.6.33.orig/drivers/gpio/gpiolib.c +++ linux-2.6.33/drivers/gpio/gpiolib.c @@ -228,11 +228,14 @@ static ssize_t gpio_direction_show(struc if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; - else - status = sprintf(buf, "%s\n", - test_bit(FLAG_IS_OUT, &desc->flags) - ? "out" : "in"); - + else { + status = sprintf(buf, + "%s\n", + gpio_get_direction( (desc - gpio_desc) ) == + DIRECTION_OUT ? + "out" : + "in"); + } mutex_unlock(&sysfs_lock); return status; } @@ -1507,6 +1510,29 @@ void gpio_set_value_cansleep(unsigned gp } EXPORT_SYMBOL_GPL(gpio_set_value_cansleep); +enum gpio_direction gpio_get_direction(unsigned gpio) +{ + struct gpio_chip *chip; + struct gpio_desc *desc = &gpio_desc[gpio]; + + chip = gpio_to_chip(gpio); + might_sleep_if(extra_checks && chip->can_sleep); + + if (chip->get_direction) { + if (chip->get_direction(chip, gpio - chip->base) == + DIRECTION_IN) { + clear_bit(FLAG_IS_OUT, &desc->flags); + return DIRECTION_IN; + } else { + set_bit(FLAG_IS_OUT, &desc->flags); + return DIRECTION_OUT; + } + } + return test_bit(FLAG_IS_OUT, &desc->flags) ? + DIRECTION_OUT : + DIRECTION_IN; +} +EXPORT_SYMBOL_GPL(gpio_get_direction); #ifdef CONFIG_DEBUG_FS Index: linux-2.6.33/drivers/gpio/langwell_gpio.c =================================================================== --- linux-2.6.33.orig/drivers/gpio/langwell_gpio.c +++ linux-2.6.33/drivers/gpio/langwell_gpio.c @@ -107,6 +107,19 @@ static int lnw_gpio_direction_output(str return 0; } +static enum gpio_direction lnw_gpio_get_direction(struct gpio_chip *chip, + unsigned offset) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + u8 reg = offset / 32; + void __iomem *gpdr; + + gpdr = (void __iomem *)(&lnw->reg_base->GPDR[reg]); + if (readl(gpdr) & BIT(offset % 32)) + return DIRECTION_OUT; + return DIRECTION_IN; +} + static int lnw_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); @@ -240,6 +253,7 @@ static int __devinit lnw_gpio_probe(stru lnw->chip.label = dev_name(&pdev->dev); lnw->chip.direction_input = lnw_gpio_direction_input; lnw->chip.direction_output = lnw_gpio_direction_output; + lnw->chip.get_direction = lnw_gpio_get_direction; lnw->chip.get = lnw_gpio_get; lnw->chip.set = lnw_gpio_set; lnw->chip.to_irq = lnw_gpio_to_irq; Index: linux-2.6.33/drivers/gpio/langwell_pmic_gpio.c =================================================================== --- linux-2.6.33.orig/drivers/gpio/langwell_pmic_gpio.c +++ linux-2.6.33/drivers/gpio/langwell_pmic_gpio.c @@ -165,15 +165,33 @@ static int pmic_gpio_direction_output(st return rc; } -static int pmic_gpio_get(struct gpio_chip *chip, unsigned offset) +static enum gpio_direction pmic_gpio_get_direction(struct gpio_chip *chip, + unsigned offset) { - /* we only have 8 GPIO can use as input */ if (offset > 8) { - printk(KERN_ERR - "%s: only pin 0-7 support input\n", __func__); - return -1; + /* GPOWSs and GPOs are always outputs */ + return DIRECTION_OUT; } - return ipc_read_char(GPIO0 + offset) & GPIO_DIN; + if (ipc_read_char(GPIO0 + offset) & GPIO_DIR) + return DIRECTION_IN; + return DIRECTION_OUT; +} + +static int pmic_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + if (offset < 8) { + /* GPIOSW: Get state according to direction */ + if (pmic_gpio_get_direction( chip, offset ) == DIRECTION_IN) + return (ipc_read_char(GPIO0 + offset) & GPIO_DIN); + return (ipc_read_char(GPIO0 + offset) & GPIO_DOU); + } else if (offset < 16) { + /* GPOSW */ + return (ipc_read_char(GPOSWCTL0 + offset - 8) & GPOSW_DOU); + } else if (offset < 24) { + /* GPO */ + return (ipc_read_char(GPO) & (1 << (offset - 16))); + } + return 0; } static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) @@ -284,6 +302,7 @@ static int __devinit pmic_gpio_probe(str pg->chip.label = "langwell_pmic"; pg->chip.direction_input = pmic_gpio_direction_input; pg->chip.direction_output = pmic_gpio_direction_output; + pg->chip.get_direction = pmic_gpio_get_direction; pg->chip.get = pmic_gpio_get; pg->chip.set = pmic_gpio_set; pg->chip.to_irq = pmic_gpio_to_irq; Index: linux-2.6.33/drivers/gpio/pca953x.c =================================================================== --- linux-2.6.33.orig/drivers/gpio/pca953x.c +++ linux-2.6.33/drivers/gpio/pca953x.c @@ -144,6 +144,24 @@ static int pca953x_gpio_direction_output return 0; } +static enum gpio_direction pca953x_gpio_get_direction(struct gpio_chip *gc, + unsigned off) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + ret = pca953x_read_reg(chip, PCA953X_DIRECTION, ®_val); + if (ret == 0) { + if ( reg_val & (1u << off) ) + return DIRECTION_IN; + return DIRECTION_OUT; + } + return DIRECTION_IN; +} + static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip; @@ -199,6 +217,7 @@ static void pca953x_setup_gpio(struct pc gc->direction_input = pca953x_gpio_direction_input; gc->direction_output = pca953x_gpio_direction_output; + gc->get_direction = pca953x_gpio_get_direction; gc->get = pca953x_gpio_get_value; gc->set = pca953x_gpio_set_value; gc->to_irq = pca953x_gpio_to_irq; Index: linux-2.6.33/include/asm-generic/gpio.h =================================================================== --- linux-2.6.33.orig/include/asm-generic/gpio.h +++ linux-2.6.33/include/asm-generic/gpio.h @@ -101,6 +101,8 @@ struct gpio_chip { char **names; unsigned can_sleep:1; unsigned exported:1; + enum gpio_direction (*get_direction)(struct gpio_chip *chip, + unsigned offset); }; extern const char *gpiochip_is_requested(struct gpio_chip *chip, @@ -120,6 +122,7 @@ extern void gpio_free(unsigned gpio); extern int gpio_direction_input(unsigned gpio); extern int gpio_direction_output(unsigned gpio, int value); +extern enum gpio_direction gpio_get_direction(unsigned gpio); extern int gpio_get_value_cansleep(unsigned gpio); extern void gpio_set_value_cansleep(unsigned gpio, int value); Index: linux-2.6.33/include/linux/gpio.h =================================================================== --- linux-2.6.33.orig/include/linux/gpio.h +++ linux-2.6.33/include/linux/gpio.h @@ -3,6 +3,11 @@ /* see Documentation/gpio.txt */ +enum gpio_direction { + DIRECTION_IN = 0, + DIRECTION_OUT = 1, +}; + #ifdef CONFIG_GENERIC_GPIO #include <asm/gpio.h> @@ -126,6 +131,13 @@ static inline int irq_to_gpio(unsigned i return -EINVAL; } +static inline enum gpio_direction gpio_get_direction(unsigned gpio) +{ + /* GPIO can never have been requested or set as {in,out}put */ + WARN_ON(1); + return DIRECTION_IN; +} + #endif #endif /* __LINUX_GPIO_H */ Index: linux-2.6.33/include/linux/ipc_module.h =================================================================== --- /dev/null +++ linux-2.6.33/include/linux/ipc_module.h @@ -0,0 +1,60 @@ +/* + * include/linux/ipc_module.h + * + * Copyright (C) 2009 Aava Mobile Oy + * Written by Mikko Kovanen <mikko.kovanen@aavamobile.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef IPC_MODULE_H +#define IPC_MODULE_H + +#include <linux/types.h> + +#ifndef __IPC_DEFS_H__ +#define E_INVALID_CMD -249 +#define E_READ_USER_CMD -250 +#define E_READ_USER_DATA -251 +#define E_WRITE_USER_DATA -252 +#define E_PMIC_MALLOC -253 + +#define MAX_PMICREGS 5 +#define MAX_PMIC_MOD_REGS 4 + +struct pmicreg { + __u16 register_address; + __u8 value; +}; + +struct ipc_pmic_reg_data { + _Bool ioc; + struct pmicreg pmic_reg_data[MAX_PMICREGS]; + __u8 num_entries; +}; +#endif /* __IPC_DEFS_H__ */ + +#define IPC_IOC_MAGIC 'a' + + +#define IPC_IOC_PMIC_REG_READ _IOR(IPC_IOC_MAGIC, \ + 0, \ + struct ipc_pmic_reg_data) + +#define IPC_IOC_PMIC_REG_WRITE _IOW(IPC_IOC_MAGIC, \ + 1, \ + struct ipc_pmic_reg_data) + +#endif /* IPC_MODULE_H */ + Index: linux-2.6.33/drivers/input/keyboard/mrst_keypad.c =================================================================== --- linux-2.6.33.orig/drivers/input/keyboard/mrst_keypad.c +++ linux-2.6.33/drivers/input/keyboard/mrst_keypad.c @@ -40,6 +40,9 @@ #include <linux/device.h> #include <linux/err.h> #include <linux/gpio.h> +/*jhuot start*/ +#include <asm/ipc_defs.h> +/*jhuot end*/ /* * Keypad Controller registers @@ -116,10 +119,10 @@ MODULE_DEVICE_TABLE(pci, keypad_pci_tbl) #define keypad_writel(off, v) writel((v), keypad->mmio_base + (off)) #define MAX_MATRIX_KEY_NUM (8 * 8) -#define MAX_DIRECT_KEY_NUM (4) +#define MAX_DIRECT_KEY_NUM (2) -#define MAX_MATRIX_KEY_ROWS (8) -#define MAX_MATRIX_KEY_COLS (8) +#define MAX_MATRIX_KEY_ROWS (7) +#define MAX_MATRIX_KEY_COLS (7) #define DEBOUNCE_INTERVAL 100 #define KEY_HALFSHUTTER KEY_PROG1 @@ -167,7 +170,7 @@ static unsigned int mrst_keycode_fn[MAX_ /* direct key map */ static unsigned int mrst_direct_keycode[MAX_DIRECT_KEY_NUM] = { - KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_HALFSHUTTER, KEY_FULLSHUTTER, + KEY_VOLUMEUP, KEY_VOLUMEDOWN, //KEY_HALFSHUTTER, KEY_FULLSHUTTER, }; struct mrst_keypad { @@ -430,6 +433,8 @@ scan: if ((bits_changed & (1 << row)) == 0) continue; + printk(KERN_INFO "BUTTONS: " + "report key row %d, col %d\n", row, col); input_report_key(keypad->input_dev, lookup_matrix_keycode(keypad, row, col), new_state[col] & (1 << row)); @@ -513,6 +518,8 @@ static void mrst_keypad_scan_direct(stru for (i = 0; i < keypad->direct_key_num; i++) { if (bits_changed & (1 << i)) { + printk(KERN_INFO "BUTTONS: " + "scan_direct %d\n", keypad->direct_key_map[i]); input_report_key(keypad->input_dev, keypad->direct_key_map[i], (new_state & (1 << i))); @@ -528,10 +535,13 @@ static irqreturn_t mrst_keypad_irq_handl struct mrst_keypad *keypad = dev_id; unsigned long kpc = keypad_readl(KPC); + printk(KERN_INFO "BUTTONS: irq_handler, kpc %lu\n", kpc); if (kpc & KPC_DI) + printk(KERN_INFO "BUTTONS: mrst_keypad_scan_direct\n"); mrst_keypad_scan_direct(keypad); if (kpc & KPC_MI) + printk(KERN_INFO "BUTTONS: mrst_keypad_scan_matrix\n"); mrst_keypad_scan_matrix(keypad); return IRQ_HANDLED; @@ -544,13 +554,47 @@ static int mrst_keypad_gpio_init(void) MAX_MATRIX_KEY_COLS + MAX_DIRECT_KEY_NUM; /* explicitely tell which pins have been occupied... */ +/* for (i = KEYPAD_MATRIX_GPIO_IN_PIN; i < pins; i++, cnt++) { err = gpio_request(i, NULL); if (err) { printk(KERN_ERR "GPIO pin %d failed to request.\n", i); goto err_request; } - } + }*/ + + for (i = 0; i < MAX_MATRIX_KEY_ROWS; i++){ + err = gpio_request(KEYPAD_MATRIX_GPIO_IN_PIN + i,NULL); + + if (err) { + printk(KERN_ERR "GPIO pin %d failed to request.\n", i); + goto err_request; + } + + } + + for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) + { + err = gpio_request(KEYPAD_MATRIX_GPIO_OUT_PIN + i, NULL); + if (err) { + printk(KERN_ERR "GPIO pin %d failed to request.\n", i); + goto err_request; + } + + } + + for (i = 0; i < MAX_DIRECT_KEY_NUM; i++) + { + err = gpio_request(KEYPAD_DIRECT_GPIO_IN_PIN + i,NULL); + + if (err) { + printk(KERN_ERR "GPIO pin %d failed to request.\n", i); + goto err_request; + } + + + } + for (i = 0; i < MAX_MATRIX_KEY_ROWS; i++) gpio_direction_input(KEYPAD_MATRIX_GPIO_IN_PIN + i); @@ -642,6 +686,9 @@ static int __devinit mrst_keypad_probe(s struct mrst_keypad *keypad; struct input_dev *input_dev; int error; +/* jhuot start */ + struct ipc_io_bus_master_regs *p_reg_data; +/* jhuot end */ #ifndef MODULE printk(KERN_INFO MRST_KEYPAD_DRIVER_NAME "\n"); @@ -711,6 +758,18 @@ static int __devinit mrst_keypad_probe(s goto failed_free_dev; } + +/* jhuot start */ + /* Enable 75 kOhm internal pull-ups for KBD_DKIN0 and KBD_DKIN1 */ + /*bus: 0x4h, address: 0x20h, bits 0...3 */ + p_reg_data = kzalloc(sizeof(struct ipc_io_bus_master_regs), GFP_KERNEL); + /*01 = W, 04 = bus, 20 = address*/ + p_reg_data->ctrl_reg_addr = 0x01040020; + /*b3-b0 = 1010 (75kOhm pull-ups) = 0xAh*/ + p_reg_data->ctrl_reg_data = 0xA; + ipc_program_io_bus_master(p_reg_data); +/* jhuot end */ + /* Register the input device */ error = input_register_device(input_dev); if (error) { Index: linux-2.6.33/drivers/gpu/drm/mrst/Kconfig =================================================================== --- linux-2.6.33.orig/drivers/gpu/drm/mrst/Kconfig +++ linux-2.6.33/drivers/gpu/drm/mrst/Kconfig @@ -23,6 +23,20 @@ config IMG_DOES_NOT_SUPPORT_MENLOW help Choose Menlow +config DRM_MRST_AAVA + bool "Aava platform specific MIPI display" + depends on DRM_MRST + default n + help + Choose Aava platform MIPI display, temp option + +config DRM_MRST_CDK + bool "Aava platform specific MIPI display" + depends on DRM_MRST && !DRM_MRST_AAVA + default y + help + Choose CDK + config PVR_RELEASE string "Build IMG kernel services as release" depends on DRM_MRST Index: linux-2.6.33/drivers/gpu/drm/mrst/Makefile =================================================================== --- linux-2.6.33.orig/drivers/gpu/drm/mrst/Makefile +++ linux-2.6.33/drivers/gpu/drm/mrst/Makefile @@ -131,7 +131,6 @@ DRV_OBJS = $(DRMDRVDIR)/lnc_topaz.o \ $(DRMDRVDIR)/psb_hotplug.o \ $(DRMDRVDIR)/psb_intel_bios.o \ $(DRMDRVDIR)/psb_intel_display.o \ - $(DRMDRVDIR)/psb_intel_dsi.o \ $(DRMDRVDIR)/psb_intel_i2c.o \ $(DRMDRVDIR)/psb_intel_lvds.o \ $(DRMDRVDIR)/psb_intel_modes.o \ @@ -164,4 +163,5 @@ DRV_OBJS = $(DRMDRVDIR)/lnc_topaz.o \ mrst-objs += $(ENV_OBJS) $(COMMON_OBJS) $(BRIDGED_OBJS) $(SYSCONFIG_OBJS) $(SGX_OBJS) $(FB_OBJS) $(DRV_OBJS) obj-$(CONFIG_DRM_MRST) += mrst.o - +obj-$(CONFIG_DRM_MRST_AAVA) += $(DRMDRVDIR)/psb_intel_dsi_aava.o +obj-$(CONFIG_DRM_MRST_CDK) += $(DRMDRVDIR)/psb_intel_dsi.o Index: linux-2.6.33/drivers/gpu/drm/mrst/drv/psb_intel_dsi_aava.c =================================================================== --- /dev/null +++ linux-2.6.33/drivers/gpu/drm/mrst/drv/psb_intel_dsi_aava.c @@ -0,0 +1,996 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * jim liu <jim.liu@intel.com> + */ + +/* This enables setting backlights on with a delay at startup, + should be removed after resolving issue with backlights going off + after setting them on in initial mrst_dsi_set_power call */ +#define AAVA_BACKLIGHT_HACK + +#include <linux/backlight.h> +#include <drm/drmP.h> +#include <drm/drm.h> +#include <drm/drm_crtc.h> +#include <drm/drm_edid.h> + +#include <asm/ipc_defs.h> + +#ifdef AAVA_BACKLIGHT_HACK +#include <linux/workqueue.h> +#endif /* AAVA_BACKLIGHT_HACK */ + +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "ospm_power.h" + +#define DRM_MODE_ENCODER_MIPI 5 + +//#define DBG_PRINTS 1 +#define DBG_PRINTS 0 + +#define NEW_CRAP_SAMPLE_SETTINGS + +#define AAVA_EV_0_5 + +#define VSIZE 480 +#define HSIZE 864 +#define HFP_DOTS 10 +#define HBP_DOTS 10 +#define HSYNC_DOTS 4 +#define VFP_LINES 8 +#define VBP_LINES 8 +#define VSYNC_LINES 4 + +#define MIPI_LANES 2 +#define MIPI_HACT ((HSIZE * 3) / MIPI_LANES) +#define MIPI_HFP ((HFP_DOTS * 3) / MIPI_LANES) +#define MIPI_HBP ((HBP_DOTS * 3) / MIPI_LANES) +#define MIPI_HSPAD ((HSYNC_DOTS * 3) / MIPI_LANES) +#define MIPI_VFP VFP_LINES +#define MIPI_VSPAD VSYNC_LINES +#define MIPI_VBP VBP_LINES + +#define DISP_HPIX (HSIZE - 1) +#define DISP_VPIX (VSIZE - 1) +#define DISP_HBLANK_START DISP_HPIX +#define DISP_HBLANK_END (DISP_HBLANK_START + HFP_DOTS + HSYNC_DOTS + HBP_DOTS - 1) +#define DISP_HSYNC_START (DISP_HBLANK_START + HFP_DOTS - 1) +#define DISP_HSYNC_END (DISP_HSYNC_START + HSYNC_DOTS - 1) +#define DISP_VBLANK_START DISP_VPIX +#define DISP_VBLANK_END (DISP_VBLANK_START + VFP_LINES + VSYNC_LINES + VBP_LINES - 1) +#define DISP_VSYNC_START (DISP_VBLANK_START + VFP_LINES - 1) +#define DISP_VSYNC_END (DISP_VSYNC_START + VSYNC_LINES - 1) + +#define BRIGHTNESS_MAX_LEVEL 100 + +static unsigned int dphy_reg = 0x0d0a7f06; +static unsigned int mipi_clock = 0x2; + +#ifdef AAVA_BACKLIGHT_HACK +static void bl_work_handler(struct work_struct *work); +DECLARE_DELAYED_WORK(bl_work, bl_work_handler); +#endif /* AAVA_BACKLIGHT_HACK */ + +// Temporary access from sysfs begin +static struct drm_encoder *orig_encoder; +static void mrst_dsi_prepare(struct drm_encoder *encoder); +static void mrst_dsi_commit(struct drm_encoder *encoder); +static void mrst_dsi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +static void panel_reset(void); + +static ssize_t dphy_store(struct class *class, const char *buf, size_t len) +{ + ssize_t status; + unsigned long value; + + status = strict_strtoul(buf, 16, &value); + dphy_reg = value; + printk("!!! dphy_reg = %x, clock = %x\n", dphy_reg, mipi_clock); + + return len; +} + +static ssize_t clock_store(struct class *class, const char *buf, size_t len) +{ + ssize_t status; + unsigned long value; + + status = strict_strtoul(buf, 0, &value); + mipi_clock = value; + printk("!!! dphy_reg = %x, clock = %x\n", dphy_reg, mipi_clock); + + return len; +} + +static ssize_t apply_settings(struct class *class, const char *buf, size_t len) +{ + ssize_t status; + long value; + + printk("!!! dphy_reg = %x, clock = %x\n", dphy_reg, mipi_clock); + + status = strict_strtoul(buf, 0, &value); + if (value > 0) { + mrst_dsi_prepare(orig_encoder); + msleep(500); + if (value > 1) { + panel_reset(); + msleep(500); + } + mrst_dsi_mode_set(orig_encoder, NULL, NULL); + msleep(500); + mrst_dsi_commit(orig_encoder); + } + + return len; +} +// Temporary access from sysfs end + +static void panel_init(struct drm_device *dev) +{ +#if DBG_PRINTS + printk("panel_init\n"); +#endif /* DBG_PRINTS */ + + /* Flip page order */ + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x00008036); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000229); + +#ifdef NEW_CRAP_SAMPLE_SETTINGS + // 0xF0, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x005a5af0); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000329); +#endif + + /* Write protection key */ + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x005a5af1); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000329); + +#ifdef NEW_CRAP_SAMPLE_SETTINGS + // 0xFC, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x005a5afc); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000329); + + // 0xB7, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); +#ifdef DOES_NOT_WORK + /* Suggested by TPO, doesn't work as usual */ + REG_WRITE(0xb068, 0x110000b7); + REG_WRITE(0xb068, 0x00000044); +#else + REG_WRITE(0xb068, 0x770000b7); + REG_WRITE(0xb068, 0x00000044); +#endif + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000529); + + // 0xB6, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x000a0ab6); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000329); + + // 0xF2, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x081010f2); + REG_WRITE(0xb068, 0x4a070708); + REG_WRITE(0xb068, 0x000000c5); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000929); + + // 0xF8, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x024003f8); + REG_WRITE(0xb068, 0x01030a04); + REG_WRITE(0xb068, 0x0e020220); + REG_WRITE(0xb068, 0x00000004); + + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000d29); + + // 0xE2, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x398fc3e2); + REG_WRITE(0xb068, 0x0000916f); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000629); + +#ifdef DOES_NOT_WORK + /* Suggested by TPO, doesn't work as usual */ + // 0xE3, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x20f684e3); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000429); + + msleep(50); +#endif + + // 0xB0, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x000000b0); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000229); + + // 0xF4, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x240242f4); + REG_WRITE(0xb068, 0x78ee2002); + REG_WRITE(0xb068, 0x2a071050); + REG_WRITE(0xb068, 0x507fee10); + REG_WRITE(0xb068, 0x10300710); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00001429); + + // 0xBA, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x19fe07ba); + REG_WRITE(0xb068, 0x101c0a31); + REG_WRITE(0xb068, 0x00000010); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000929); + + // 0xBB, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x28ff07bb); + REG_WRITE(0xb068, 0x24280a31); + REG_WRITE(0xb068, 0x00000034); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000929); + + // 0xFB, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x535d05fb); + REG_WRITE(0xb068, 0x1b1a2130); + REG_WRITE(0xb068, 0x221e180e); + REG_WRITE(0xb068, 0x131d2120); + REG_WRITE(0xb068, 0x535d0508); + REG_WRITE(0xb068, 0x1c1a2131); + REG_WRITE(0xb068, 0x231f160d); + REG_WRITE(0xb068, 0x111b2220); + REG_WRITE(0xb068, 0x535c2008); + REG_WRITE(0xb068, 0x1f1d2433); + REG_WRITE(0xb068, 0x2c251a10); + REG_WRITE(0xb068, 0x2c34372d); + REG_WRITE(0xb068, 0x00000023); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00003129); + + // 0xFA, for new crap displays + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x525c0bfa); + REG_WRITE(0xb068, 0x1c1c232f); + REG_WRITE(0xb068, 0x2623190e); + REG_WRITE(0xb068, 0x18212625); + REG_WRITE(0xb068, 0x545d0d0e); + REG_WRITE(0xb068, 0x1e1d2333); + REG_WRITE(0xb068, 0x26231a10); + REG_WRITE(0xb068, 0x1a222725); + REG_WRITE(0xb068, 0x545d280f); + REG_WRITE(0xb068, 0x21202635); + REG_WRITE(0xb068, 0x31292013); + REG_WRITE(0xb068, 0x31393d33); + REG_WRITE(0xb068, 0x00000029); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00003129); +#endif + + /* Set DM */ + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_DATA_FIFO_FULL) == + HS_DATA_FIFO_FULL); + REG_WRITE(0xb068, 0x000100f7); + while ((REG_READ(GEN_FIFO_STAT_REG) & HS_CTRL_FIFO_FULL) == + HS_CTRL_FIFO_FULL); + REG_WRITE(0xb070, 0x00000329); +} + + +static void panel_reset_on(void) +{ + struct ipc_pmic_reg_data tmp_reg = {0}; +#if DBG_PRINTS + printk("panel_reset_on\n"); +#endif /* DBG_PRINTS */ + tmp_reg.ioc = 1; + tmp_reg.num_entries = 1; +#ifdef AAVA_EV_0_5 + tmp_reg.pmic_reg_data[0].register_address = 0xe6; + tmp_reg.pmic_reg_data[0].value = 0x01; +#else /* CDK */ + tmp_reg.pmic_reg_data[0].register_address = 0xf4; + if (ipc_pmic_register_read(&tmp_reg)) { + printk("panel_reset_on: failed to read pmic reg 0xf4!\n"); + return; + } + tmp_reg.pmic_reg_data[0].value &= 0xbf; +#endif /* AAVA_EV_0_5 */ + if (ipc_pmic_register_write(&tmp_reg, TRUE)) { + printk("panel_reset_on: failed to write pmic reg 0xe6!\n"); + } +} + + +static void panel_reset_off(void) +{ + struct ipc_pmic_reg_data tmp_reg = {0}; +#if DBG_PRINTS + printk("panel_reset_off\n"); +#endif /* DBG_PRINTS */ + tmp_reg.ioc = 1; + tmp_reg.num_entries = 1; +#ifdef AAVA_EV_0_5 + tmp_reg.pmic_reg_data[0].register_address = 0xe6; + tmp_reg.pmic_reg_data[0].value = 0x09; +#else /* CDK */ + tmp_reg.pmic_reg_data[0].register_address = 0xf4; + if (ipc_pmic_register_read(&tmp_reg)) { + printk("panel_reset_off: failed to read pmic reg 0xf4!\n"); + return; + } + tmp_reg.pmic_reg_data[0].value |= 0x40; +#endif /* AAVA_EV_0_5 */ + if (ipc_pmic_register_write(&tmp_reg, TRUE)) { + printk("panel_reset_off: failed to write pmic reg 0xe6!\n"); + } +} + + +static void panel_reset(void) +{ +#if DBG_PRINTS + printk("panel_reset\n"); +#endif /* DBG_PRINTS */ + + panel_reset_on(); + msleep(20); + panel_reset_off(); + msleep(20); +} + + +static void backlight_state(bool on) +{ + struct ipc_pmic_reg_data tmp_reg; + +#if DBG_PRINTS + printk("backlight_state\n"); +#endif /* DBG_PRINTS */ + + tmp_reg.ioc = 1; + tmp_reg.num_entries = 2; + tmp_reg.pmic_reg_data[0].register_address = 0x2a; + tmp_reg.pmic_reg_data[1].register_address = 0x28; + + if( on ) { +#if DBG_PRINTS + printk("backlight_state: ON\n"); +#endif /* DBG_PRINTS */ + tmp_reg.pmic_reg_data[0].value = 0xaa; +#ifdef AAVA_EV_0_5 + tmp_reg.pmic_reg_data[1].value = 0x30; +#else /* CDK */ + tmp_reg.pmic_reg_data[1].value = 0x60; +#endif /* AAVA_EV_0_5 */ + } else { +#if DBG_PRINTS + printk("backlight_state: OFF\n"); +#endif /* DBG_PRINTS */ + tmp_reg.pmic_reg_data[0].value = 0x00; + tmp_reg.pmic_reg_data[1].value = 0x00; + } + + if (ipc_pmic_register_write(&tmp_reg, TRUE)) { + printk("backlight_state: failed to write pmic regs 0x2a and 0x28!\n"); + } +} + +#ifdef AAVA_BACKLIGHT_HACK +static void bl_work_handler(struct work_struct *work) +{ + backlight_state(true); +} +#endif /* AAVA_BACKLIGHT_HACK */ + + +/** + * Sets the power state for the panel. + */ +static void mrst_dsi_set_power(struct drm_device *dev, + struct psb_intel_output *output, bool on) +{ + DRM_DRIVER_PRIVATE_T *dev_priv = dev->dev_private; + u32 pp_status; + +#if DBG_PRINTS + printk("mrst_dsi_set_power\n"); +#endif /* DBG_PRINTS */ + + /* + * The DIS device must be ready before we can change power state. + */ + if (!dev_priv->dsi_device_ready) + { +#if DBG_PRINTS + printk("mrst_dsi_set_power: !dev_priv->dsi_device_ready!\n"); +#endif /* DBG_PRINTS */ + return; + } + + /* + * We don't support dual DSI yet. May be in POR in the future. + */ + if (dev_priv->dual_display) + { +#if DBG_PRINTS + printk("mrst_dsi_set_power: dev_priv->dual_display!\n"); +#endif /* DBG_PRINTS */ + return; + } + + if (!ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, + OSPM_UHB_FORCE_POWER_ON)) + return; + + if (on) { +#if DBG_PRINTS + printk("mrst_dsi_set_power: on\n"); +#endif /* DBG_PRINTS */ + if (dev_priv->dpi && !dev_priv->dpi_panel_on) { +#if DBG_PRINTS + printk("mrst_dsi_set_power: dpi\n"); +#endif /* DBG_PRINTS */ + REG_WRITE(DPI_CONTROL_REG, DPI_TURN_ON); + REG_WRITE(PP_CONTROL, + (REG_READ(PP_CONTROL) | POWER_TARGET_ON)); + do { + pp_status = REG_READ(PP_STATUS); + } while ((pp_status & (PP_ON | PP_READY)) == PP_READY); + + /* Run TPO display specific initialisations */ +// MiKo TBD, this delay may need to be tuned + msleep(50); + panel_init(dev); + + /* Set backlights on */ + backlight_state( true ); + dev_priv->dpi_panel_on = true; + } + } else { +#if DBG_PRINTS + printk("mrst_dsi_set_power: off\n"); +#endif /* DBG_PRINTS */ + if (dev_priv->dpi && dev_priv->dpi_panel_on) { +#if DBG_PRINTS + printk("mrst_dsi_set_power: dpi\n"); +#endif /* DBG_PRINTS */ + /* Set backlights off */ + backlight_state( false ); + +// MiKo TBD, something clever could be done here to save power, for example: +// -Set display to sleep mode, or +// -Set display to HW reset, or +// -Shutdown the voltages to display + + REG_WRITE(PP_CONTROL, + (REG_READ(PP_CONTROL) & ~POWER_TARGET_ON)); + do { + pp_status = REG_READ(PP_STATUS); + } while (pp_status & PP_ON); + + REG_WRITE(DPI_CONTROL_REG, DPI_SHUT_DOWN); + + dev_priv->dpi_panel_on = false; + } + } + + ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); +} + + +static void mrst_dsi_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + +#if DBG_PRINTS + printk("mrst_dsi_dpms\n"); +#endif /* DBG_PRINTS */ + + if (mode == DRM_MODE_DPMS_ON) + mrst_dsi_set_power(dev, output, true); + else + mrst_dsi_set_power(dev, output, false); +} + + +static void mrst_dsi_save(struct drm_connector *connector) +{ +#if DBG_PRINTS + printk("mrst_dsi_save\n"); +#endif /* DBG_PRINTS */ + // MiKo TBD +} + + +static void mrst_dsi_restore(struct drm_connector *connector) +{ +#if DBG_PRINTS + printk("mrst_dsi_restore\n"); +#endif /* DBG_PRINTS */ + // MiKo TBD +} + + +static void mrst_dsi_prepare(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + +#if DBG_PRINTS + printk("mrst_dsi_prepare\n"); +#endif /* DBG_PRINTS */ + + if (!ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, + OSPM_UHB_FORCE_POWER_ON)) + return; + + mrst_dsi_set_power(dev, output, false); + + ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); +} + + +static void mrst_dsi_commit(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + +#if DBG_PRINTS + printk("mrst_dsi_commit\n"); +#endif /* DBG_PRINTS */ + + mrst_dsi_set_power(dev, output, true); +} + + +static void mrst_dsi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + DRM_DRIVER_PRIVATE_T *dev_priv = dev->dev_private; + u32 SupportedFormat = 0; + u32 resolution = 0; + uint64_t curValue = DRM_MODE_SCALE_FULLSCREEN; + +#if DBG_PRINTS + printk("mrst_dsi_mode_set\n"); +#endif /* DBG_PRINTS */ + + if (!ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, + OSPM_UHB_FORCE_POWER_ON)) + return; + + /* Sleep to ensure that the graphics engine is ready + * since its mode_set is called before ours + */ + msleep(100); + + switch (dev_priv->bpp) + { + case 24: + SupportedFormat = RGB_888_FMT; + break; + default: + printk("mrst_dsi_mode_set, invalid bpp!\n"); + break; + } + + if (dev_priv->dpi) { + drm_connector_property_get_value( + &enc_to_psb_intel_output(encoder)->base, + dev->mode_config.scaling_mode_property, + &curValue); + if (curValue == DRM_MODE_SCALE_CENTER) { + REG_WRITE(PFIT_CONTROL, 0); + } else if (curValue == DRM_MODE_SCALE_FULLSCREEN) { + REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); + } else { + printk("mrst_dsi_mode_set, scaling not supported!\n"); + REG_WRITE(PFIT_CONTROL, 0); + } + + + /* MIPI clock ratio 1:1 */ + //REG_WRITE(MIPI_CONTROL_REG, 0x00000018); + //REG_WRITE(0xb080, 0x0b061a02); + + /* MIPI clock ratio 2:1 */ + //REG_WRITE(MIPI_CONTROL_REG, 0x00000019); + //REG_WRITE(0xb080, 0x3f1f1c04); + + /* MIPI clock ratio 3:1 */ + //REG_WRITE(MIPI_CONTROL_REG, 0x0000001a); + //REG_WRITE(0xb080, 0x091f7f08); + + /* MIPI clock ratio 4:1 */ + REG_WRITE(MIPI_CONTROL_REG, (0x00000018 | mipi_clock)); + REG_WRITE(0xb080, dphy_reg); + + /* Enable all interrupts */ + REG_WRITE(INTR_EN_REG, 0xffffffff); + + REG_WRITE(TURN_AROUND_TIMEOUT_REG, 0x0000000A); + REG_WRITE(DEVICE_RESET_REG, 0x000000ff); + REG_WRITE(INIT_COUNT_REG, 0x00000fff); + REG_WRITE(HS_TX_TIMEOUT_REG, 0x90000); + REG_WRITE(LP_RX_TIMEOUT_REG, 0xffff); + REG_WRITE(HIGH_LOW_SWITCH_COUNT_REG, 0x46); + REG_WRITE(EOT_DISABLE_REG, 0x00000000); + REG_WRITE(LP_BYTECLK_REG, 0x00000004); + + REG_WRITE(VIDEO_FMT_REG, dev_priv->videoModeFormat); + + SupportedFormat <<= FMT_DPI_POS; + REG_WRITE(DSI_FUNC_PRG_REG, + (dev_priv->laneCount | SupportedFormat)); + + resolution = dev_priv->HactiveArea | + (dev_priv->VactiveArea << RES_V_POS); + REG_WRITE(DPI_RESOLUTION_REG, resolution); + + REG_WRITE(VERT_SYNC_PAD_COUNT_REG, dev_priv->VsyncWidth); + REG_WRITE(VERT_BACK_PORCH_COUNT_REG, dev_priv->VbackPorch); + REG_WRITE(VERT_FRONT_PORCH_COUNT_REG, dev_priv->VfrontPorch); + + REG_WRITE(HORIZ_SYNC_PAD_COUNT_REG, dev_priv->HsyncWidth); + REG_WRITE(HORIZ_BACK_PORCH_COUNT_REG, dev_priv->HbackPorch); + REG_WRITE(HORIZ_FRONT_PORCH_COUNT_REG, dev_priv->HfrontPorch); + REG_WRITE(HORIZ_ACTIVE_AREA_COUNT_REG, MIPI_HACT); + } + + /* Enable MIPI Port */ + REG_WRITE(MIPI, MIPI_PORT_EN); + + REG_WRITE(DEVICE_READY_REG, 0x00000001); + REG_WRITE(DPI_CONTROL_REG, 0x00000002); /* Turn On */ + + dev_priv->dsi_device_ready = true; + + /* Enable pipe */ + REG_WRITE(PIPEACONF, dev_priv->pipeconf); + REG_READ(PIPEACONF); + + /* Wait for 20ms for the pipe enable to take effect. */ + udelay(20000); + + /* Enable plane */ + REG_WRITE(DSPACNTR, dev_priv->dspcntr); + + /* Wait for 20ms for the plane enable to take effect. */ + udelay(20000); + + ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); +} + + +/** + * Detect the MIPI connection. + * + * This always returns CONNECTOR_STATUS_CONNECTED. + * This connector should only have + * been set up if the MIPI was actually connected anyway. + */ +static enum drm_connector_status mrst_dsi_detect(struct drm_connector + *connector) +{ +#if DBG_PRINTS + printk("mrst_dsi_detect\n"); +#endif /* DBG_PRINTS */ + return connector_status_connected; +} + + +/** + * Return the list of MIPI DDB modes if available. + */ +static int mrst_dsi_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct psb_intel_output *psb_intel_output = to_psb_intel_output(connector); + struct psb_intel_mode_device *mode_dev = psb_intel_output->mode_dev; + + /* Didn't get an DDB, so + * Set wide sync ranges so we get all modes + * handed to valid_mode for checking + */ + connector->display_info.min_vfreq = 0; + connector->display_info.max_vfreq = 200; + connector->display_info.min_hfreq = 0; + connector->display_info.max_hfreq = 200; + + if (mode_dev->panel_fixed_mode != NULL) { + struct drm_display_mode *mode = + drm_mode_duplicate(dev, mode_dev->panel_fixed_mode); + drm_mode_probed_add(connector, mode); + return 1; + } + return 0; +} + + +static const struct drm_encoder_helper_funcs mrst_dsi_helper_funcs = { + .dpms = mrst_dsi_dpms, + .mode_fixup = psb_intel_lvds_mode_fixup, + .prepare = mrst_dsi_prepare, + .mode_set = mrst_dsi_mode_set, + .commit = mrst_dsi_commit, +}; + + +static const struct drm_connector_helper_funcs + mrst_dsi_connector_helper_funcs = { + .get_modes = mrst_dsi_get_modes, + .mode_valid = psb_intel_lvds_mode_valid, + .best_encoder = psb_intel_best_encoder, +}; + + +static const struct drm_connector_funcs mrst_dsi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .save = mrst_dsi_save, + .restore = mrst_dsi_restore, + .detect = mrst_dsi_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = psb_intel_lvds_set_property, + .destroy = psb_intel_lvds_destroy, +}; + + +/** Returns the panel fixed mode from configuration. */ +struct drm_display_mode *mrst_dsi_get_configuration_mode(struct drm_device *dev) +{ + struct drm_display_mode *mode; + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) + return NULL; + + /* MiKo, fixed mode for TPO display + Note: Using defined values for easier match with ITP scripts + and adding 1 since psb_intel_display.c decreases by 1 + */ + mode->hdisplay = (DISP_HPIX + 1); + mode->vdisplay = (DISP_VPIX + 1); + mode->hsync_start = (DISP_HSYNC_START + 1); + mode->hsync_end = (DISP_HSYNC_END + 1); + mode->htotal = (DISP_HBLANK_END + 1); + mode->vsync_start = (DISP_VSYNC_START + 1); + mode->vsync_end = (DISP_VSYNC_END + 1); + mode->vtotal = (DISP_VBLANK_END + 1); + mode->clock = 33264; + + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + return mode; +} + + +/* ************************************************************************* *\ +FUNCTION: mrst_mipi_settings_init + ` +DESCRIPTION: + +\* ************************************************************************* */ +static bool mrst_mipi_settings_init(DRM_DRIVER_PRIVATE_T *dev_priv) +{ + /* MiKo, fixed values for TPO display */ + dev_priv->pixelClock = 33264; + dev_priv->HsyncWidth = MIPI_HSPAD; + dev_priv->HbackPorch = MIPI_HBP; + dev_priv->HfrontPorch = MIPI_HFP; + dev_priv->HactiveArea = HSIZE; + dev_priv->VsyncWidth = MIPI_VSPAD; + dev_priv->VbackPorch = MIPI_VBP; + dev_priv->VfrontPorch = MIPI_VFP; + dev_priv->VactiveArea = VSIZE; + dev_priv->bpp = 24; + + /* video mode */ + dev_priv->dpi = true; + + /* MiKo, set these true by default to ensure that first mode set is done + cleanly + */ + dev_priv->dpi_panel_on = true; + dev_priv->dsi_device_ready = true; + + /* 2 lanes */ + dev_priv->laneCount = MIPI_LANES; + + /* Burst mode */ + dev_priv->videoModeFormat = BURST_MODE; + + return true; +} + + +/** + * mrst_dsi_init - setup MIPI connectors on this device + * @dev: drm device + * + * Create the connector, try to figure out what + * modes we can display on the MIPI panel (if present). + */ +void mrst_dsi_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev) +{ + DRM_DRIVER_PRIVATE_T *dev_priv = dev->dev_private; + struct psb_intel_output *psb_intel_output; + struct drm_connector *connector; + struct drm_encoder *encoder; + +#if DBG_PRINTS + printk("mrst_dsi_init\n"); +#endif /* DBG_PRINTS */ + + psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); + if (!psb_intel_output) + return; + + panel_reset(); + +#ifdef AAVA_BACKLIGHT_HACK + schedule_delayed_work(&bl_work, 2*HZ); +#endif /* AAVA_BACKLIGHT_HACK */ + + psb_intel_output->mode_dev = mode_dev; + connector = &psb_intel_output->base; + encoder = &psb_intel_output->enc; + drm_connector_init(dev, + &psb_intel_output->base, + &mrst_dsi_connector_funcs, + DRM_MODE_CONNECTOR_MIPI); + + drm_encoder_init(dev, + &psb_intel_output->enc, + &psb_intel_lvds_enc_funcs, + DRM_MODE_ENCODER_MIPI); + + drm_mode_connector_attach_encoder(&psb_intel_output->base, + &psb_intel_output->enc); + psb_intel_output->type = INTEL_OUTPUT_MIPI; + + drm_encoder_helper_add(encoder, &mrst_dsi_helper_funcs); + drm_connector_helper_add(connector, &mrst_dsi_connector_helper_funcs); + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + drm_connector_attach_property(connector, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_FULLSCREEN); + drm_connector_attach_property(connector, + dev_priv->backlight_property, + BRIGHTNESS_MAX_LEVEL); + + if (!mrst_mipi_settings_init(dev_priv)) + printk("Can't initialize MIPI settings\n"); + + /* No config phase */ + dev_priv->config_phase = false; + + /* Get the fixed mode */ + mode_dev->panel_fixed_mode = mrst_dsi_get_configuration_mode(dev); + if (mode_dev->panel_fixed_mode) { + mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; + } else { + printk("Found no modes for MIPI!\n"); + goto failed_find; + } +// Temporary access from sysfs begin + orig_encoder = encoder; +// Temporary access from sysfs end + drm_sysfs_connector_add(connector); + return; + +failed_find: + drm_encoder_cleanup(encoder); + drm_connector_cleanup(connector); + kfree(connector); +} + +// Temporary access from sysfs begin +static struct class_attribute miko_class_attrs[] = { + __ATTR(dphy, 0644, NULL, dphy_store), + __ATTR(clock, 0644, NULL, clock_store), + __ATTR(apply, 0200, NULL, apply_settings), + __ATTR_NULL, +}; + +static struct class miko_class = { + .name = "miko", + .owner = THIS_MODULE, + + .class_attrs = miko_class_attrs, +}; + +static int __init miko_sysfs_init(void) +{ + int status; + + status = class_register(&miko_class); + if (status < 0) + return status; + + return status; +} +postcore_initcall(miko_sysfs_init); +// Temporary access from sysfs end + Index: linux-2.6.33/drivers/misc/Makefile =================================================================== --- linux-2.6.33.orig/drivers/misc/Makefile +++ linux-2.6.33/drivers/misc/Makefile @@ -21,7 +21,7 @@ obj-$(CONFIG_SGI_XP) += sgi-xp/ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o obj-$(CONFIG_HP_ILO) += hpilo.o -obj-$(CONFIG_MRST) += intel_mrst.o +obj-$(CONFIG_X86_MRST) += intel_mrst.o obj-$(CONFIG_ISL29003) += isl29003.o obj-$(CONFIG_MRST_RAR_HANDLER) += memrar.o memrar-y := memrar_allocator.o memrar_handler.o Index: linux-2.6.33/drivers/misc/intel_mrst.c =================================================================== --- linux-2.6.33.orig/drivers/misc/intel_mrst.c +++ linux-2.6.33/drivers/misc/intel_mrst.c @@ -112,6 +112,48 @@ static int intel_mrst_sdio_EVP_power_dow static int intel_mrst_sdio_8688_power_up(void) { +/*ouljhuot start*/ +/*WLAN / BT power-up sequence:*/ +/*1. power (GPO4) & reset (GPO3) low*/ +/*2. power (GPO4) high*/ +/*3. reset (GPO3) high*/ + +/*GPS power-up sequence:*/ +/*1. power (GPO1) & reset (GPO2) low*/ +/*2. VDD_IO and VDD_LP_PLLREG_IN high*/ +/*VDD_IO & VDD_LP_PLLREG_IN == VPMIC_1V8*/ +/*3. usleep(1) (tvddio_nreset min. 500ns)*/ +/*4. reset (GPO2) high*/ +/*5. VDD_COREREG_IN and VDD_RFREG_IN high*/ + /*VDD_COREREG_IN == VWLAN_GPS_1V8 (GYMXIO)*/ + /*VDD_RFREG_IN == VGPS_ANA_3V3 (GYMX33)*/ +/*6. power (GPO1) high*/ +/*7. msleep(1);*/ + unsigned int temp = 0; + + /* Register 0xf4 has 4 GPIO lines connected to the MRVL 8688 * IFX GPS: + * bit 4: WiFi PDn + * bit 3: WiFi RESETn + * bit 2: GPS RESET_N + * bit 1: GPS PD_N*/ + + /*WLAN POWER and RESET low*/ + intel_mrst_pmic_read(0xf4, &temp); + temp &= ~0x18; + intel_mrst_pmic_write(0xf4, temp); +/* msleep(1);*/ + + /*GPS RESET low & POWER low*/ + intel_mrst_pmic_read(0xf4, &temp); + temp &= ~0x6; + intel_mrst_pmic_write(0xf4, temp); +/* usleep(1);*/ + + msleep(1); + /*GPS RESET high*/ + temp |= 0x4; + intel_mrst_pmic_write(0xf4, temp); +/*ouljhuot end*/ intel_mrst_pmic_write(0x37, 0x3f); /* Set VDDQ for Marvell 8688 */ intel_mrst_pmic_write(0x4a, 0x3f); /* Set GYMXIOCNT for Marvell 8688 */ intel_mrst_pmic_write(0x4e, 0x3f); /* Set GYMX33CNT for Marvell 8688 */ @@ -124,6 +166,22 @@ static int intel_mrst_sdio_8688_power_up intel_mrst_pmic_write(0x4c, 0x27); /* Enable V1p8_VWYMXARF for MRVL8688 */ + +/*ouljhuot start*/ + /*WLAN POWER high*/ + temp |= 0x10; + intel_mrst_pmic_write(0xf4, temp); + + /*WLAN RESET high*/ + temp |= 0x8; + intel_mrst_pmic_write(0xf4, temp); + + /*GPS POWER high*/ + temp |= 0x2; + intel_mrst_pmic_write(0xf4, temp); +/* msleep(16);*/ +/*ouljhuot end*/ + return 0; } @@ -153,6 +211,35 @@ static int intel_mrst_bringup_8688_sdio2 return 0; } + + + + /*ouljhuot start*/ +static int intel_mrst_sdio_gps_power_up(void) + { + unsigned int temp = 0; + + /*GPS RESET low & POWER low*/ + intel_mrst_pmic_read(0xf4, &temp); + temp &= ~0x6; + intel_mrst_pmic_write(0xf4, temp); + msleep(1); + /*GPS RESET high*/ + temp |= 0x4; + intel_mrst_pmic_write(0xf4, temp); + + intel_mrst_pmic_write(0x4a, 0x3f); /* Ensure GYMXIOCNT */ + intel_mrst_pmic_write(0x4e, 0x3f); /* Ensure GYMX33CNT */ + + /*GPS POWER high*/ + temp |= 0x2; + intel_mrst_pmic_write(0xf4, temp); + /* Wait to settle */ + msleep(16); + + return 0; + } + static int intel_mrst_bringup_EVP_sdio2_Option_spi(void) { unsigned int temp = 0; @@ -199,7 +286,10 @@ static int __init intel_mrst_module_init printk(KERN_INFO "intel_mrst_module_init: bringing up power for " "8688 WLAN on SDIO2 & IFX GPS over SPI...\n"); - ret = intel_mrst_bringup_8688_sdio2(); +/*ouljhuot start*/ + ret = intel_mrst_sdio_8688_power_up(); +/* ret = intel_mrst_sdio_gps_power_up();*/ +/*ouljhuot end*/ #endif /* CONFIG_8688_RC */ Index: linux-2.6.33/drivers/hwmon/lis331dl.c =================================================================== --- linux-2.6.33.orig/drivers/hwmon/lis331dl.c +++ linux-2.6.33/drivers/hwmon/lis331dl.c @@ -45,6 +45,8 @@ MODULE_LICENSE("GPL v2"); #define ACCEL_NORMAL_MODE 0 #define ACCEL_MEMORY_REBOOT 1 +#define POS_READ_MAX_RETRY (5) + /* internal return values */ struct acclero_data { @@ -93,9 +95,24 @@ static ssize_t x_pos_show(struct device { struct i2c_client *client = to_i2c_client(dev); int ret_val; + int retry = 0; +x_retry: ret_val = i2c_smbus_read_byte_data(client, 0x29); - return sprintf(buf, "%d\n", ret_val); + if (ret_val == -ETIMEDOUT) { + dev_dbg(dev, "x pos read timed out, retry %d\n", retry); + retry++; + if (retry <= POS_READ_MAX_RETRY) { + msleep(10); + goto x_retry; + } else { + ret_val = 0; + dev_err(dev, "x pos read failed %d retries\n", retry); + } + } + /* ouljkorh, 09.11.2009, change start */ + return sprintf(buf, "%d\n", (signed char)ret_val); + /* ouljkorh, 09.11.2009, change end */ } static ssize_t y_pos_show(struct device *dev, @@ -103,9 +120,24 @@ static ssize_t y_pos_show(struct device { struct i2c_client *client = to_i2c_client(dev); int ret_val; + int retry = 0; +y_retry: ret_val = i2c_smbus_read_byte_data(client, 0x2B); - return sprintf(buf, "%d\n", ret_val); + if (ret_val == -ETIMEDOUT) { + dev_dbg(dev, "y pos read timed out, retry %d\n", retry); + retry++; + if (retry <= POS_READ_MAX_RETRY) { + msleep(10); + goto y_retry; + } else { + ret_val = 0; + dev_err(dev, "y pos read failed %d retries\n", retry); + } + } + /* ouljkorh, 09.11.2009, change start */ + return sprintf(buf, "%d\n", (signed char)ret_val); + /* ouljkorh, 09.11.2009, change end */ } static ssize_t z_pos_show(struct device *dev, @@ -113,9 +145,24 @@ static ssize_t z_pos_show(struct device { struct i2c_client *client = to_i2c_client(dev); int ret_val; + int retry = 0; +z_retry: ret_val = i2c_smbus_read_byte_data(client, 0x2D); - return sprintf(buf, "%d\n", ret_val); + if (ret_val == -ETIMEDOUT) { + dev_dbg(dev, "z pos read timed out, retry %d\n", retry); + retry++; + if (retry <= POS_READ_MAX_RETRY) { + msleep(10); + goto z_retry; + } else { + ret_val = 0; + dev_err(dev, "z pos read failed %d retries\n", retry); + } + } + /* ouljkorh, 09.11.2009, change start */ + return sprintf(buf, "%d\n", (signed char)ret_val); + /* ouljkorh, 09.11.2009, change end */ } static ssize_t xyz_pos_show(struct device *dev, @@ -123,11 +170,38 @@ static ssize_t xyz_pos_show(struct devic { int x, y, z; struct i2c_client *client = to_i2c_client(dev); + int retry = 0; +xyz_retry: + if (retry > POS_READ_MAX_RETRY) { + dev_err(dev, "xyz read retry failed\n"); + x = y = z = 0; + return sprintf(buf, "(%d,%d,%d)\n", (signed char)x, + (signed char)y, (signed char)z); + } + retry++; x = i2c_smbus_read_byte_data(client, 0x29); + if (x == -ETIMEDOUT) { + msleep(100); + goto xyz_retry; + } + msleep(100); y = i2c_smbus_read_byte_data(client, 0x2B); + if (y == -ETIMEDOUT) { + msleep(100); + goto xyz_retry; + } + msleep(100); z = i2c_smbus_read_byte_data(client, 0x2D); - return sprintf(buf, "(%d,%d,%d)\n", x, y, z); + if (z == -ETIMEDOUT) { + msleep(100); + goto xyz_retry; + } + + /* ouljkorh, 09.11.2009, change start */ + return sprintf(buf, "(%d,%d,%d)\n", (signed char)x, + (signed char)y, (signed char)z); + /* ouljkorh, 09.11.2009, change end */ } static ssize_t data_rate_store(struct device *dev, Index: linux-2.6.33/drivers/usb/gadget/u_serial.c =================================================================== --- linux-2.6.33.orig/drivers/usb/gadget/u_serial.c +++ linux-2.6.33/drivers/usb/gadget/u_serial.c @@ -783,11 +783,6 @@ static int gs_open(struct tty_struct *tt port->open_count = 1; port->openclose = false; - /* low_latency means ldiscs work in tasklet context, without - * needing a workqueue schedule ... easier to keep up. - */ - tty->low_latency = 1; - /* if connected, start the I/O stream */ if (port->port_usb) { struct gserial *gser = port->port_usb; Index: linux-2.6.33/drivers/i2c/busses/i2c-mrst.c =================================================================== --- linux-2.6.33.orig/drivers/i2c/busses/i2c-mrst.c +++ linux-2.6.33/drivers/i2c/busses/i2c-mrst.c @@ -37,7 +37,7 @@ #include "i2c-mrst.h" -#define MAX_T_POLL_COUNT 4000 /* FIXME */ +#define MAX_T_POLL_COUNT 8000 /* FIXME */ #define DEF_BAR 0 #define VERSION "Version 0.5" Index: linux-2.6.33/arch/x86/kernel/mrst.c =================================================================== --- linux-2.6.33.orig/arch/x86/kernel/mrst.c +++ linux-2.6.33/arch/x86/kernel/mrst.c @@ -23,6 +23,9 @@ #include <linux/input.h> #include <linux/platform_device.h> #include <linux/irq.h> +/*jhuot, added for MAX3107 data*/ +#include <linux/spi/mrst_spi.h> + #include <asm/string.h> #include <asm/setup.h> @@ -267,6 +270,27 @@ void __init x86_mrst_early_setup(void) #define MRST_SPI2_CS_START 4 static struct langwell_pmic_gpio_platform_data pmic_gpio_pdata; +#ifdef CONFIG_SERIAL_MAX3107 +static struct mrst_spi_chip spi_slave0 = { + .poll_mode = 1, + .enable_dma = 0, + .type = SPI_FRF_SPI, +}; + +static struct spi_board_info mrst_spi_board_info[] __initdata = { + { + /* the modalias must be the same as spi device driver name */ + .modalias = "max3107", /* spi_driver name driving device */ + .max_speed_hz = 3125000,/* default value */ + .bus_num = 0, /* SPI0 */ + .chip_select = 0, /* Framework chip select. */ + .platform_data = NULL, /* fill later */ + .controller_data = &spi_slave0, + .irq = 0x13d, + }, +}; +#endif + static int __init sfi_parse_spib(struct sfi_table_header *table) { struct sfi_table_simple *sb; @@ -290,31 +314,48 @@ static int __init sfi_parse_spib(struct pr_info("Moorestown SPI devices info:\n"); for (i = 0, j = 0; i < num; i++, pentry++) { - strncpy(info[j].modalias, pentry->name, 16); - info[j].irq = pentry->irq_info; - info[j].bus_num = pentry->host_num; - info[j].chip_select = pentry->cs; - info[j].max_speed_hz = 3125000; /* hard coded */ - if (info[i].chip_select >= MRST_SPI2_CS_START) { - /* these SPI2 devices are not exposed to system as PCI - * devices, but they have separate RTE entry in IOAPIC - * so we have to enable them one by one here - */ - ioapic = mp_find_ioapic(info[j].irq); - irq_attr.ioapic = ioapic; - irq_attr.ioapic_pin = info[j].irq; - irq_attr.trigger = 1; - irq_attr.polarity = 1; - io_apic_set_pci_routing(NULL, info[j].irq, +#ifdef CONFIG_SERIAL_MAX3107 + if (j != 1) { /*other devices info*/ +#endif + strncpy(info[j].modalias, pentry->name, 16); + info[j].irq = pentry->irq_info; + info[j].bus_num = pentry->host_num; + info[j].chip_select = pentry->cs; + info[j].max_speed_hz = 3125000; /* hard coded */ + if (info[i].chip_select >= MRST_SPI2_CS_START) { + /* these SPI2 devices are not exposed to system as PCI + * devices, but they have separate RTE entry in IOAPIC + * so we have to enable them one by one here + */ + ioapic = mp_find_ioapic(info[j].irq); + irq_attr.ioapic = ioapic; + irq_attr.ioapic_pin = info[j].irq; + irq_attr.trigger = 1; + irq_attr.polarity = 1; + io_apic_set_pci_routing(NULL, info[j].irq, &irq_attr); - } - info[j].platform_data = pentry->dev_info; + } - if (!strcmp(pentry->name, "pmic_gpio")) { - memcpy(&pmic_gpio_pdata, pentry->dev_info, 8); - pmic_gpio_pdata.gpiointr = 0xffffeff8; - info[j].platform_data = &pmic_gpio_pdata; + info[j].platform_data = pentry->dev_info; + + if (!strcmp(pentry->name, "pmic_gpio")) { + memcpy(&pmic_gpio_pdata, pentry->dev_info, 8); + pmic_gpio_pdata.gpiointr = 0xffffeff8; + info[j].platform_data = &pmic_gpio_pdata; + } +#ifdef CONFIG_SERIAL_MAX3107 + } else { /*MAX3107 info*/ + info[j] = mrst_spi_board_info[0]; + } + +#endif + /* jhuot edit start: change GPS chip select from 2 to 3 */ + if (info[j].bus_num == 0 && info[j].chip_select == 2) { + info[j].chip_select = 3; + } else if (info[j].bus_num == 0 && info[j].chip_select == 3) { + info[j].chip_select = 2; } + /* jhuot edit end */ pr_info("info[%d]: name = %16s, irq = 0x%04x, bus = %d, " "cs = %d\n", j, info[j].modalias, info[j].irq, info[j].bus_num, info[j].chip_select); Index: linux-2.6.33/drivers/serial/Kconfig =================================================================== --- linux-2.6.33.orig/drivers/serial/Kconfig +++ linux-2.6.33/drivers/serial/Kconfig @@ -540,6 +540,21 @@ config SERIAL_S5PC100 help Serial port support for the Samsung S5PC100 SoCs +config SERIAL_MAX3107 + tristate "MAX3107 support" + depends on SPI + select SERIAL_CORE + help + MAX3107 chip support + +config MAX3107_LOW_POWER + boolean "Enable very low power consumption scheme for Max3107" + default n + depends on SERIAL_MAX3107 + help + Adds hardware suspend for MAX3107 instead of sleep/auto-sleep, + but causes longer latency in wake-up (re-initialization of the chip). + config SERIAL_MAX3100 tristate "MAX3100 support" depends on SPI Index: linux-2.6.33/drivers/serial/Makefile =================================================================== --- linux-2.6.33.orig/drivers/serial/Makefile +++ linux-2.6.33/drivers/serial/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0. obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o obj-$(CONFIG_SERIAL_S5PC100) += s3c6400.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o +obj-$(CONFIG_SERIAL_MAX3107) += max3107.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o obj-$(CONFIG_SERIAL_MUX) += mux.o obj-$(CONFIG_SERIAL_68328) += 68328serial.o Index: linux-2.6.33/drivers/serial/max3107.c =================================================================== --- /dev/null +++ linux-2.6.33/drivers/serial/max3107.c @@ -0,0 +1,1484 @@ +/* + * max3107.c - spi uart protocol driver for Maxim 3107 + * Based on max3100.c + * by Christian Pellegrin <chripell@evolware.org> + * and max3110.c + * by Feng Tang <feng.tang@intel.com> + * + * Copyright (C) Aavamobile 2009 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/spi/spi.h> +#include <linux/spi/mrst_spi.h> +#include <linux/freezer.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/serial_max3107.h> + +/* Debug trace definitions */ +#define DBG_LEVEL 0 + +#if (DBG_LEVEL > 0) +#define DBG_TRACE(format,args...) printk(KERN_ERR "%s: " format, \ + __FUNCTION__ , ## args) +#else +#define DBG_TRACE(format,args...) +#endif + +#if (DBG_LEVEL > 1) +#define DBG_TRACE_SPI_DATA +#endif + +struct max3107_port { + /* UART port structure */ + struct uart_port port; + + /* SPI device structure */ + struct spi_device *spi; + + /* GPIO chip stucture */ + struct gpio_chip chip; + + /* Workqueue that does all the magic */ + struct workqueue_struct *workqueue; + struct work_struct work; + + /* Lock for shared data */ + spinlock_t data_lock; + + /* Device configuration */ + int ext_clk; /* 1 if external clock used */ + int loopback; /* Current loopback mode state */ + int baud; /* Current baud rate */ + + /* State flags */ + int suspended; /* Indicates suspend mode */ + int tx_fifo_empty; /* Flag for TX FIFO state */ + int rx_enabled; /* Flag for receiver state */ + int tx_enabled; /* Flag for transmitter state */ + + /* Shared data */ + u16 irqen_reg; /* Current IRQ enable register value */ + u16 mode1_reg; /* Current mode1 register value*/ + int mode1_commit; /* Flag for setting new mode1 register value */ + u16 lcr_reg; /* Current LCR register value */ + int lcr_commit; /* Flag for setting new LCR register value */ + u32 brg_cfg; /* Current Baud rate generator config */ + int brg_commit; /* Flag for setting new baud rate generator + * config + */ + + int handle_irq; /* Indicates that IRQ should be handled */ +}; + +/* Platform data structure */ +struct max3107_plat { + /* Loopback mode enable */ + int loopback; + /* External clock enable */ + int ext_clk; + /* HW suspend function */ + void (*max3107_hw_suspend) (struct max3107_port *s, int suspend); + /* Polling mode enable */ + int polled_mode; + /* Polling period if polling mode enabled */ + int poll_time; +}; + + +/* Perform SPI transfer for write/read of device register(s) */ +static int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len) +{ + struct spi_message spi_msg; + struct spi_transfer spi_xfer; + + DBG_TRACE("enter\n"); + + /* Initialize SPI ,message */ + spi_message_init(&spi_msg); + + /* Initialize SPI transfer */ + memset(&spi_xfer, 0, sizeof spi_xfer); + spi_xfer.len = len; + spi_xfer.tx_buf = tx; + spi_xfer.rx_buf = rx; + spi_xfer.speed_hz = MAX3107_SPI_SPEED; + + /* Add SPI transfer to SPI message */ + spi_message_add_tail(&spi_xfer, &spi_msg); + +#ifdef DBG_TRACE_SPI_DATA + { + int i; + printk("tx len %d:\n", spi_xfer.len); + for (i = 0 ; i < spi_xfer.len && i < 32 ; i++) { + printk(" %x", ((u8*)spi_xfer.tx_buf)[i]); + } + printk("\n"); + } +#endif + + /* Perform synchronous SPI transfer */ + if (spi_sync(s->spi, &spi_msg)) { + dev_err(&s->spi->dev, "spi_sync failure\n"); + return -EIO; + } + +#ifdef DBG_TRACE_SPI_DATA + if (spi_xfer.rx_buf) { + int i; + printk("rx len %d:\n", spi_xfer.len); + for (i = 0 ; i < spi_xfer.len && i < 32 ; i++) { + printk(" %x", ((u8*)spi_xfer.rx_buf)[i]); + } + printk("\n"); + } +#endif + return 0; +} + +/* Puts received data to circular buffer */ +static void put_data_to_circ_buf(struct max3107_port *s, unsigned char *data, + int len) +{ + struct uart_port *port = &s->port; + struct tty_struct *tty; + + DBG_TRACE("enter\n"); + + if (!port->state) { + /* UART is not open */ + dev_warn(&s->spi->dev, "UART is closed\n"); + return; + } + + tty = port->state->port.tty; + if (!tty) { + /* TTY is not open */ + dev_warn(&s->spi->dev, "TTY is closed\n"); + return; + } + + /* Insert received data */ + tty_insert_flip_string(tty, data, len); + /* Update RX counter */ + port->icount.rx += len; +} + +/* Handle data receiving */ +static void max3107_handlerx(struct max3107_port *s, u16 rxlvl) +{ + int i; + int j; + int len; /* SPI transfer buffer length */ + u16 buf[MAX3107_RX_FIFO_SIZE+2]; /* SPI transfer buffer + * +2 for RX FIFO interrupt + * disabling and RX level query + */ + u8 valid_str[MAX3107_RX_FIFO_SIZE]; + + DBG_TRACE("enter\n"); + + if (!s->rx_enabled) { + /* RX is disabled */ + return; + } + + if (rxlvl == 0) { + /* RX fifo is empty */ + return; + } else if (rxlvl >= MAX3107_RX_FIFO_SIZE) { + dev_warn(&s->spi->dev, "Possible RX FIFO overrun %d\n", rxlvl); + /* Ensure sanity of RX level */ + rxlvl = MAX3107_RX_FIFO_SIZE; + } + + while (rxlvl) { + DBG_TRACE("rxlvl %d\n", rxlvl); + /* Clear buffer */ + memset(buf, 0, sizeof(buf)); + len = 0; + if (s->irqen_reg & MAX3107_IRQ_RXFIFO_BIT) { + /* First disable RX FIFO interrupt */ + DBG_TRACE("Disabling RX INT\n"); + buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + spin_lock(&s->data_lock); + s->irqen_reg &= ~MAX3107_IRQ_RXFIFO_BIT; + buf[0] |= s->irqen_reg; + spin_unlock(&s->data_lock); + len++; + } + /* Just increase the length by amount of words in FIFO since + * buffer was zeroed and SPI transfer of 0x0000 means reading + * from RX FIFO + */ + len += rxlvl; + /* Append RX level query */ + buf[len] = MAX3107_RXFIFOLVL_REG; + len++; + + /* Perform the SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, len*2)) { + dev_err(&s->spi->dev, + "SPI transfer for RX handling failed\n"); + return; + } + + /* Skip RX FIFO interrupt disabling word if it was added */ + j = ((len-1)-rxlvl); + /* Read received words */ + for (i = 0; i < rxlvl; i++, j++) { + valid_str[i] = (u8)buf[j]; + } + put_data_to_circ_buf(s, valid_str, rxlvl); + /* Get new RX level */ + rxlvl = (buf[len-1] & MAX3107_SPI_RX_DATA_MASK); + } + + if (s->rx_enabled) { + /* RX still enabled, re-enable RX FIFO interrupt */ + DBG_TRACE("Enabling RX INT\n"); + buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + spin_lock(&s->data_lock); + s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT; + buf[0] |= s->irqen_reg; + spin_unlock(&s->data_lock); + if (max3107_rw(s, (u8 *)buf, NULL, 2)) { + dev_err(&s->spi->dev, + "RX FIFO interrupt enabling failed\n"); + } + } + + /* Push the received data to receivers */ + tty_flip_buffer_push(s->port.state->port.tty); +} + + +/* Handle data sending */ +static void max3107_handletx(struct max3107_port *s) +{ + struct circ_buf *xmit = &s->port.state->xmit; + int i; + int len; /* SPI transfer buffer length */ + u16 buf[MAX3107_TX_FIFO_SIZE+3]; /* SPI transfer buffer + * +3 for TX FIFO empty + * interrupt disabling and + * enabling and TX enabling + */ + + DBG_TRACE("enter\n"); + + if (!s->tx_fifo_empty) { + /* Don't send more data before previous data is sent */ + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port)) { + /* No data to send or TX is stopped */ + return; + } + + /* Get length of data pending in circular buffer */ + len = uart_circ_chars_pending(xmit); + if (len) { + /* Limit to size of TX FIFO */ + if (len > MAX3107_TX_FIFO_SIZE) + len = MAX3107_TX_FIFO_SIZE; + + DBG_TRACE("txlen %d\n", len); + + /* Update TX counter */ + s->port.icount.tx += len; + + /* TX FIFO will no longer be empty */ + s->tx_fifo_empty = 0; + + i = 0; + if (s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT) { + /* First disable TX empty interrupt */ + DBG_TRACE("Disabling TE INT\n"); + buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + spin_lock(&s->data_lock); + s->irqen_reg &= ~MAX3107_IRQ_TXEMPTY_BIT; + buf[i] |= s->irqen_reg; + spin_unlock(&s->data_lock); + i++; + len++; + } + + /* Add data to send */ + for ( ; i < len ; i++ ) { + buf[i] = (MAX3107_WRITE_BIT | MAX3107_THR_REG); + buf[i] |= ((u16)xmit->buf[xmit->tail] & + MAX3107_SPI_TX_DATA_MASK); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } + + if (!(s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT)) { + /* Enable TX empty interrupt */ + DBG_TRACE("Enabling TE INT\n"); + buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + spin_lock(&s->data_lock); + s->irqen_reg |= MAX3107_IRQ_TXEMPTY_BIT; + buf[i] |= s->irqen_reg; + spin_unlock(&s->data_lock); + i++; + len++; + } + if (!s->tx_enabled) { + /* Enable TX */ + DBG_TRACE("Enable TX\n"); + buf[i] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + spin_lock(&s->data_lock); + s->mode1_reg &= ~MAX3107_MODE1_TXDIS_BIT; + buf[i] |= s->mode1_reg; + spin_unlock(&s->data_lock); + s->tx_enabled = 1; + i++; + len++; + } + + /* Perform the SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, len*2)) { + dev_err(&s->spi->dev, + "SPI transfer for TX handling failed\n"); + return; + } + } + + /* Indicate wake up if circular buffer is getting low on data */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + +} + +/* Handle interrupts + * Also reads and returns current RX FIFO level + */ +static u16 handle_interrupt(struct max3107_port *s) +{ + u16 buf[4]; /* Buffer for SPI transfers */ + u8 irq_status; + u16 rx_level; + + DBG_TRACE("enter\n"); + + /* Read IRQ status register */ + buf[0] = MAX3107_IRQSTS_REG; + /* Read status IRQ status register */ + buf[1] = MAX3107_STS_IRQSTS_REG; + /* Read LSR IRQ status register */ + buf[2] = MAX3107_LSR_IRQSTS_REG; + /* Query RX level */ + buf[3] = MAX3107_RXFIFOLVL_REG; + + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 8)) { + dev_err(&s->spi->dev, + "SPI transfer for interrupt handling failed\n"); + return 0; + } + + irq_status = (u8)buf[0]; + DBG_TRACE("IRQSTS %x\n", irq_status); + rx_level = (buf[3] & MAX3107_SPI_RX_DATA_MASK); + + if (irq_status & MAX3107_IRQ_LSR_BIT) { + /* LSR interrupt */ + if ( buf[2] & MAX3107_LSR_RXTO_BIT ) { + /* RX timeout interrupt, + * handled by normal RX handling + */ + DBG_TRACE("RX TO INT\n"); + } + } + + if (irq_status & MAX3107_IRQ_TXEMPTY_BIT) { + /* Tx empty interrupt, + * disable TX and set tx_fifo_empty flag + */ + DBG_TRACE("TE INT, disabling TX\n"); + buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + spin_lock(&s->data_lock); + s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT; + buf[0] |= s->mode1_reg; + spin_unlock(&s->data_lock); + if (max3107_rw(s, (u8 *)buf, NULL, 2)) + dev_err(&s->spi->dev, + "SPI transfer for TX disabling failed\n"); + s->tx_enabled = 0; + s->tx_fifo_empty = 1; + } + + if (irq_status & MAX3107_IRQ_RXFIFO_BIT) { + /* RX FIFO interrupt, + * handled by normal RX handling + */ + DBG_TRACE("RFIFO INT\n"); + } + + /* Return RX level */ + return rx_level; +} + +/* Trigger work thread*/ +static void max3107_dowork(struct max3107_port *s) +{ + if (!work_pending(&s->work) && !freezing(current) && !s->suspended) + queue_work(s->workqueue, &s->work); +} + +/* Work thread */ +static void max3107_work(struct work_struct *w) +{ + struct max3107_port *s = container_of(w, struct max3107_port, work); + u16 rxlvl = 0; + int len; /* SPI transfer buffer length */ + u16 buf[5]; /* Buffer for SPI transfers */ + + DBG_TRACE("enter\n"); + + /* Start by reading current RX FIFO level */ + buf[0] = MAX3107_RXFIFOLVL_REG; + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, + "SPI transfer for RX level query failed\n"); + rxlvl = 0; + } else { + rxlvl = (buf[0] & MAX3107_SPI_RX_DATA_MASK); + } + + do { + DBG_TRACE("rxlvl %d\n", rxlvl); + + /* Handle RX */ + max3107_handlerx(s, rxlvl); + rxlvl = 0; + + if (s->handle_irq) { + /* Handle pending interrupts + * We also get new RX FIFO level since new data may + * have been received while pushing received data to + * receivers + */ + s->handle_irq = 0; + rxlvl = handle_interrupt(s); + } + + /* Handle TX */ + max3107_handletx(s); + + /* Handle configuration changes */ + len = 0; + spin_lock(&s->data_lock); + if (s->mode1_commit) { + DBG_TRACE("mode1_commit\n"); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + buf[len++] |= s->mode1_reg; + s->mode1_commit = 0; + } + if (s->lcr_commit) { + DBG_TRACE("lcr_commit\n"); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG); + buf[len++] |= s->lcr_reg; + s->lcr_commit = 0; + } + if (s->brg_commit) { + DBG_TRACE("brg_commit\n"); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG); + buf[len++] |= ((s->brg_cfg >> 16) & + MAX3107_SPI_TX_DATA_MASK); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG); + buf[len++] |= ((s->brg_cfg >> 8) & + MAX3107_SPI_TX_DATA_MASK); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG); + buf[len++] |= ((s->brg_cfg) & 0xff); + s->brg_commit = 0; + } + spin_unlock(&s->data_lock); + + if (len > 0) { + if (max3107_rw(s, (u8 *)buf, NULL, len*2)) + dev_err(&s->spi->dev, + "SPI transfer for config failed\n"); + } + + /* Reloop if interrupt handling indicated data in RX FIFO */ + } while (rxlvl); + +} + +/* Set sleep mode */ +static void max3107_set_sleep(struct max3107_port *s, int mode) +{ + u16 buf[1]; /* Buffer for SPI transfer */ + + DBG_TRACE("enter, mode %d\n", mode); + + buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + spin_lock(&s->data_lock); + switch (mode) { + case MAX3107_DISABLE_FORCED_SLEEP: + s->mode1_reg &= ~MAX3107_MODE1_FORCESLEEP_BIT; + break; + case MAX3107_ENABLE_FORCED_SLEEP: + s->mode1_reg |= MAX3107_MODE1_FORCESLEEP_BIT; + break; + case MAX3107_DISABLE_AUTOSLEEP: + s->mode1_reg &= ~MAX3107_MODE1_AUTOSLEEP_BIT; + break; + case MAX3107_ENABLE_AUTOSLEEP: + s->mode1_reg |= MAX3107_MODE1_AUTOSLEEP_BIT; + break; + default: + spin_unlock(&s->data_lock); + dev_warn(&s->spi->dev, "invalid sleep mode\n"); + return; + } + buf[0] |= s->mode1_reg; + spin_unlock(&s->data_lock); + + if (max3107_rw(s, (u8 *)buf, NULL, 2)) + dev_err(&s->spi->dev, "SPI transfer for sleep mode failed\n"); + + if (mode == MAX3107_DISABLE_AUTOSLEEP || + mode == MAX3107_DISABLE_FORCED_SLEEP ) { + msleep(MAX3107_WAKEUP_DELAY); + } +} + +/* Perform full register initialization */ +static void max3107_register_init(struct max3107_port *s) +{ + int len = 0; /* SPI transfer buffer length */ + u16 buf[11]; /* Buffer for SPI transfers */ + + DBG_TRACE("enter\n"); + + /* 1. Configure baud rate, 9600 as default */ + s->baud = 9600; + if (s->ext_clk) + s->brg_cfg = MAX3107_BRG_B9600; + else + s->brg_cfg = MAX3107_BRG_IB9600; + buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG); + buf[len++] |= ((s->brg_cfg >> 16) & MAX3107_SPI_TX_DATA_MASK); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG); + buf[len++] |= ((s->brg_cfg >> 8) & MAX3107_SPI_TX_DATA_MASK); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG); + buf[len++] |= ((s->brg_cfg) & 0xff); + + /* 2. Configure LCR register, 8N1 mode by default */ + s->lcr_reg = MAX3107_LCR_WORD_LEN_8; + buf[len] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG); + buf[len++] |= s->lcr_reg; + + /* 3. Configure MODE 1 register */ + s->mode1_reg = 0; + /* Enable IRQ pin */ + s->mode1_reg |= MAX3107_MODE1_IRQSEL_BIT; + /* Disable TX */ + s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT; + s->tx_enabled = 0; + /* RX is enabled */ + s->rx_enabled = 1; + buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + buf[len++] |= s->mode1_reg; + + /* 4. Configure MODE 2 register */ + buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG); + if (s->loopback) { + /* Enable loopback */ + buf[len] |= MAX3107_MODE2_LOOPBACK_BIT; + } + /* Reset FIFOs */ + buf[len++] |= MAX3107_MODE2_FIFORST_BIT; + s->tx_fifo_empty = 1; + + /* 5. Configure FIFO trigger level register */ + buf[len] = (MAX3107_WRITE_BIT | MAX3107_FIFOTRIGLVL_REG); + /* RX FIFO trigger for 16 words, TX FIFO trigger not used */ + buf[len++] |= (MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(0)); + + /* 6. Configure flow control levels */ + buf[len] = (MAX3107_WRITE_BIT | MAX3107_FLOWLVL_REG); + /* Flow control halt level 96, resume level 48 */ + buf[len++] |= (MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96)); + + /* 7. Configure flow control */ + buf[len] = (MAX3107_WRITE_BIT | MAX3107_FLOWCTRL_REG); + /* Enable auto CTS and auto RTS flow control */ + buf[len++] |= (MAX3107_FLOWCTRL_AUTOCTS_BIT | + MAX3107_FLOWCTRL_AUTORTS_BIT); + + /* 8. Configure RX timeout register */ + buf[len] = (MAX3107_WRITE_BIT | MAX3107_RXTO_REG); + /* Timeout after 48 character intervals */ + buf[len++] |= 0x0030; + + /* 9. Configure LSR interrupt enable register */ + buf[len] = (MAX3107_WRITE_BIT | MAX3107_LSR_IRQEN_REG); + /* Enable RX timeout interrupt */ + buf[len++] |= MAX3107_LSR_RXTO_BIT; + + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, len*2)) + dev_err(&s->spi->dev, "SPI transfer for init failed\n"); + + len = 0; + /* 10. Clear IRQ status register by reading it */ + buf[len++] = MAX3107_IRQSTS_REG; + + /* 11. Configure interrupt enable register */ + /* Enable LSR interrupt */ + s->irqen_reg = MAX3107_IRQ_LSR_BIT; + /* Enable RX FIFO interrupt */ + s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT; + buf[len] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + buf[len++] |= s->irqen_reg; + + /* 12. Clear FIFO reset that was set in step 6 */ + buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG); + if (s->loopback) { + /* Keep loopback enabled */ + buf[len] |= MAX3107_MODE2_LOOPBACK_BIT; + } + buf[len++] |= 0x0000; + + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, len*2)) + dev_err(&s->spi->dev, "SPI transfer for init failed\n"); + +} + +/* IRQ handler */ +static irqreturn_t max3107_irq(int irqno, void *dev_id) +{ + struct max3107_port *s = dev_id; + + if (irqno != s->spi->irq) { + /* Unexpected IRQ */ + return IRQ_NONE; + } + + /* Indicate irq */ + s->handle_irq = 1; + + /* Trigger work thread */ + max3107_dowork(s); + + return IRQ_HANDLED; +} + +/* HW suspension function + * + * Currently autosleep is used to decrease current consumption, alternative + * approach would be to set the chip to reset mode if UART is not being + * used but that would mess the GPIOs + * + */ +static void max3107_hw_susp(struct max3107_port *s, int suspend) +{ + DBG_TRACE("enter, suspend %d\n", suspend); + + if (suspend) { + /* Suspend requested, + * enable autosleep to decrease current consumption + */ + s->suspended = 1; + max3107_set_sleep(s, MAX3107_ENABLE_AUTOSLEEP); + } else { + /* Resume requested, + * disable autosleep + */ + s->suspended = 0; + max3107_set_sleep(s, MAX3107_DISABLE_AUTOSLEEP); + } +} + +/* Modem status IRQ enabling */ +static void max3107_enable_ms(struct uart_port *port) +{ + /* Modem status not supported */ +} + +/* Data send function */ +static void max3107_start_tx(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + DBG_TRACE("enter\n"); + + /* Trigger work thread for sending data */ + max3107_dowork(s); +} + +/* Function for checking that there is no pending transfers */ +static unsigned int max3107_tx_empty(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + DBG_TRACE("returning %d\n", + (s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit))); + return (s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit)); +} + +/* Function for stopping RX */ +static void max3107_stop_rx(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + DBG_TRACE("enter\n"); + + /* Set RX disabled in MODE 1 register */ + spin_lock(&s->data_lock); + s->mode1_reg |= MAX3107_MODE1_RXDIS_BIT; + s->mode1_commit = 1; + spin_unlock(&s->data_lock); + /* Set RX disabled */ + s->rx_enabled = 0; + /* Trigger work thread for doing the actual configuration change */ + max3107_dowork(s); +} + +/* Function for returning control pin states */ +static unsigned int max3107_get_mctrl(struct uart_port *port) +{ + /* DCD and DSR are not wired and CTS/RTS is handled automatically + * so just indicate DSR and CAR asserted + */ + return (TIOCM_DSR | TIOCM_CAR); +} + +/* Function for setting control pin states */ +static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* DCD and DSR are not wired and CTS/RTS is hadnled automatically + * so do nothing + */ +} + +/* Function for configuring UART parameters */ +static void max3107_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + struct tty_struct *tty; + int baud; + u16 new_lcr = 0; + u32 new_brg = 0; + + DBG_TRACE("enter\n"); + + if (!port->state) { + /* UART is not open */ + dev_warn(&s->spi->dev, "UART is closed\n"); + return; + } + + tty = port->state->port.tty; + if (!tty) { + /* TTY is not open */ + dev_warn(&s->spi->dev, "TTY is closed\n"); + return; + } + + if (old) { + if ((termios->c_cflag == old->c_cflag) && + (RELEVANT_IFLAG(termios->c_iflag) == + RELEVANT_IFLAG(old->c_iflag))) { + /* Nothing relevant is changing */ + return; + } + } + + /* Get new LCR register values */ + /* Word size */ + if ((termios->c_cflag & CSIZE) == CS7) + new_lcr |= MAX3107_LCR_WORD_LEN_7; + else + new_lcr |= MAX3107_LCR_WORD_LEN_8; + + /* Parity */ + if (termios->c_cflag & PARENB) { + new_lcr |= MAX3107_LCR_PARITY_BIT; + if (!(termios->c_cflag & PARODD)) + new_lcr |= MAX3107_LCR_EVENPARITY_BIT; + } + + /* Stop bits */ + if (termios->c_cflag & CSTOPB) { + /* 2 stop bits */ + new_lcr |= MAX3107_LCR_STOPLEN_BIT; + } + + /* Mask termios capabilities we don't support */ + termios->c_cflag &= ~CMSPAR; + + /* Set status ignore mask */ + s->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + s->port.ignore_status_mask |= MAX3107_ALL_ERRORS; + + /* Set low latency to immediately handle pushed data */ + s->port.state->port.tty->low_latency = 1; + + /* Get new baud rate generator configuration */ + baud = tty_get_baud_rate(tty); + switch (baud) { + case 300: + new_brg = s->ext_clk ? MAX3107_BRG_B300 : MAX3107_BRG_IB300; + break; + case 600: + new_brg = s->ext_clk ? MAX3107_BRG_B600 : MAX3107_BRG_IB600; + break; + case 1200: + new_brg = s->ext_clk ? MAX3107_BRG_B1200 : MAX3107_BRG_IB1200; + break; + case 2400: + new_brg = s->ext_clk ? MAX3107_BRG_B2400 : MAX3107_BRG_IB2400; + break; + case 4800: + new_brg = s->ext_clk ? MAX3107_BRG_B4800 : MAX3107_BRG_IB4800; + break; + case 9600: + new_brg = s->ext_clk ? MAX3107_BRG_B9600 : MAX3107_BRG_IB9600; + break; + case 19200: + new_brg = s->ext_clk ? MAX3107_BRG_B19200 : MAX3107_BRG_IB19200; + break; + case 38400: + new_brg = s->ext_clk ? MAX3107_BRG_B38400 : MAX3107_BRG_IB38400; + break; + case 57600: + new_brg = s->ext_clk ? MAX3107_BRG_B57600 : MAX3107_BRG_IB57600; + break; + case 115200: + new_brg = s->ext_clk ? MAX3107_BRG_B115200 : MAX3107_BRG_IB115200; + break; + case 230400: + new_brg = s->ext_clk ? MAX3107_BRG_B230400 : MAX3107_BRG_IB230400; + break; + case 460800: + new_brg = s->ext_clk ? MAX3107_BRG_B460800 : MAX3107_BRG_IB460800; + break; + case 921600: + new_brg = s->ext_clk ? MAX3107_BRG_B921600 : MAX3107_BRG_IB921600; + break; + default: + /* Use previous */ + baud = s->baud; + new_brg = s->brg_cfg; + tty_termios_encode_baud_rate(termios, baud, baud); + } + s->baud = baud; + + /* Update timeout according to new baud rate */ + uart_update_timeout(port, termios->c_cflag, baud); + + spin_lock(&s->data_lock); + if (s->lcr_reg != new_lcr) { + s->lcr_reg = new_lcr; + s->lcr_commit = 1; + } + if (s->brg_cfg != new_brg) { + s->brg_cfg = new_brg; + s->brg_commit = 1; + } + spin_unlock(&s->data_lock); + + /* Trigger work thread for doing the actual configuration change */ + max3107_dowork(s); +} + +/* Port shutdown function */ +static void max3107_shutdown(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + DBG_TRACE("enter\n"); + + if (s->suspended) { + /* Resume HW */ + max3107_hw_susp(s, 0); + } + + /* Free the interrupt */ + free_irq(s->spi->irq, s); + + if (s->workqueue) { + /* Flush and destroy work queue */ + flush_workqueue(s->workqueue); + destroy_workqueue(s->workqueue); + s->workqueue = NULL; + } + + /* Suspend HW */ + max3107_hw_susp(s, 1); +} + +/* Port startup function */ +static int max3107_startup(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + DBG_TRACE("enter\n"); + + /* Initialize work queue */ + s->workqueue = create_freezeable_workqueue("max3107"); + if (!s->workqueue) { + dev_err(&s->spi->dev, "Workqueue creation failed\n"); + return -EBUSY; + } + INIT_WORK(&s->work, max3107_work); + + /* Setup IRQ */ + if (request_irq(s->spi->irq, max3107_irq, IRQF_TRIGGER_FALLING, + "max3107", s)) { + dev_err(&s->spi->dev, "IRQ reguest failed\n"); + destroy_workqueue(s->workqueue); + s->workqueue = NULL; + return -EBUSY; + } + + /* Resume HW */ + max3107_hw_susp(s, 0); + + /* Init registers */ + max3107_register_init(s); + + return 0; +} + +/* Port type function */ +static const char *max3107_type(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + return s->spi->modalias; +} + +/* Port release function */ +static void max3107_release_port(struct uart_port *port) +{ + /* Do nothing */ +} + +/* Port request function */ +static int max3107_request_port(struct uart_port *port) +{ + /* Do nothing */ + return 0; +} + +/* Port config function */ +static void max3107_config_port(struct uart_port *port, int flags) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + /* Use PORT_MAX3100 since we are at least int the same serie */ + s->port.type = PORT_MAX3100; +} + +/* Port verify function */ +static int max3107_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3100) + return 0; + + return -EINVAL; +} + +/* Port stop TX function */ +static void max3107_stop_tx(struct uart_port *port) +{ + /* Do nothing */ +} + +/* Port break control function */ +static void max3107_break_ctl(struct uart_port *port, int break_state) +{ + /* We don't support break control, do nothing */ +} + +/* GPIO direction query function */ +static enum gpio_direction max3107_gpio_get_direction(struct gpio_chip *chip, + unsigned offset) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[1]; /* Buffer for SPI transfer */ + + DBG_TRACE("enter\n"); + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO configuration register */ + buf[0] = MAX3107_GPIOCFG_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO config read failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + + /* Check the direction bit */ + if (buf[0] & (0x0001 << offset)) + return DIRECTION_OUT; + return DIRECTION_IN; +} + +/* GPIO direction to input function */ +static int max3107_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[1]; /* Buffer for SPI transfer */ + + DBG_TRACE("enter\n"); + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO configuration register */ + buf[0] = MAX3107_GPIOCFG_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO config read failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + + /* Set GPIO to input */ + buf[0] &= ~(0x0001 << offset); + + /* Write new GPIO configuration register value */ + buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG); + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 2)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO config write failed\n"); + return -EIO; + } + return 0; +} + +/* GPIO direction to output function */ +static int max3107_gpio_direction_out(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[2]; /* Buffer for SPI transfers */ + + DBG_TRACE("enter\n"); + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO configuration and data registers */ + buf[0] = MAX3107_GPIOCFG_REG; + buf[1] = MAX3107_GPIODATA_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO config and data read failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + buf[1] &= MAX3107_SPI_RX_DATA_MASK; + + /* Set GPIO to output */ + buf[0] |= (0x0001 << offset); + /* Set value */ + if (value) + buf[1] |= (0x0001 << offset); + else + buf[1] &= ~(0x0001 << offset); + + /* Write new GPIO configuration and data register values */ + buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG); + buf[1] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG); + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 4)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO config and data write failed\n"); + return -EIO; + } + return 0; +} + +/* GPIO value query function */ +static int max3107_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[1]; /* Buffer for SPI transfer */ + + DBG_TRACE("enter\n"); + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO data register */ + buf[0] = MAX3107_GPIODATA_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO data read failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + + /* Return value */ + return buf[0] & (0x0001 << offset); +} + +/* GPIO value set function */ +static void max3107_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[2]; /* Buffer for SPI transfers */ + + DBG_TRACE("enter\n"); + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return; + } + + /* Read current GPIO configuration registers*/ + buf[0] = MAX3107_GPIODATA_REG; + buf[1] = MAX3107_GPIOCFG_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO data and config read failed\n"); + return; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + buf[1] &= MAX3107_SPI_RX_DATA_MASK; + + if (!(buf[1] & (0x0001 << offset))) { + /* Configured as input, can't set value */ + dev_warn(&s->spi->dev, + "Trying to set value for input GPIO\n"); + return; + } + + /* Set value */ + if (value) + buf[0] |= (0x0001 << offset); + else + buf[0] &= ~(0x0001 << offset); + + /* Write new GPIO data register value */ + buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG); + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 2)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO data write failed\n"); + } +} + +/* Platform data */ +static struct max3107_plat max3107_plat_data = { + .loopback = 0, + .ext_clk = 1, +#ifdef CONFIG_MAX3107_LOW_POWER + .max3107_hw_suspend = &max3107_hw_susp, +#endif /* CONFIG_MAX3107_LOW_POWER */ + .polled_mode = 0, + .poll_time = 0, +}; + +/* Port functions */ +static struct uart_ops max3107_ops = { + .tx_empty = max3107_tx_empty, + .set_mctrl = max3107_set_mctrl, + .get_mctrl = max3107_get_mctrl, + .stop_tx = max3107_stop_tx, + .start_tx = max3107_start_tx, + .stop_rx = max3107_stop_rx, + .enable_ms = max3107_enable_ms, + .break_ctl = max3107_break_ctl, + .startup = max3107_startup, + .shutdown = max3107_shutdown, + .set_termios = max3107_set_termios, + .type = max3107_type, + .release_port = max3107_release_port, + .request_port = max3107_request_port, + .config_port = max3107_config_port, + .verify_port = max3107_verify_port, +}; + +/* UART driver data */ +static struct uart_driver max3107_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "ttyMAX", + .dev_name = "ttyMAX", + .major = MAX3107_MAJOR, + .minor = MAX3107_MINOR, + .nr = 1, +}; + +/* GPIO chip data */ +static struct gpio_chip max3107_gpio_chip = { + .owner = THIS_MODULE, + .get_direction = max3107_gpio_get_direction, + .direction_input = max3107_gpio_direction_in, + .direction_output = max3107_gpio_direction_out, + .get = max3107_gpio_get, + .set = max3107_gpio_set, + .can_sleep = 1, + .base = MAX3107_GPIO_BASE, + .ngpio = MAX3107_GPIO_COUNT, +}; + +/* Device probe function */ +static int __devinit max3107_probe(struct spi_device *spi) +{ + struct max3107_port *s; + struct max3107_plat *pdata = &max3107_plat_data; + u16 buf[2]; /* Buffer for SPI transfers */ + int retval; + + DBG_TRACE("enter\n"); + + /* Reset the chip */ + if (gpio_request(MAX3107_RESET_GPIO, "max3107")) { + printk(KERN_ERR "Requesting RESET GPIO failed\n"); + return -EIO; + } + if (gpio_direction_output(MAX3107_RESET_GPIO, 0)) { + printk(KERN_ERR "Setting RESET GPIO to 0 failed\n"); + gpio_free(MAX3107_RESET_GPIO); + return -EIO; + } + msleep(MAX3107_RESET_DELAY); + if (gpio_direction_output(MAX3107_RESET_GPIO, 1)) { + printk(KERN_ERR "Setting RESET GPIO to 1 failed\n"); + gpio_free(MAX3107_RESET_GPIO); + return -EIO; + } + gpio_free(MAX3107_RESET_GPIO); + msleep(MAX3107_WAKEUP_DELAY); + + /* Allocate port structure */ + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + printk(KERN_ERR "Allocating port structure failed\n"); + return -ENOMEM; + } + + /* Initialize shared data lock */ + spin_lock_init(&s->data_lock); + + /* SPI intializations */ + dev_set_drvdata(&spi->dev, s); + spi->mode = SPI_MODE_0; + spi->dev.platform_data = pdata; + spi->bits_per_word = 16; + s->ext_clk = pdata->ext_clk; + s->loopback = pdata->loopback; + spi_setup(spi); + s->spi = spi; + + /* Check REV ID to ensure we are talking to what we expect */ + buf[0] = MAX3107_REVID_REG; + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, "SPI transfer for REVID read failed\n"); + return -EIO; + } + if ((buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID1 && + (buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID2) { + dev_err(&s->spi->dev, "REVID %x does not match\n", + (buf[0] & MAX3107_SPI_RX_DATA_MASK) ); + return -ENODEV; + } + + /* Disable all interrupts */ + buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG | 0x0000); + buf[0] |= 0x0000; + + /* Configure clock source */ + buf[1] = (MAX3107_WRITE_BIT | MAX3107_CLKSRC_REG); + if (s->ext_clk) { + /* External clock */ + buf[1] |= MAX3107_CLKSRC_EXTCLK_BIT; + } + /* PLL bypass */ + buf[1] |= MAX3107_CLKSRC_PLLBYP_BIT; + + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 4)) { + dev_err(&s->spi->dev, "SPI transfer for init failed\n"); + return -EIO; + } + + + /* Register UART driver */ + retval = uart_register_driver(&max3107_uart_driver); + if (retval) { + dev_err(&s->spi->dev, "Registering UART driver failed\n"); + return retval; + } + + /* Initialize UART port data */ + s->port.fifosize = 128; + s->port.ops = &max3107_ops; + s->port.line = 0; + s->port.dev = &spi->dev; + s->port.uartclk = 9600; + s->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + s->port.irq = s->spi->irq; + /* Use PORT_MAX3100 since we are at least int the same serie */ + s->port.type = PORT_MAX3100; + + /* Add UART port */ + retval = uart_add_one_port(&max3107_uart_driver, &s->port); + if (retval < 0) { + dev_err(&s->spi->dev, "Adding UART port failed\n"); + return retval; + } + + /* Initialize GPIO chip data */ + s->chip = max3107_gpio_chip; + s->chip.label = spi->modalias; + s->chip.dev = &spi->dev; + + /* Add GPIO chip */ + retval = gpiochip_add(&s->chip); + if (retval) { + dev_err(&s->spi->dev, "Adding GPIO chip failed\n"); + return retval; + } + + /* Go to suspend mode */ + max3107_hw_susp(s, 1); + + return 0; +} + +/* Driver remove function */ +static int __devexit max3107_remove(struct spi_device *spi) +{ + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + DBG_TRACE("enter\n"); + + /* Remove GPIO chip */ + if (gpiochip_remove(&s->chip)) + dev_warn(&s->spi->dev, "Removing GPIO chip failed\n"); + + /* Remove port */ + if (uart_remove_one_port(&max3107_uart_driver, &s->port)) + dev_warn(&s->spi->dev, "Removing UART port failed\n"); + + /* Unregister UART driver */ + uart_unregister_driver(&max3107_uart_driver); + + /* Free port structure */ + kfree(s); + + return 0; +} + +/* Driver suspend function */ +static int max3107_suspend(struct spi_device *spi, pm_message_t state) +{ +#ifdef CONFIG_PM + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + DBG_TRACE("enter\n"); + + /* Suspend UART port */ + uart_suspend_port(&max3107_uart_driver, &s->port); + + /* Go to suspend mode */ + max3107_hw_susp(s, 1); +#endif /* CONFIG_PM */ + return 0; +} + +/* Driver resume function */ +static int max3107_resume(struct spi_device *spi) +{ +#ifdef CONFIG_PM + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + DBG_TRACE("enter\n"); + + /* Resume from suspend */ + max3107_hw_susp(s, 0); + + /* Resume UART port */ + uart_resume_port(&max3107_uart_driver, &s->port); +#endif /* CONFIG_PM */ + return 0; +} + +/* Spi driver data */ +static struct spi_driver max3107_driver = { + .driver = { + .name = "max3107", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = max3107_probe, + .remove = __devexit_p(max3107_remove), + .suspend = max3107_suspend, + .resume = max3107_resume, +}; + +/* Driver init function */ +static int __init max3107_init(void) +{ + DBG_TRACE("enter\n"); + return spi_register_driver(&max3107_driver); +} + +/* Driver exit function */ +static void __exit max3107_exit(void) +{ + DBG_TRACE("enter\n"); + spi_unregister_driver(&max3107_driver); +} + +module_init(max3107_init); +module_exit(max3107_exit); + +MODULE_DESCRIPTION("MAX3107 driver"); +MODULE_AUTHOR("Aavamobile"); +MODULE_ALIAS("max3107-spi-uart"); +MODULE_LICENSE("GPLv2"); Index: linux-2.6.33/drivers/spi/mrst_spi.c =================================================================== --- linux-2.6.33.orig/drivers/spi/mrst_spi.c +++ linux-2.6.33/drivers/spi/mrst_spi.c @@ -1364,8 +1364,16 @@ static struct pci_driver mrst_spi_driver .resume = mrst_spi_resume, }; +/* + * spi_register_master will call scan board info, and MRST + * should only have one board_info registered + */ static int __init mrst_spi_init(void) { +/*#ifdef CONFIG_SERIAL_MAX3107*/ +/* spi_register_board_info(mrst_spi_board_info,*/ +/* ARRAY_SIZE(mrst_spi_board_info));*/ +/*#endif*/ return pci_register_driver(&mrst_spi_driver); } Index: linux-2.6.33/include/linux/serial_max3107.h =================================================================== --- /dev/null +++ linux-2.6.33/include/linux/serial_max3107.h @@ -0,0 +1,352 @@ +/* + * max3107.h - spi uart protocol driver header for Maxim 3107 + * + * Copyright (C) Aavamobile 2009 + * Based on serial_max3100.h by Christian Pellegrin + * + * 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_SERIAL_MAX3107_H +#define _LINUX_SERIAL_MAX3107_H + +/* Serial definitions */ +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + +/* Serial error status definitions */ +#define MAX3107_PARITY_ERROR 1 +#define MAX3107_FRAME_ERROR 2 +#define MAX3107_OVERRUN_ERROR 4 +#define MAX3107_ALL_ERRORS (MAX3107_PARITY_ERROR | \ + MAX3107_FRAME_ERROR | \ + MAX3107_OVERRUN_ERROR) + + +/* TTY definitions */ +#define MAX3107_MAJOR TTY_MAJOR +#define MAX3107_MINOR 65 + + +/* GPIO definitions */ +#define MAX3107_GPIO_BASE 88 +#define MAX3107_GPIO_COUNT 4 + + +/* GPIO connected to chip's reset pin */ +#define MAX3107_RESET_GPIO 87 + + +/* Chip reset delay */ +#define MAX3107_RESET_DELAY 10 + +/* Chip wakeup delay */ +#define MAX3107_WAKEUP_DELAY 50 + + +/* Sleep mode definitions */ +#define MAX3107_DISABLE_FORCED_SLEEP 0 +#define MAX3107_ENABLE_FORCED_SLEEP 1 +#define MAX3107_DISABLE_AUTOSLEEP 2 +#define MAX3107_ENABLE_AUTOSLEEP 3 + + +/* Definitions for register access with SPI transfers + * + * SPI transfer format: + * + * Master to slave bits xzzzzzzzyyyyyyyy + * Slave to master bits aaaaaaaabbbbbbbb + * + * where: + * x = 0 for reads, 1 for writes + * z = register address + * y = new register value if write, 0 if read + * a = unspecified + * b = register value if read, unspecified if write + */ + +/* SPI speed */ +#define MAX3107_SPI_SPEED (3125000 * 2) + +/* Write bit */ +#define MAX3107_WRITE_BIT (1 << 15) + +/* SPI TX data mask */ +#define MAX3107_SPI_RX_DATA_MASK (0x00ff) + +/* SPI RX data mask */ +#define MAX3107_SPI_TX_DATA_MASK (0x00ff) + +/* Register access masks */ +#define MAX3107_RHR_REG (0x0000) /* RX FIFO */ +#define MAX3107_THR_REG (0x0000) /* TX FIFO */ +#define MAX3107_IRQEN_REG (0x0100) /* IRQ enable */ +#define MAX3107_IRQSTS_REG (0x0200) /* IRQ status */ +#define MAX3107_LSR_IRQEN_REG (0x0300) /* LSR IRQ enable */ +#define MAX3107_LSR_IRQSTS_REG (0x0400) /* LSR IRQ status */ +#define MAX3107_SPCHR_IRQEN_REG (0x0500) /* Special char IRQ enable */ +#define MAX3107_SPCHR_IRQSTS_REG (0x0600) /* Special char IRQ status */ +#define MAX3107_STS_IRQEN_REG (0x0700) /* Status IRQ enable */ +#define MAX3107_STS_IRQSTS_REG (0x0800) /* Status IRQ status */ +#define MAX3107_MODE1_REG (0x0900) /* MODE1 */ +#define MAX3107_MODE2_REG (0x0a00) /* MODE2 */ +#define MAX3107_LCR_REG (0x0b00) /* LCR */ +#define MAX3107_RXTO_REG (0x0c00) /* RX timeout */ +#define MAX3107_HDPIXDELAY_REG (0x0d00) /* Auto transceiver delays */ +#define MAX3107_IRDA_REG (0x0e00) /* IRDA settings */ +#define MAX3107_FLOWLVL_REG (0x0f00) /* Flow control levels */ +#define MAX3107_FIFOTRIGLVL_REG (0x1000) /* FIFO IRQ trigger levels */ +#define MAX3107_TXFIFOLVL_REG (0x1100) /* TX FIFO level */ +#define MAX3107_RXFIFOLVL_REG (0x1200) /* RX FIFO level */ +#define MAX3107_FLOWCTRL_REG (0x1300) /* Flow control */ +#define MAX3107_XON1_REG (0x1400) /* XON1 character */ +#define MAX3107_XON2_REG (0x1500) /* XON2 character */ +#define MAX3107_XOFF1_REG (0x1600) /* XOFF1 character */ +#define MAX3107_XOFF2_REG (0x1700) /* XOFF2 character */ +#define MAX3107_GPIOCFG_REG (0x1800) /* GPIO config */ +#define MAX3107_GPIODATA_REG (0x1900) /* GPIO data */ +#define MAX3107_PLLCFG_REG (0x1a00) /* PLL config */ +#define MAX3107_BRGCFG_REG (0x1b00) /* Baud rate generator conf */ +#define MAX3107_BRGDIVLSB_REG (0x1c00) /* Baud rate divisor LSB */ +#define MAX3107_BRGDIVMSB_REG (0x1d00) /* Baud rate divisor MSB */ +#define MAX3107_CLKSRC_REG (0x1e00) /* Clock source */ +#define MAX3107_REVID_REG (0x1f00) /* Revision identification */ + +/* IRQ register bits */ +#define MAX3107_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ +#define MAX3107_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */ +#define MAX3107_IRQ_STS_BIT (1 << 2) /* Status interrupt */ +#define MAX3107_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */ +#define MAX3107_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */ +#define MAX3107_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */ +#define MAX3107_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */ +#define MAX3107_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */ + +/* LSR register bits */ +#define MAX3107_LSR_RXTO_BIT (1 << 0) /* RX timeout */ +#define MAX3107_LSR_RXOVR_BIT (1 << 1) /* RX overrun */ +#define MAX3107_LSR_RXPAR_BIT (1 << 2) /* RX parity error */ +#define MAX3107_LSR_FRERR_BIT (1 << 3) /* Frame error */ +#define MAX3107_LSR_RXBRK_BIT (1 << 4) /* RX break */ +#define MAX3107_LSR_RXNOISE_BIT (1 << 5) /* RX noise */ +#define MAX3107_LSR_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_LSR_CTS_BIT (1 << 7) /* CTS pin state */ + +/* Special character register bits */ +#define MAX3107_SPCHR_XON1_BIT (1 << 0) /* XON1 character */ +#define MAX3107_SPCHR_XON2_BIT (1 << 1) /* XON2 character */ +#define MAX3107_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */ +#define MAX3107_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */ +#define MAX3107_SPCHR_BREAK_BIT (1 << 4) /* RX break */ +#define MAX3107_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */ +#define MAX3107_SPCHR_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_SPCHR_UNDEF7_BIT (1 << 7) /* Undefined/not used */ + +/* Status register bits */ +#define MAX3107_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */ +#define MAX3107_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */ +#define MAX3107_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */ +#define MAX3107_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */ +#define MAX3107_STS_UNDEF4_BIT (1 << 4) /* Undefined/not used */ +#define MAX3107_STS_CLKREADY_BIT (1 << 5) /* Clock ready */ +#define MAX3107_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */ +#define MAX3107_STS_UNDEF7_BIT (1 << 7) /* Undefined/not used */ + +/* MODE1 register bits */ +#define MAX3107_MODE1_RXDIS_BIT (1 << 0) /* RX disable */ +#define MAX3107_MODE1_TXDIS_BIT (1 << 1) /* TX disable */ +#define MAX3107_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */ +#define MAX3107_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */ +#define MAX3107_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */ +#define MAX3107_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */ +#define MAX3107_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */ +#define MAX3107_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */ + +/* MODE2 register bits */ +#define MAX3107_MODE2_RST_BIT (1 << 0) /* Chip reset */ +#define MAX3107_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */ +#define MAX3107_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */ +#define MAX3107_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */ +#define MAX3107_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */ +#define MAX3107_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */ +#define MAX3107_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */ +#define MAX3107_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */ + +/* LCR register bits */ +#define MAX3107_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ +#define MAX3107_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 + * + * Word length bits table: + * 00 -> 5 bit words + * 01 -> 6 bit words + * 10 -> 7 bit words + * 11 -> 8 bit words + */ +#define MAX3107_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit + * + * STOP length bit table: + * 0 -> 1 stop bit + * 1 -> 1-1.5 stop bits if + * word length is 5, + * 2 stop bits otherwise + */ +#define MAX3107_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ +#define MAX3107_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ +#define MAX3107_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ +#define MAX3107_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ +#define MAX3107_LCR_RTS_BIT (1 << 7) /* RTS pin control */ +#define MAX3107_LCR_WORD_LEN_5 (0x0000) +#define MAX3107_LCR_WORD_LEN_6 (0x0001) +#define MAX3107_LCR_WORD_LEN_7 (0x0002) +#define MAX3107_LCR_WORD_LEN_8 (0x0003) + + +/* IRDA register bits */ +#define MAX3107_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */ +#define MAX3107_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */ +#define MAX3107_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */ +#define MAX3107_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */ +#define MAX3107_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */ +#define MAX3107_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */ +#define MAX3107_IRDA_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_IRDA_UNDEF7_BIT (1 << 7) /* Undefined/not used */ + +/* Flow control trigger level register masks */ +#define MAX3107_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */ +#define MAX3107_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */ +#define MAX3107_FLOWLVL_HALT(words) ((words/8) & 0x000f) +#define MAX3107_FLOWLVL_RES(words) (((words/8) & 0x000f) << 4) + +/* FIFO interrupt trigger level register masks */ +#define MAX3107_FIFOTRIGLVL_TX_MASK (0x000f) /* TX FIFO trigger level */ +#define MAX3107_FIFOTRIGLVL_RX_MASK (0x00f0) /* RX FIFO trigger level */ +#define MAX3107_FIFOTRIGLVL_TX(words) ((words/8) & 0x000f) +#define MAX3107_FIFOTRIGLVL_RX(words) (((words/8) & 0x000f) << 4) + +/* Flow control register bits */ +#define MAX3107_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */ +#define MAX3107_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */ +#define MAX3107_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs + * are used in conjunction with + * XOFF2 for definition of + * special character */ +#define MAX3107_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */ +#define MAX3107_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */ +#define MAX3107_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1 + * + * SWFLOW bits 1 & 0 table: + * 00 -> no transmitter flow + * control + * 01 -> receiver compares + * XON2 and XOFF2 + * and controls + * transmitter + * 10 -> receiver compares + * XON1 and XOFF1 + * and controls + * transmitter + * 11 -> receiver compares + * XON1, XON2, XOFF1 and + * XOFF2 and controls + * transmitter + */ +#define MAX3107_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */ +#define MAX3107_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3 + * + * SWFLOW bits 3 & 2 table: + * 00 -> no received flow + * control + * 01 -> transmitter generates + * XON2 and XOFF2 + * 10 -> transmitter generates + * XON1 and XOFF1 + * 11 -> transmitter generates + * XON1, XON2, XOFF1 and + * XOFF2 + */ + +/* GPIO configuration register bits */ +#define MAX3107_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */ +#define MAX3107_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */ +#define MAX3107_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */ +#define MAX3107_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */ +#define MAX3107_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */ +#define MAX3107_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */ +#define MAX3107_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */ +#define MAX3107_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */ + +/* GPIO DATA register bits */ +#define MAX3107_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */ +#define MAX3107_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */ +#define MAX3107_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */ +#define MAX3107_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */ +#define MAX3107_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */ +#define MAX3107_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */ +#define MAX3107_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */ +#define MAX3107_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */ + +/* PLL configuration register masks */ +#define MAX3107_PLLCFG_PREDIV_MASK (0x003f) /* PLL predivision value */ +#define MAX3107_PLLCFG_PLLFACTOR_MASK (0x00c0) /* PLL multiplication factor */ + +/* Baud rate generator configuration register masks and bits */ +#define MAX3107_BRGCFG_FRACT_MASK (0x000f) /* Fractional portion of + * Baud rate generator divisor + */ +#define MAX3107_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */ +#define MAX3107_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */ +#define MAX3107_BRGCFG_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_BRGCFG_UNDEF7_BIT (1 << 7) /* Undefined/not used */ + +/* Clock source register bits */ +#define MAX3107_CLKSRC_INTOSC_BIT (1 << 0) /* Internal osc enable */ +#define MAX3107_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */ +#define MAX3107_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */ +#define MAX3107_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */ +#define MAX3107_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */ +#define MAX3107_CLKSRC_UNDEF5_BIT (1 << 5) /* Undefined/not used */ +#define MAX3107_CLKSRC_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */ + + +/* HW definitions */ +#define MAX3107_RX_FIFO_SIZE 128 +#define MAX3107_TX_FIFO_SIZE 128 +#define MAX3107_REVID1 0x00a0 +#define MAX3107_REVID2 0x00a1 + + +/* Baud rate generator configuration values for external clock */ +#define MAX3107_BRG_B300 (0x0A9400 | 0x05) +#define MAX3107_BRG_B600 (0x054A00 | 0x03) +#define MAX3107_BRG_B1200 (0x02A500 | 0x01) +#define MAX3107_BRG_B2400 (0x015200 | 0x09) +#define MAX3107_BRG_B4800 (0x00A900 | 0x04) +#define MAX3107_BRG_B9600 (0x005400 | 0x0A) +#define MAX3107_BRG_B19200 (0x002A00 | 0x05) +#define MAX3107_BRG_B38400 (0x001500 | 0x03) +#define MAX3107_BRG_B57600 (0x000E00 | 0x02) +#define MAX3107_BRG_B115200 (0x000700 | 0x01) +#define MAX3107_BRG_B230400 (0x000300 | 0x08) +#define MAX3107_BRG_B460800 (0x000100 | 0x0c) +#define MAX3107_BRG_B921600 (0x000100 | 0x1c) + +/* Baud rate generator configuration values for internal clock */ +#define MAX3107_BRG_IB300 (0x008000 | 0x00) +#define MAX3107_BRG_IB600 (0x004000 | 0x00) +#define MAX3107_BRG_IB1200 (0x002000 | 0x00) +#define MAX3107_BRG_IB2400 (0x001000 | 0x00) +#define MAX3107_BRG_IB4800 (0x000800 | 0x00) +#define MAX3107_BRG_IB9600 (0x000400 | 0x00) +#define MAX3107_BRG_IB19200 (0x000200 | 0x00) +#define MAX3107_BRG_IB38400 (0x000100 | 0x00) +#define MAX3107_BRG_IB57600 (0x000000 | 0x0B) +#define MAX3107_BRG_IB115200 (0x000000 | 0x05) +#define MAX3107_BRG_IB230400 (0x000000 | 0x03) +#define MAX3107_BRG_IB460800 (0x000000 | 0x00) +#define MAX3107_BRG_IB921600 (0x000000 | 0x00) + +#endif /* _LINUX_SERIAL_MAX3107_H */ Index: linux-2.6.33/include/drm/drm_mode.h =================================================================== --- linux-2.6.33.orig/include/drm/drm_mode.h +++ linux-2.6.33/include/drm/drm_mode.h @@ -160,9 +160,9 @@ struct drm_mode_get_encoder { #define DRM_MODE_CONNECTOR_DisplayPort 10 #define DRM_MODE_CONNECTOR_HDMIA 11 #define DRM_MODE_CONNECTOR_HDMIB 12 -#define DRM_MODE_CONNECTOR_TV 13 +#define DRM_MODE_CONNECTOR_TV 15 #define DRM_MODE_CONNECTOR_eDP 14 -#define DRM_MODE_CONNECTOR_MIPI 15 +#define DRM_MODE_CONNECTOR_MIPI 13 struct drm_mode_get_connector { Index: linux-2.6.33/include/sound/intel_sst_ioctl.h =================================================================== --- linux-2.6.33.orig/include/sound/intel_sst_ioctl.h +++ linux-2.6.33/include/sound/intel_sst_ioctl.h @@ -361,4 +361,9 @@ struct snd_sst_dbufs { #define SNDRV_SST_SET_TARGET_DEVICE _IOW('L', 0x21, \ struct snd_sst_target_device *) +#define SNDRV_SST_AAVA_HACK _IO('L', 0x22) + +/*Test utility for testing comms */ +#define SNDRV_LPE_TEST_COMMS _IOW('L', 0x23, int) + #endif /* __INTEL_SST_IOCTL_H__ */ Index: linux-2.6.33/sound/pci/sst/Makefile =================================================================== --- linux-2.6.33.orig/sound/pci/sst/Makefile +++ linux-2.6.33/sound/pci/sst/Makefile @@ -2,7 +2,7 @@ # Makefile for Intel MID Audio drivers # EXTRA_CFLAGS=-g -DCONFIG_MRST_RAR_HANDLER -snd-intel-sst-objs := intel_sst.o intel_sst_ipc.o intel_sst_stream.o intel_sst_interface.o intel_sst_dsp.o intel_sst_pvt.o +snd-intel-sst-objs := intel_sst.o intel_sst_ipc.o intel_sst_stream.o intel_sst_interface.o intel_sst_dsp.o intel_sst_pvt.o koski_audio_conf.o snd-intelmid-objs := intelmid.o intelmid_v0_control.o intelmid_v1_control.o intelmid_v2_control.o intelmid_ctrl.o intelmid_pvt.o # Toplevel Module Dependency obj-$(CONFIG_SND_INTEL_SST) += snd-intel-sst.o Index: linux-2.6.33/sound/pci/sst/intel_sst_interface.c =================================================================== --- linux-2.6.33.orig/sound/pci/sst/intel_sst_interface.c +++ linux-2.6.33/sound/pci/sst/intel_sst_interface.c @@ -54,6 +54,7 @@ #include "intel_sst_fw_ipc.h" #include "intel_sst_common.h" +#include "koski_audio_conf.h" #define AM_MODULE 1 #define STREAM_MODULE 0 @@ -1232,6 +1233,33 @@ int intel_sst_ioctl(struct inode *i_node } break; } + case _IOC_NR(SNDRV_SST_AAVA_HACK): + { + pr_debug("SNDRV_SST_AAVA_HACK recieved\n"); + sst_drv_ctx->scard_ops->set_pcm_audio_params(999999, 2); + break; + } + + case _IOC_NR(SNDRV_LPE_TEST_COMMS): { + int *coms = (int *)arg; + pr_debug("SNDRV_LPE_TEST_COMMS recieved!\n"); + if (*coms == 1) { + /* Activate voice port */ + sst_drv_ctx->scard_ops->set_pcm_voice_params(); + sst_drv_ctx->scard_ops->set_voice_port(ACTIVATE); + } else if (*coms == 2) { + /* Deactivate voice port */ + sst_drv_ctx->scard_ops->set_voice_port(DEACTIVATE); + + } else if (*coms == 3) { + /* Set voice parameters */ + sst_drv_ctx->scard_ops->set_voice_port(DEACTIVATE); + } + + retval = 0; + break; + } + case _IOC_NR(SNDRV_SST_SET_VOL): { struct snd_sst_vol *set_vol; struct snd_sst_vol *rec_vol = (struct snd_sst_vol *)arg; @@ -1482,6 +1510,10 @@ int intel_sst_ioctl(struct inode *i_node pr_debug("SNDRV_SST_SET_TARGET_PLAYBACK DEVICE recieved!\n"); target_device = (struct snd_sst_target_device *)arg; BUG_ON(!target_device); +//#ifdef KOSKI_AUDIO +#if 1 + configure_koski_audio(target_device->device_route); +#else if (minor != AM_MODULE) { dev_err(&sst_drv_ctx->pci->dev,\ "Time:%8ld::%8ld >> SST: %s %d ERR: called for non +\ @@ -1491,6 +1523,7 @@ int intel_sst_ioctl(struct inode *i_node break; } retval = sst_target_device_select(target_device); +#endif break; } Index: linux-2.6.33/sound/pci/sst/intelmid.c =================================================================== --- linux-2.6.33.orig/sound/pci/sst/intelmid.c +++ linux-2.6.33/sound/pci/sst/intelmid.c @@ -47,6 +47,7 @@ #include "intelmid_snd_control.h" #include "intelmid.h" #include "intelmid_ctrl.h" +#include "koski_audio_conf.h" MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); @@ -65,6 +66,7 @@ MODULE_PARM_DESC(card_id, "ID string for int sst_card_vendor_id; int audio_interrupt_enable = 0; +volatile int koski_jack; /* Data path functionalities */ static struct snd_pcm_hardware snd_intelmad_stream = { @@ -452,6 +454,7 @@ void sst_process_mad_jack_detection(stru {0x18b, 0x10, MASK4}, }; struct sc_reg_access sc_access_read = {0,}; + struct sc_reg_access sc_access_write_koski[1] = {{0,},}; struct sc_reg_access sc_access_write[] = { {0x198, 0x00, 0x0}, @@ -463,6 +466,7 @@ void sst_process_mad_jack_detection(stru mad_jack_detect->intelmaddata; intsts = mad_jack_detect->intsts; + printk("*******************************************\n"); switch (intelmaddata->sstdrv_ops->vendor_id) { case SND_FS: @@ -522,13 +526,13 @@ void sst_process_mad_jack_detection(stru sc_access_read.reg_addr = 0x201; sst_sc_reg_access(&sc_access_read, PMIC_READ, 1); value = (sc_access_read.value); - pr_debug("value returned = 0x%x\n", value); + printk("JKSNS value returned = 0x%x\n", value); } - if (value & 0xc0) { + if (value == 0xc0) { /*accessory is removed */ if (intelmaddata->jack[0].jack_status) { - pr_debug("MAD headset detected\n"); + /* pr_debug("MAD headset detected\n");*/ /* send headphone detect/undetect */ jack = &intelmaddata->jack[0].jack; present = 0; @@ -536,7 +540,7 @@ void sst_process_mad_jack_detection(stru } if (intelmaddata->jack[1].jack_status) { - pr_debug("MAD headphone detected\n"); + /* pr_debug("MAD headphone detected\n");*/ /* send headphone detect/undetect */ jack = &intelmaddata->jack[1].jack; present = 0; @@ -545,11 +549,11 @@ void sst_process_mad_jack_detection(stru } - if (value & 0x00) { + if (value == 0x00) { if (intelmaddata->jack[1].jack_status) { /*button pressed */ - pr_debug("MAD headphone detected\n"); + /* pr_debug("MAD headphone detected\n");*/ /* send headphone detect/undetect */ jack = &intelmaddata->jack[2].jack; present = 1; @@ -558,7 +562,7 @@ void sst_process_mad_jack_detection(stru buttonpressflag = 1; } else { - pr_debug("MAD headphone detected\n"); + /* pr_debug("MAD headphone detected\n");*/ /* send headphone detect/undetect */ jack = &intelmaddata->jack[0].jack; present = 1; @@ -566,8 +570,8 @@ void sst_process_mad_jack_detection(stru jack_event_flag = 1; } } - if (value & 0x40) { - pr_debug("MAD headset detected\n"); + if (value == 0x40) { + /* pr_debug("MAD headset detected\n");*/ /* send headphone detect/undetect */ jack = &intelmaddata->jack[1].jack; present = 1; @@ -576,6 +580,12 @@ void sst_process_mad_jack_detection(stru } } + + sc_access_write_koski[0].reg_addr = 0x204; + sc_access_write_koski[0].value = 0xe; + sst_sc_reg_access(sc_access_write_koski, PMIC_WRITE, 1); + printk("MX Audio interrupt enable... DONE\n"); + break; case SND_NC: if (intelmaddata->pmic_status == PMIC_INIT) { @@ -619,11 +629,38 @@ void sst_process_mad_jack_detection(stru if (!jack) { pr_debug("MAD error jack empty \n"); - - } else { - pr_debug("MAD sending jack report for = %d!!!\n", present); + } + else + { + if (present) + { + printk("Jack... YES\n"); + set_output_device(OUTPUT_HEADSET); + koski_jack = 1; + } + else + { + printk("Jack... NO\n"); + set_output_device(OUTPUT_SPEAKER); + koski_jack = 0; + } if (jack) - pr_debug("MAD sending jack report for = %d !!!\n", jack->type); + { + switch (jack->type) { + case SND_JACK_HEADPHONE: + printk("SND_JACK_HEADPHONE\n"); + break; + case SND_JACK_HEADSET: + printk("SND_JACK_HEADSET\n"); + break; + case SND_JACK_HS_SHORT_PRESS: + printk("SND_JACK_HS_SHORT_PRESS\n"); + break; + case SND_JACK_HS_LONG_PRESS: + printk("SND_JACK_HS_LONG_PRESS\n"); + break; + } + } if (jack_event_flag) snd_jack_report(jack, present); if (buttonpressflag) Index: linux-2.6.33/sound/pci/sst/intelmid_ctrl.c =================================================================== --- linux-2.6.33.orig/sound/pci/sst/intelmid_ctrl.c +++ linux-2.6.33/sound/pci/sst/intelmid_ctrl.c @@ -43,11 +43,17 @@ #include "intelmid_snd_control.h" #include "intelmid.h" +extern int sst_card_vendor_id; + static char *out_names[] = {"Headphones", - "Internal speakers"}; + "Internal speakers", + "Receiver Koski" + }; static char *in_names[] = {"HS_MIC", - "AMIC", - "DMIC"}; + "AMIC", + "DMIC", + "LINE_IN Koski" + }; struct snd_pmic_ops *intelmad_vendor_ops[MAX_VENDORS] = { &snd_pmic_ops_fs, Index: linux-2.6.33/sound/pci/sst/intelmid_snd_control.h =================================================================== --- linux-2.6.33.orig/sound/pci/sst/intelmid_snd_control.h +++ linux-2.6.33/sound/pci/sst/intelmid_snd_control.h @@ -75,12 +75,14 @@ enum SND_INPUT_DEVICE { HS_MIC, AMIC, DMIC, + LINE_IN, IN_UNDEFINED }; enum SND_OUTPUT_DEVICE { STEREO_HEADPHONE, INTERNAL_SPKR, + RECEIVER, OUT_UNDEFINED }; @@ -100,8 +102,11 @@ enum pmic_controls { PMIC_SND_RIGHT_PB_VOL = 0x0011, PMIC_SND_LEFT_HP_MUTE = 0x0012, PMIC_SND_RIGHT_HP_MUTE = 0x0013, - PMIC_SND_LEFT_SPEAKER_MUTE = 0x0014, - PMIC_SND_RIGHT_SPEAKER_MUTE = 0x0015, + PMIC_SND_LEFT_SPEAKER_MUTE = 0x0014, + PMIC_SND_RIGHT_SPEAKER_MUTE = 0x0015, + PMIC_SND_RECEIVER_VOL = 0x0016, + PMIC_SND_RECEIVER_MUTE = 0x0017, + /* Other controls */ PMIC_SND_MUTE_ALL = 0x0020, PMIC_MAX_CONTROLS = 0x0020, Index: linux-2.6.33/sound/pci/sst/intelmid_v1_control.c =================================================================== --- linux-2.6.33.orig/sound/pci/sst/intelmid_v1_control.c +++ linux-2.6.33/sound/pci/sst/intelmid_v1_control.c @@ -1,9 +1,10 @@ /* - * intel_sst_v1_control.c - Intel SST Driver for audio engine + * intel_sst_v3_control.c - Intel SST Driver for audio engine * * Copyright (C) 2008-09 Intel Corp * Authors: Vinod Koul <vinod.koul@intel.com> * Harsha Priya <priya.harsha@intel.com> + * Meena Kannan <kurinji.meena.kannan@intel.com> * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This program is free software; you can redistribute it and/or modify @@ -21,7 +22,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * - * This file contains the control operations of vendor 2 + * This file contains the control operations of vendor 3 */ #include <linux/cdev.h> @@ -45,6 +46,7 @@ #include "intel_sst_fw_ipc.h" #include "intel_sst_common.h" #include "intelmid_snd_control.h" +#include <linux/gpio.h> enum _reg_v2 { @@ -59,6 +61,7 @@ enum _reg_v2 { HP_OP_SINGLE_ENDED = 0x224, ENABLE_OPDEV_CTRL = 0x226, ENABLE_DEV_AND_USE_XTAL = 0x227, + MX_JACK_DETECT = 0x225, /* Max audio subsystem (PQ49) MAX 8921 */ AS_IP_MODE_CTL = 0xF9, @@ -73,117 +76,525 @@ enum _reg_v2 { VOL_CTRL_LT = 0x21c, VOL_CTRL_RT = 0x21d, + MAX_GPOCTL = 0xF4, + }; + +enum {FALSE=0, TRUE}; + +#define KOSKI_VOICE_CODEC_ENABLE 46 +/* + * mx_print_regs + * + */ +void mx_print_regs(void) +{ + struct sc_reg_access sc_access[1] = {{0,},}; + + int i = 0; + int retval = 0; + + pr_debug("Printing out MX Audio registers:\n"); + pr_debug("REG VALUE\n"); + + sc_access[0].reg_addr = MAX_GPOCTL; + retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); + pr_debug("0x%x 0x%x\n", sc_access[0].reg_addr, sc_access[0].value); + + for( i = 0xf9; i <= 0xff; i++ ) + { + sc_access[0].reg_addr = i; + retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); + pr_debug("0x%x 0x%x\n", sc_access[0].reg_addr, + sc_access[0].value); + } + + for( i = 0x200; i <= 0x227; i++ ) + { + sc_access[0].reg_addr = i; + retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); + pr_debug("0x%x 0x%x\n", sc_access[0].reg_addr, + sc_access[0].value); + } + +} + + +/* + * mx_koski_get_output_dev + * + */ +int mx_koski_get_output_dev(void) +{ + struct sc_reg_access sc_access[1] = {{0,},}; + int retval = 0, temp_value; + + sc_access[0].reg_addr = AS_CONFIG; + retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); + if (0 != retval) { + /* pmic communication fails */ + pr_debug("pmic commn failed \n"); + return retval; + } + temp_value = sc_access[0].value; + + if (temp_value & 0x1) + { + pr_debug("output device RECEIVER\n"); + } + else if ((temp_value & 0x4) || (temp_value & 0x8)) + { + pr_debug("output device HEADSET\n"); + } + else if ((temp_value & 0x10) || (temp_value & 0x20)) + { + pr_debug("output device SPEAKERS\n"); + } + else + { + pr_debug("output device NOT CONFIGURED!!!\n"); + } + + return temp_value; + +} + + +/* + * mx_change_gpoctl_reg + * + * Pull down GPO pins as they interfere I2S + */ +void mx_change_gpoctl_reg(bool set) +{ + struct sc_reg_access sc_access[1] = {{0,},}; + int retval = 0; + + return; + + sc_access[0].reg_addr = MAX_GPOCTL; + retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); + + pr_debug("0x%x 0x%x\n", sc_access[0].reg_addr, sc_access[0].value); + + if(set) + { + /* Pull up GPO5 and GPO6*/ + sc_access[0].value = sc_access[0].value | 0x60; + retval = sst_sc_reg_access(sc_access, PMIC_WRITE, 1); + } + else + { + /* Pull down GPO5 and GPO6*/ + sc_access[0].value = sc_access[0].value & 0x9F; + retval = sst_sc_reg_access(sc_access, PMIC_WRITE, 1); + } + + if (0 != retval) { + pr_debug("pmic communication failed \n"); + } +} + +/* + * mx_koski_write_reg + * + */ +void mx_koski_write_reg(void) +{ + int retval = 0; + struct sc_reg_access sc_access[1] = {{0,},}; + int fd; + char buf[1]; + char address[10] = ""; + char value[10] = ""; + int i = 0; + bool address_full = FALSE; + long i_address = 0; + long i_value = 0; + + mm_segment_t old_fs = get_fs(); + set_fs(KERNEL_DS); + + fd = sys_open("/root/testi.txt", O_RDONLY, 0); + if (fd >= 0) + { + printk(KERN_DEBUG); + while (sys_read(fd, buf, 1) == 1) + { + printk("%c", buf[0]); + if( buf[0] != ' ') + { + if (!address_full) + { + address[i] = buf[0]; + } + else + { + value[i] = buf[0]; + } + i++; + if( i > 9 ) + { + pr_debug("FILE READ FAILED, char count exceeded!!!\n"); + break; + } + } + else + { + pr_debug("Space found!\n"); + if (!address_full) + { + pr_debug("Address found!\n"); + address[i] = 0; + address_full = TRUE; + strict_strtol ( address, 16, &i_address); + pr_debug("Address found = %s %ld\n", address, i_address); + + } + else + { + value[i] = 0; + strict_strtol ( value, 16, &i_value); + pr_debug("Value found = %s %ld\n", value, i_value); + } + i=0; + } + } + + printk("\n"); + sys_close(fd); + + } + + set_fs(old_fs); + + pr_debug("SET REG called... address = %lx value = %lx\n", + i_address, i_value); + sc_access[0].reg_addr = (int)i_address; + sc_access[0].value = (int)i_value; + retval = sst_sc_reg_access(sc_access, PMIC_WRITE, 1); + + if (0 != retval) + pr_debug("pmic communication failed \n"); + pr_debug("******** \n"); + mx_print_regs(); + pr_debug("******** \n"); +} + +/* + * mx_init_card + * + */ int mx_init_card(void) { - struct sc_reg_access sc_access[] = { + int retval = 0; + + int err=0; + /* Default initialization is for voice call. + Codec I2S0 is in slave mode. */ + struct sc_reg_access sc_access[] = { {0x200, 0x80, 0x00}, {0x201, 0xC0, 0x00}, {0x202, 0x00, 0x00}, {0x203, 0x00, 0x00}, - {0x204, 0x02, 0x00}, - {0x205, 0x10, 0x00}, - {0x206, 0x60, 0x00}, - {0x207, 0x00, 0x00}, - {0x208, 0x90, 0x00}, - {0x209, 0x51, 0x00}, + {0x204, 0x0e, 0x00}, /* JAck detection: 0c -> 0e */ + {0x205, 0x20, 0x00}, /* 0x10 -> 0x20 Koski scale external clock */ + {0x206, 0x8f, 0x00}, /* PLL, 8kHz DAI1*/ + {0x207, 0x21, 0x00}, /* 8kHz DAI1 */ + {0x208, 0x18, 0x00}, /* DAI1 slave: DLY1 and HIZOFF1 enabled */ + {0x209, 0x32, 0x00}, {0x20a, 0x00, 0x00}, - {0x20b, 0x10, 0x00}, - {0x20c, 0x00, 0x00}, - {0x20d, 0x00, 0x00}, - {0x20e, 0x11, 0x00}, + {0x20b, 0x5A, 0x00}, /* 0x10 -> 0x5A Koski */ + {0x20c, 0xBE, 0x00}, /* 0x00 -> 0xBE Koski */ + {0x20d, 0x00, 0x00}, /* DAI2 'off' */ + {0x20e, 0x40, 0x00}, {0x20f, 0x00, 0x00}, {0x210, 0x84, 0x00}, - {0x211, 0x33, 0x00}, + {0x211, 0x33, 0x00}, /* Voice filter */ {0x212, 0x00, 0x00}, {0x213, 0x00, 0x00}, {0x214, 0x41, 0x00}, {0x215, 0x00, 0x00}, {0x216, 0x00, 0x00}, - {0x217, 0x00, 0x00}, - {0x218, 0x03, 0x00}, - {0x219, 0x03, 0x00}, - {0x21a, 0x00, 0x00}, - {0x21b, 0x00, 0x00}, + {0x217, 0x20, 0x00}, + {0x218, 0x00, 0x00}, + {0x219, 0x00, 0x00}, + {0x21a, 0x40, 0x00}, + {0x21b, 0x40, 0x00}, {0x21c, 0x09, 0x00}, {0x21d, 0x09, 0x00}, {0x21e, 0x00, 0x00}, {0x21f, 0x00, 0x00}, - {0x220, 0x54, 0x00}, - {0x221, 0x54, 0x00}, - {0x222, 0x50, 0x00}, - {0x223, 0x00, 0x00}, - {0x224, 0x04, 0x00}, - {0x225, 0x80, 0x00}, - {0x226, 0x0F, 0x00}, - {0x227, 0x88, 0x00}, + {0x220, 0x00, 0x00}, /* Microphone configurations */ + {0x221, 0x00, 0x00}, /* Microphone configurations */ + {0x222, 0x50, 0x00}, /* Microphone configurations */ + {0x223, 0x21, 0x00}, /* Jack detection: 20 -> 21 */ + {0x224, 0x00, 0x00}, + {0x225, 0x80, 0x00}, /* Jack detection: 00 -> 80 */ + {0x226, 0x3C, 0x00}, /* 0x0F -> 0x3C Internal speaker as default */ + {0x227, 0x00, 0x00}, /* 0x88 -> 0x80; Koski select external clock */ {0xf9, 0x40, 0x00}, - {0xfa, 0x1F, 0x00}, - {0xfb, 0x00, 0x00}, - {0xfc, 0x00, 0x00}, - {0xfd, 0x00, 0x00}, - {0xfe, 0x00, 0x00}, - {0xff, 0x00, 0x00}, + {0xfa, 0x19, 0x00}, /* 0x1F -> 0x19 Koski Left speaker volume */ + {0xfb, 0x19, 0x00}, /* 0x00 -> 0x19 Koski Right speaker volume */ + {0xfc, 0x12, 0x00}, + {0xfd, 0x12, 0x00}, + {0xfe, 0x00, 0x00}, /* 0x00 -> Koski output mixer for stereo */ + {0xff, 0xB0, 0x00}, /* 0x00 -> 0x81 Koski enable receiver */ }; + snd_pmic_ops_mx.card_status = SND_CARD_INIT_DONE; - return sst_sc_reg_access(sc_access, PMIC_WRITE, 47); + retval = sst_sc_reg_access(sc_access, PMIC_WRITE, 47); + + mx_change_gpoctl_reg(TRUE); + mx_change_gpoctl_reg(FALSE); + + /*init clock sig to voice codec*/ + err = gpio_request(KOSKI_VOICE_CODEC_ENABLE, "sound_voice_codec"); + + if (err) + { + printk("Error enabling voice codec clock\n"); + } + else + { + gpio_direction_output(KOSKI_VOICE_CODEC_ENABLE, 1); + printk("Voice codec clock enabled\n"); + } + + mx_print_regs(); + + return retval; } -int mx_power_up_pb(void) +/* + * mx_init_capture_card + * + */ +int mx_init_capture_card(void) { + /* Sample rate set to 8000Hz */ struct sc_reg_access sc_access[] = { - {0x226, 0x0C, MASK3|MASK2}, - {0x227, 0x88, MASK7|MASK3}, + {0x200, 0xC0, 0x0}, + {0x201, 0xC0, 0x0}, + {0x202, 0x00, 0x0}, + {0x203, 0x00, 0x0}, + {0x204, 0x0e, 0x0}, /* changed from 0x0E*//*jack interrupt enabled*/ + {0x205, 0x20, 0x0}, /* changed from 0x10*/ + {0x206, 0x8f, 0x0}, /* changed from 0x00*/ + {0x207, 0x21, 0x0}, /* changed from 0x00*/ + {0x208, 0x90, 0x0}, /* changed from 0x00*/ + {0x209, 0x32, 0x0}, /* changed from 0x00*/ + {0x20a, 0x00, 0x0}, + {0x20b, 0x8f, 0x0}, /* changed from 0x58*/ + {0x20c, 0x21, 0x0}, /* changed from 0x33*/ + {0x20d, 0x90, 0x0}, + {0x20e, 0x22, 0x0}, /* changed from 0x52*/ + {0x20f, 0x00, 0x0}, + {0x210, 0x84, 0x0}, /* changed from 0x21*/ + {0x211, 0x00, 0x0}, /* changed from 0x80*/ + {0x212, 0x00, 0x0}, + {0x213, 0x00, 0x0}, + {0x214, 0x41, 0x0}, + {0x215, 0x81, 0x0}, /* changed from 0x00*/ + {0x216, 0x00, 0x0}, + {0x217, 0x00, 0x0}, + {0x218, 0x00, 0x0}, + {0x219, 0x00, 0x0}, + {0x21a, 0x00, 0x0}, + {0x21b, 0x00, 0x0}, + {0x21c, 0x09, 0x0}, + {0x21d, 0x09, 0x0}, + {0x21e, 0x00, 0x0}, + {0x21f, 0x00, 0x0}, + {0x220, 0x00, 0x0}, + {0x221, 0x00, 0x0}, + {0x222, 0x00, 0x0}, + {0x223, 0x31, 0x0}, /* changed from 0x00*//*jack interrupt enabled*/ + {0x224, 0x40, 0x0}, + {0x225, 0x80, 0x0}, /*jack interrupt enabled*/ + {0x226, 0xC0, 0x0}, + {0x227, 0x80, 0x0}, /* changed from 0x88*/ + {0xf9, 0x40, 0x0}, + {0xfa, 0x1F, 0x0}, + {0xfb, 0x1F, 0x0}, /* changed from 0x00*/ + {0xfc, 0x00, 0x0}, + {0xfd, 0x00, 0x0}, + {0xfe, 0x00, 0x0}, + {0xff, 0xa0, 0x0}, }; + int retval = 0; - if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) - retval = mx_init_card(); - if (retval) + pr_debug("mx_init_capture_card called...\n"); + + retval = sst_sc_reg_access(sc_access, PMIC_WRITE, 47); + if (0 != retval) { + /* pmic communication fails */ + pr_debug("pmic commn failed \n"); return retval; - return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); + } + + mx_change_gpoctl_reg(TRUE); + mx_print_regs(); + + pr_debug("Capture configuration complete!!\n"); + return 0; } -int mx_power_up_cp(void) +/* + * mx_init_playback_card + * + */ +int mx_init_playback_card(int sel_output) { + + /* Default settings for playback from Langwell I2S1. */ + /* Output is routed to stereo speakers. */ + struct sc_reg_access sc_access[] = { - {0x226, 0x03, MASK1|MASK0}, - {0x227, 0x88, MASK7|MASK3}, + {0x200, 0x00, 0x0}, + {0x201, 0xC0, 0x0}, + {0x202, 0x00, 0x0}, + {0x203, 0x00, 0x0}, + {0x204, 0x0e, 0x0}, + {0x205, 0x20, 0x0}, + {0x206, 0x00, 0x0}, + {0x207, 0x00, 0x0}, + {0x208, 0x00, 0x0}, + {0x209, 0x51, 0x0}, + {0x20a, 0x00, 0x0}, + {0x20b, 0x5a, 0x0}, + {0x20c, 0xbe, 0x0}, + {0x20d, 0x90, 0x0}, + {0x20e, 0x51, 0x0}, + {0x20f, 0x00, 0x0}, + {0x210, 0x21, 0x0}, + {0x211, 0x00, 0x0}, + {0x212, 0x00, 0x0}, + {0x213, 0x00, 0x0}, + {0x214, 0x41, 0x0}, + {0x215, 0x81, 0x0}, + {0x216, 0x00, 0x0}, + {0x217, 0x00, 0x0}, + {0x218, 0x00, 0x0}, + {0x219, 0x00, 0x0}, + {0x21a, 0x00, 0x0}, + {0x21b, 0x00, 0x0}, + //{0x21c, 0x09, 0x0}, + //{0x21d, 0x09, 0x0}, + {0x21e, 0x00, 0x0}, + {0x21f, 0x00, 0x0}, + {0x220, 0x00, 0x0}, + {0x221, 0x00, 0x0}, + {0x222, 0x00, 0x0}, + {0x223, 0x01, 0x0}, /* Jack detection: 00 -> 01 */ + {0x224, 0x40, 0x0}, + {0x225, 0x80, 0x0}, /* JAck detection: 00 -> 80 */ + {0x226, 0x3c, 0x0}, + {0x227, 0x80, 0x0}, + {0xf9, 0x40, 0x0}, + {0xfa, 0x19, 0x0}, + {0xfb, 0x19, 0x0}, + //{0xfc, 0x00, 0x0}, + //{0xfd, 0x00, 0x0}, + {0xfe, 0x00, 0x0}, /* 0x60 -> 0x00 Output mixer for stereo */ + {0xff, sel_output, 0x0}, /* Removed sel_output */ }; + +#ifdef KOSKI_JACK_DETECTION + struct sc_reg_access sc_access_hs_pb[] = { + {0xff, 0x8c, 0x0}, + {0xfc, 0x10, 0x0}, + {0xfd, 0x10, 0x0}, + }; +#endif + int retval = 0; - if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) - retval = mx_init_card(); - if (retval) + printk("mx_init_playback_card called...\n"); + + + retval = sst_sc_reg_access(sc_access, PMIC_WRITE, sizeof(sc_access)/4); + if (0 != retval) { + /* pmic communication fails */ + pr_debug("pmic commn failed \n"); return retval; + } + +#ifdef KOSKI_JACK_DETECTION + /* Check if Jack is present */ + if (koski_jack) + { + pr_debug("Koski Jack present!\n"); + + retval = sst_sc_reg_access(sc_access_hs_pb, PMIC_WRITE, 3); + if (0 != retval) { + /* pmic communication fails */ + pr_debug("pmic commn failed \n"); + return retval; + } + } +#endif + + mx_print_regs(); - return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); + pr_debug("Playback configuration complete!!\n"); + return 0; } -int mx_power_down_pb(void) +/* + * mx_power_up_pb + * + */ +int mx_power_up_pb(void) { -/* struct sc_reg_access sc_access[] = { - {0x226, 0x00, MASK3|MASK2}, + +#if 0 + struct sc_reg_access sc_access[] = { + {0x226, 0x3C, MASK3|MASK2}, /* 0x0F -> 0x3C Koski enable internal speaker as default */ + {0x227, 0x80, MASK7|MASK3}, /* 0x88 -> 0x80; Koski select external clock */ }; +#endif + + int temp_val = 0; int retval = 0; + printk("mx_power_up_pb\n"); + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) retval = mx_init_card(); if (retval) return retval; - return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); - Turning Off this code, as power off coausing first few frames - to be cutt off... */ + temp_val = mx_koski_get_output_dev(); + pr_debug("Selected output temp_val = 0x%x\n", temp_val); + + mx_init_playback_card(temp_val); + + /* Set selected output_device */ + temp_val = 0; + temp_val = mx_koski_get_output_dev(); + pr_debug("Selected output for Playback: temp_val = 0x%x\n", temp_val); + return 0; + } -int mx_power_down_cp(void) + +/* + * mx_power_up_cp + * + */ +int mx_power_up_cp(void) { -/* struct sc_reg_access sc_access[] = { - {0x226, 0x00, MASK1|MASK0}, +#if 0 + struct sc_reg_access sc_access[] = { + {0x226, 0x03, MASK1|MASK0}, + {0x227, 0x80, MASK7|MASK3}, /* 0x88 -> 0x80; Koski select external clock */ }; +#endif + int retval = 0; if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) @@ -191,39 +602,103 @@ int mx_power_down_cp(void) if (retval) return retval; - return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); -*/ + mx_init_capture_card(); + return 0; + } -int mx_power_down(void) + +/* + * mx_power_down_pb + * + */ +int mx_power_down_pb(void) { -/* struct sc_reg_access sc_access[] = { - {0x227, 0x00, MASK7}, - }; - int retval = 0; + int retval = 0; - if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) - retval = mx_init_card(); - if (retval) + struct sc_reg_access sc_access[1] = {{0,},}; + + sc_access[0].reg_addr = 0x227; + sc_access[0].value = 0x00; + + + retval = sst_sc_reg_access(sc_access, PMIC_WRITE, 1); + if (0 != retval) { + /* pmic communication fails */ + printk("pmic commn failed \n"); return retval; + } - return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); -*/ return 0; + return 0; } + +/* + * mx_power_down_cp + * + */ +int mx_power_down_cp(void) +{ + return 0; +} + + +/* + * mx_power_down + * + */ +int mx_power_down(void) +{ + return 0; +} + + +/* + * mx_set_voice_port + * + */ int mx_set_voice_port(int status) { int retval = 0; + struct sc_reg_access sc_access[1] = {{0,},}; + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) retval = mx_init_card(); if (retval) return retval; + if (status == ACTIVATE) + { + pr_debug("Voice port... ACTIVATE\n"); + sc_access[0].reg_addr = 0x227; + sc_access[0].value = 0x80; + } + else if (status == DEACTIVATE) + { + pr_debug("Voice port... DEACTIVATE\n"); + sc_access[0].reg_addr = 0x227; + sc_access[0].value = 0x00; + } + + retval = sst_sc_reg_access(sc_access, PMIC_WRITE, 1); + if (0 != retval) { + /* pmic communication fails */ + pr_debug("pmic commn failed \n"); + return retval; + } + + mx_print_regs(); + return retval; } + +/* + * mx_set_audio_port + * + */ int mx_set_audio_port(int status) { int retval = 0; @@ -232,89 +707,220 @@ int mx_set_audio_port(int status) retval = mx_init_card(); if (retval) return retval; + + if (status == ACTIVATE) + { + pr_debug("Activating audio port...\n"); + } + else if (status == DEACTIVATE) + { + pr_debug("De-activating audio port...\n"); + } + + /* Configurations for playback */ + mx_init_playback_card(0xb0); + + pr_debug("Audio port configuration complete!!\n"); + return retval; } + + +/* + * mx_set_pcm_voice_params + * + */ int mx_set_pcm_voice_params(void) { int retval = 0; + struct sc_reg_access sc_access[] = { + {0x200, 0x80, 0x00}, + {0x201, 0xC0, 0x00}, + {0x202, 0x00, 0x00}, + {0x203, 0x00, 0x00}, + {0x204, 0x0c, 0x00}, + {0x205, 0x20, 0x00}, /* 0x10 -> 0x20 Koski scale external clock */ + {0x206, 0x8f, 0x00}, /* PLL, 8kHz DAI1*/ + {0x207, 0x21, 0x00}, /* 8kHz DAI1 */ + {0x208, 0x18, 0x00}, /* DAI1 slave: DLY1 and HIZOFF1 enabled */ + {0x209, 0x32, 0x00}, + {0x20a, 0x00, 0x00}, + {0x20b, 0x5A, 0x00}, /* 0x10 -> 0x5A Koski */ + {0x20c, 0xBE, 0x00}, /* 0x00 -> 0xBE Koski */ + {0x20d, 0x00, 0x00}, /* DAI2 'off' */ + {0x20e, 0x40, 0x00}, + {0x20f, 0x00, 0x00}, + {0x210, 0x84, 0x00}, + {0x211, 0x33, 0x00}, /* Voice filter */ + {0x212, 0x00, 0x00}, + {0x213, 0x00, 0x00}, + {0x214, 0x41, 0x00}, + {0x215, 0x00, 0x00}, + {0x216, 0x00, 0x00}, + {0x217, 0x20, 0x00}, + {0x218, 0x00, 0x00}, + {0x219, 0x00, 0x00}, + {0x21a, 0x40, 0x00}, + {0x21b, 0x40, 0x00}, + {0x21c, 0x09, 0x00}, + {0x21d, 0x09, 0x00}, + {0x21e, 0x00, 0x00}, + {0x21f, 0x00, 0x00}, + {0x220, 0x00, 0x00}, /* Microphone configurations */ + {0x221, 0x00, 0x00}, /* Microphone configurations */ + {0x222, 0x50, 0x00}, /* Microphone configurations */ + {0x223, 0x20, 0x00}, /* Microphone configurations */ + {0x224, 0x00, 0x00}, + {0x225, 0x00, 0x00}, + {0x226, 0x3C, 0x00}, /* 0x0F -> 0x3C Koski enable internal speaker as default */ + {0x227, 0x00, 0x00}, /* 0x88 -> 0x80; Koski select external clock */ + {0xf9, 0x40, 0x00}, + {0xfa, 0x19, 0x00}, /* 0x1F -> 0x19 Koski Left speaker volume */ + {0xfb, 0x19, 0x00}, /* 0x00 -> 0x19 Koski Right speaker volume */ + {0xfc, 0x12, 0x00}, + {0xfd, 0x12, 0x00}, + {0xfe, 0x00, 0x00}, /* 0x00 -> Koski output mixer for stereo */ + {0xff, 0x81, 0x00}, /* 0x00 -> 0x81 Koski enable receiver */ + }; + + pr_debug("mx_set_pcm_voice_params called...\n"); + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) retval = mx_init_card(); if (retval) return retval; + retval = sst_sc_reg_access(sc_access, PMIC_WRITE, 47); + if (0 != retval) { + /* pmic communication fails */ + pr_debug("pmic commn failed \n"); + return retval; + } + + mx_print_regs(); + + pr_debug("Voice parameters configuration complete!!\n"); + return retval; } +/* + * mx_set_pcm_audio_params + * + */ int mx_set_pcm_audio_params(int sfreq, int word_size) { int config1 = 0, config2 = 0; - struct sc_reg_access sc_access[3]; int retval = 0; + struct sc_reg_access sc_access[4] = {{0,},}; + + pr_debug("called...\n"); if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) retval = mx_init_card(); if (retval) return retval; + if(sfreq == 999999) + { + mx_koski_write_reg(); + } + else + { + pr_debug("called... sfreq = %d word_size = %d\n", sfreq, word_size); + + switch (sfreq) { + case 8000: + config1 = 0x20; + config2 = 0x0f; + break; + case 11025: + config1 = 0x14; + config2 = 0xd8; + break; + case 12000: + config1 = 0x16; + config2 = 0xaf; + break; + case 16000: + config1 = 0x1e; + config2 = 0x3f; + break; + case 22050: + config1 = 0x29; + config2 = 0xaf; + break; + case 24000: + config1 = 0x2d; + config2 = 0x5f; + break; + case 32000: + config1 = 0x3c; + config2 = 0x7f; + break; + case 44100: + config1 = 0x53; + config2 = 0x5f; + break; + case 48000: + /* for Koski Capture */ + config1 = 0x5a; + config2 = 0xbe; + break; + } - switch (sfreq) { - case 8000: - config1 = 0x10; - config2 = 0x00; - break; - case 11025: - config1 = 0x16; - config2 = 0x0d; - break; - case 12000: - config1 = 0x18; - config2 = 0x00; - break; - case 16000: - config1 = 0x20; - config2 = 0x00; - break; - case 22050: - config1 = 0x2c; - config2 = 0x1a; - break; - case 24000: - config1 = 0x30; - config2 = 0x00; - break; - case 32000: - config1 = 0x40; - config2 = 0x00; - break; - case 44100: - config1 = 0x58; - config2 = 0x33; - break; - case 48000: - config1 = 0x60; - config2 = 0x00; - break; - } + sc_access[0].reg_addr = SET_MASTER_AND_LR_CLK1; + sc_access[0].value = config1; + sc_access[1].reg_addr = SET_MASTER_AND_LR_CLK2; + sc_access[1].value = config2; + + pr_debug(" MASTER CLK 1 config1 = %x\n", config1); + pr_debug(" MASTER CLK 2 config2 = %x\n", config2); + + switch (word_size) { + case 16: + /* for Koski Capture */ + config1 = 0x22; + config2 = 0x32; + break; + case 24: + config1 = 0x52; + config2 = 0x32; + break; + } - sc_access[0].reg_addr = 0x206; - sc_access[0].value = config1; - sc_access[1].reg_addr = 0x207; - sc_access[1].value = config2; + pr_debug("config1 = %x\n", config1); + pr_debug("config2 = %x\n", config2); - if (word_size == 16) - sc_access[2].value = 0x31; - else if (word_size == 24) - sc_access[2].value = 0x32; + sc_access[2].reg_addr = DIGITAL_INTERFACE_TO_DAI2; + sc_access[2].value = config1; + sc_access[3].reg_addr = CLK_AND_FS2; + sc_access[3].value = config2; + + retval = sst_sc_reg_access(sc_access, PMIC_WRITE, 4); + if (0 != retval) + pr_debug("pmic communication failed \n"); + + }/* if else */ + + pr_debug("******** \n"); + mx_print_regs(); + pr_debug("******** \n"); + + return retval; - sc_access[2].reg_addr = 0x209; - return sst_sc_reg_access(sc_access, PMIC_WRITE, 3); } -int mx_set_selected_output_dev(u8 dev_id) + +/* + * mx_set_selected_output_dev + * + */ +int mx_set_selected_output_dev(int dev_id) { - struct sc_reg_access sc_access[2]; - int num_reg; + struct sc_reg_access sc_access[4]; + int num_reg = 0; int retval = 0; if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) @@ -323,34 +929,82 @@ int mx_set_selected_output_dev(u8 dev_id return retval; pr_debug("mx_set_selected_output_dev dev_id:0x%x\n", dev_id); + switch (dev_id) { case STEREO_HEADPHONE: + pr_debug("STEREO_HEADPHONE selected\n"); + sc_access[0].reg_addr = 0x226; - sc_access[0].value = 0x0C; - sc_access[0].mask = 0x3F; + sc_access[0].value = 0x3C; + sc_access[0].mask = 0x3C; sc_access[1].reg_addr = 0xFF; - sc_access[1].value = 0x00; - sc_access[1].mask = 0xA0; - num_reg = 2; + sc_access[1].value = 0x8C; + sc_access[1].mask = 0xff; + + /* Enable stereo headphones - Begin */ + /* Volume Left Un-Mute */ + sc_access[2].reg_addr = 0xFD; + sc_access[2].value = 0x1A; + sc_access[2].mask = 0xff; + + /* Volume right Un-Mute */ + sc_access[3].reg_addr = 0xFC; + sc_access[3].value = 0x1A; + sc_access[3].mask = 0xff; + + num_reg = 4; break; case INTERNAL_SPKR: + pr_debug("INTERNAL_SPKR selected\n"); + sc_access[0].reg_addr = 0x226; - sc_access[0].value = 0x3F; - sc_access[0].mask = 0x3F; + sc_access[0].value = 0x3C; + sc_access[0].mask = 0xff; sc_access[1].reg_addr = 0xFF; - sc_access[1].value = 0xA0; - sc_access[1].mask = 0xA0; - num_reg = 2; + sc_access[1].value = 0xB0; + sc_access[1].mask = 0xff; + + sc_access[2].reg_addr = 0xFD; + sc_access[2].value = 0x00; + sc_access[2].mask = 0xff; + + sc_access[3].reg_addr = 0xFC; + sc_access[3].value = 0x00; + sc_access[3].mask = 0xff; + + num_reg = 4; + break; + case RECEIVER: + pr_debug("RECEIVER Koski selected\n"); + + /* configuration - AS enable, receiver enable */ + sc_access[0].reg_addr = 0xFF; + sc_access[0].value = 0x81; + sc_access[0].mask = 0xff; + + /* Line output (L&R), DAC output (L&R) enable */ + sc_access[1].reg_addr = 0x226; + sc_access[1].value = 0x3C; + sc_access[1].mask = 0xff; + num_reg = 2; break; } + + mx_print_regs(); + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg); } -int mx_set_selected_input_dev(u8 dev_id) + +/* + * mx_set_selected_input_dev + * + */ +int mx_set_selected_input_dev(int dev_id) { - struct sc_reg_access sc_access[2]; + struct sc_reg_access sc_access[13]; int num_reg = 0; int retval = 0; @@ -363,29 +1017,96 @@ int mx_set_selected_input_dev(u8 dev_id) switch (dev_id) { case AMIC: - sc_access[0].reg_addr = 0x223; - sc_access[0].value = 0x00; - sc_access[0].mask = 0x00; - sst_sc_reg_access(sc_access, PMIC_WRITE, 1); + pr_debug("Not supported in mono\n"); return 0; case HS_MIC: - sc_access[0].reg_addr = 0x223; - sc_access[0].value = 0x00; - sc_access[0].mask = 0x00; - num_reg = 1; + pr_debug("HS_MIC selected \n"); + + /* configuration - AS enable, receiver enable */ + sc_access[0].reg_addr = 0xFF; + sc_access[0].value = 0x8c; + sc_access[0].mask = 0x8c; + + sc_access[1].reg_addr = 0x226; + sc_access[1].value = 0x3C; + sc_access[1].mask = 0x3C; + num_reg = 2; + break; case DMIC: - sc_access[0].reg_addr = 0x223; - sc_access[0].value = 0x30; - sc_access[0].mask = 0x30; - num_reg = 1; + pr_debug("DMIC selected \n"); + + /* configuration - AS enable, receiver enable */ + sc_access[0].reg_addr = 0xFF; + sc_access[0].value = 0x8C; + sc_access[0].mask = 0x8C; + + /* Line output (L&R), DAC output (L&R) enable */ + sc_access[1].reg_addr = 0x226; + sc_access[1].value = 0x3C; + sc_access[1].mask = 0x3C; + + sc_access[2].reg_addr = 0x206; + sc_access[2].value = 0x5a; + sc_access[2].mask = 0x5a; + + sc_access[3].reg_addr = 0x207; + sc_access[3].value = 0xbe; + sc_access[3].mask = 0xbe; + + sc_access[4].reg_addr = 0x208; + sc_access[4].value = 0x90; + sc_access[4].mask = 0x90; + + sc_access[5].reg_addr = 0x209; + sc_access[5].value = 0x32; + sc_access[5].mask = 0x32; + + sc_access[6].reg_addr = 0x20b; + sc_access[6].value = 0x00; + sc_access[6].mask = 0x00; + + sc_access[7].reg_addr = 0x20d; + sc_access[7].value = 0x90; + sc_access[7].mask = 0x90; + + sc_access[8].reg_addr = 0x20e; + sc_access[8].value = 0x22; + sc_access[8].mask = 0x22; + + sc_access[9].reg_addr = 0x210; + sc_access[9].value = 0x84; + sc_access[9].mask = 0x84; + + sc_access[10].reg_addr = 0x215; + sc_access[10].value = 0x81; + sc_access[10].mask = 0x81; + + sc_access[11].reg_addr = 0x223; + sc_access[11].value = 0x30; + sc_access[11].mask = 0x30; + num_reg = 12; + break; + case LINE_IN: + pr_debug("LINE_IN selected \n"); + + /* Line output (L&R), DAC output (L&R) enable */ + sc_access[0].reg_addr = 0x226; + sc_access[0].value = 0xFC; + sc_access[0].mask = 0xFC; + num_reg = 1; break; } return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg); } -int mx_set_mute(int dev_id, u8 value) + +/* + * mx_set_mute + * + */ +int mx_set_mute(int dev_id, int value) { struct sc_reg_access sc_access[6]; int num_reg = 0; @@ -404,19 +1125,16 @@ int mx_set_mute(int dev_id, u8 value) case PMIC_SND_HP_MIC_MUTE: sc_access[0].reg_addr = 0x220; sc_access[1].reg_addr = 0x221; - sc_access[2].reg_addr = 0x223; if (value == MUTE) { sc_access[0].value = 0x00; sc_access[1].value = 0x00; - sc_access[2].value = 0x10; } else { - sc_access[0].value = 0x20; - sc_access[1].value = 0x20; + sc_access[0].value = 0x40; + sc_access[1].value = 0x40; } sc_access[0].mask = MASK5|MASK6; sc_access[1].mask = MASK5|MASK6; - sc_access[2].mask = MASK5|MASK4; - num_reg = 3; + num_reg = 2; break; case PMIC_SND_LEFT_SPEAKER_MUTE: @@ -439,7 +1157,15 @@ int mx_set_mute(int dev_id, u8 value) sc_access[0].mask = MASK6; num_reg = 1; break; - + case PMIC_SND_RECEIVER_MUTE: + sc_access[0].reg_addr = VOL_CTRL_RT; + if (value == MUTE) + sc_access[0].value = 0x40; + else + sc_access[0].value = 0x00; + sc_access[0].mask = MASK6; + num_reg = 1; + break; case PMIC_SND_MUTE_ALL: sc_access[0].reg_addr = VOL_CTRL_RT; sc_access[1].reg_addr = VOL_CTRL_LT; @@ -451,8 +1177,8 @@ int mx_set_mute(int dev_id, u8 value) sc_access[3].value = 0x00; } else { sc_access[0].value = sc_access[1].value = 0x00; - sc_access[2].value = 0x20; - sc_access[3].value = 0x20; + sc_access[2].value = 0x40; + sc_access[3].value = 0x40; } sc_access[0].mask = sc_access[1].mask = MASK6; sc_access[2].mask = MASK5|MASK6; @@ -464,7 +1190,12 @@ int mx_set_mute(int dev_id, u8 value) return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg); } -int mx_set_vol(int dev_id, u8 value) + +/* + * mx_set_vol + * + */ +int mx_set_vol(int dev_id, int value) { struct sc_reg_access sc_access[2] = {{0},}; int num_reg = 0; @@ -477,10 +1208,13 @@ int mx_set_vol(int dev_id, u8 value) pr_debug("mx_set_vol dev_id:0x%x , value:%d \n", dev_id, value); switch (dev_id) { + case PMIC_SND_RECEIVER_VOL: + return 0; + break; case PMIC_SND_CAPTURE_VOL: sc_access[0].reg_addr = 0x220; sc_access[1].reg_addr = 0x221; - sc_access[0].value = sc_access[1].value = -value; + sc_access[0].value = sc_access[1].value = (-value) | 0x40; sc_access[0].mask = sc_access[1].mask = (MASK0|MASK1|MASK2|MASK3|MASK4); num_reg = 2; @@ -501,7 +1235,12 @@ int mx_set_vol(int dev_id, u8 value) return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg); } -int mx_get_mute(int dev_id, u8 *value) + +/* + *mx_get_mute + * + */ +int mx_get_mute(int dev_id, int *value) { struct sc_reg_access sc_access[4] = {{0},}; int retval = 0, num_reg = 0, mask = 0; @@ -527,7 +1266,6 @@ int mx_get_mute(int dev_id, u8 *value) else *value = MUTE; return retval; - case PMIC_SND_LEFT_HP_MUTE: case PMIC_SND_LEFT_SPEAKER_MUTE: sc_access[0].reg_addr = VOL_CTRL_LT; @@ -567,7 +1305,11 @@ int mx_get_mute(int dev_id, u8 *value) return retval; } -int mx_get_vol(int dev_id, u8 *value) +/* + * mx_get_vol + * + */ +int mx_get_vol(int dev_id, int *value) { struct sc_reg_access sc_access = {0,}; int retval = 0, mask = 0, num_reg = 0; @@ -620,4 +1362,3 @@ struct snd_pmic_ops snd_pmic_ops_mx = { .power_down_pmic_cp = mx_power_down_cp, .power_down_pmic = mx_power_down, }; - Index: linux-2.6.33/sound/pci/sst/koski_audio_conf.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/koski_audio_conf.c @@ -0,0 +1,431 @@ +/* + * koski_audio_conf.c - koski audio configure part for audio engine + * + * Copyright (C) 2009 aavamobile + * Authors: Samuli.konttila <samuli.konttila@aavamobile.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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include <linux/cdev.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/fcntl.h> +#include <linux/uaccess.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/workqueue.h> +#include <linux/pci.h> +#include <linux/firmware.h> +#include <sound/intel_lpe.h> +#include <sound/intel_sst_ioctl.h> +#include "intel_sst_fw_ipc.h" +#include "intel_sst_common.h" +#include "intelmid_snd_control.h" +#include "koski_audio_conf.h" + + + +/*Suported interface + + +Voicecall -> receiver +Voicecall -> speaker +Voicecall -> Bluetooth + +Voicecall + mix -> receiver +Voicecall + mix -> speaker +Voicecall + mix -> Bluetooth + + + +*/ +#define TABLE_LEN(reg_table) sizeof(reg_table)/4 + +int koski_voice_call = 0; +extern volatile int koski_jack; + + struct sc_reg_access voice_call_on[] = { + {0x200, 0x80, 0x00}, + {0x201, 0xC0, 0x00}, + {0x202, 0x00, 0x00}, + {0x203, 0x00, 0x00}, + {0x204, 0x0e, 0x00}, /* Jack detection: 0c -> 0e */ + {0x205, 0x20, 0x00}, /* 0x10 -> 0x20 Koski scale external clock */ + {0x206, 0x8f, 0x00}, /* PLL, 8kHz DAI1*/ + {0x207, 0x21, 0x00}, /* 8kHz DAI1 */ + {0x208, 0x18, 0x00}, /* DAI1 slave: DLY1 and HIZOFF1 enabled */ + {0x209, 0x32, 0x00}, + {0x20a, 0x00, 0x00}, + {0x20b, 0x5A, 0x00}, /* 0x10 -> 0x5A Koski */ + {0x20c, 0xBE, 0x00}, /* 0x00 -> 0xBE Koski */ + {0x20d, 0x00, 0x00}, /* DAI2 'off' */ + {0x20e, 0x40, 0x00}, + {0x20f, 0x00, 0x00}, + {0x210, 0x84, 0x00}, + {0x211, 0x33, 0x00}, /* Voice filter */ + {0x212, 0x00, 0x00}, + {0x213, 0x00, 0x00}, + {0x214, 0x41, 0x00}, + {0x215, 0x00, 0x00}, + {0x216, 0x00, 0x00}, + {0x217, 0x20, 0x00}, + {0x218, 0x00, 0x00}, + {0x219, 0x00, 0x00}, + {0x21a, 0x40, 0x00}, + {0x21b, 0x40, 0x00}, + {0x21e, 0x00, 0x00}, + {0x21f, 0x00, 0x00}, + {0x220, 0x00, 0x00}, /* Microphone configurations */ + {0x221, 0x00, 0x00}, /* Microphone configurations */ + {0x222, 0x50, 0x00}, /* Microphone configurations */ + {0x223, 0x21, 0x00}, /* Jack detection: 20 -> 21 */ + {0x224, 0x00, 0x00}, + {0x225, 0x80, 0x00}, /* JAck detection: 00 -> 80 */ + {0x226, 0x3C, 0x00}, /* 0x0F -> 0x3C Koski enable internal speaker as default */ + {0x227, 0x80, 0x00}, /* 0x88 -> 0x80; Koski select external clock */ + {0xf9, 0x40, 0x00}, + {0xfa, 0x19, 0x00}, /* 0x1F -> 0x19 Koski Left speaker volume */ + {0xfb, 0x19, 0x00}, /* 0x00 -> 0x19 Koski Right speaker volume */ + {0xfe, 0x00, 0x00}, /* 0x00 -> Koski output mixer for stereo */ + {0xff, 0x81, 0x00}, /* 0x00 -> 0x81 Koski enable receiver */ + }; + +struct sc_reg_access voice_call_on_hs[] = { + {0x200, 0x80, 0x00}, + {0x201, 0xC0, 0x00}, + {0x202, 0x00, 0x00}, + {0x203, 0x00, 0x00}, + {0x204, 0x0e, 0x00}, + {0x205, 0x20, 0x00}, /* 0x10 -> 0x20 Koski scale external clock */ + {0x206, 0x8f, 0x00}, /* PLL, 8kHz DAI1*/ + {0x207, 0x21, 0x00}, /* 8kHz DAI1 */ + {0x208, 0x58, 0x00}, /* DAI1 slave: DLY1 and HIZOFF1 enabled */ + {0x209, 0x32, 0x00}, + {0x20a, 0x00, 0x00}, + {0x20b, 0x5A, 0x00}, /* 0x10 -> 0x5A Koski */ + {0x20c, 0xBE, 0x00}, /* 0x00 -> 0xBE Koski */ + {0x20d, 0x00, 0x00}, /* DAI2 'off' */ + {0x20e, 0x40, 0x00}, + {0x20f, 0x00, 0x00}, + {0x210, 0x84, 0x00}, + {0x211, 0x33, 0x00}, /* Voice filter */ + {0x212, 0x00, 0x00}, + {0x213, 0x00, 0x00}, + {0x214, 0x41, 0x00}, + {0x215, 0x00, 0x00}, + {0x216, 0x00, 0x00}, + {0x217, 0x20, 0x00}, + {0x218, 0x00, 0x00}, + {0x219, 0x00, 0x00}, + {0x21a, 0x40, 0x00}, + {0x21b, 0x40, 0x00}, + {0x21c, 0x09, 0x00}, + {0x21d, 0x09, 0x00}, + {0x21e, 0x00, 0x00}, + {0x21f, 0x00, 0x00}, + {0x220, 0x00, 0x00}, /* Microphone configurations */ + {0x221, 0x40, 0x00}, /* Microphone configurations */ + {0x222, 0x10, 0x00}, /* Microphone configurations */ + {0x223, 0x01, 0x00}, /* Microphone configurations */ + {0x224, 0x00, 0x00}, + {0x225, 0x80, 0x00}, + {0x226, 0x3f, 0x00}, /* 0x0F -> 0x3C Koski enable internal speaker as default */ + {0x227, 0x80, 0x00}, /* 0x88 -> 0x80; Koski select external clock */ + {0xf9, 0x40, 0x00}, + {0xfa, 0x00, 0x00}, /* 0x1F -> 0x19 Koski Left speaker volume */ + {0xfb, 0x00, 0x00}, /* 0x00 -> 0x19 Koski Right speaker volume */ + {0xfc, 0x10, 0x00}, /* 0x1F -> 0x19 Koski Left speaker volume */ + {0xfd, 0x10, 0x00}, /* 0x00 -> 0x19 Koski Right speaker volume */ + {0xfe, 0x00, 0x00}, /* 0x00 -> Koski output mixer for stereo */ + {0xff, 0x8c, 0x00}, /* 0x00 -> 0x81 Koski enable receiver */ + }; + +struct sc_reg_access voice_call_off[] = { + {0x227, 0x00, 0x00}, + }; + +struct sc_reg_access output_receiver[] = { + {0xff, 0x81, 0x00}, + }; + +struct sc_reg_access output_speaker[] = { + {0xff, 0xb0, 0x00}, + }; + +struct sc_reg_access output_bluetooth[] = { + {0xff, 0x00, 0x00}, + {0x208, 0x00, 0x00}, + {0x209, 0x00, 0x00}, + }; + + +struct sc_reg_access output_headset[] = { + {0x208, 0x58, 0x00}, + {0x21b, 0x40, 0x00}, + {0x21c, 0x0c, 0x00}, + {0x221, 0x40, 0x00}, + {0x222, 0x10, 0x00}, + {0x223, 0x00, 0x00}, + {0x224, 0x00, 0x00}, + {0x226, 0x3f, 0x00}, + {0xfc, 0x12, 0x00}, + {0xfd, 0x12, 0x00}, + {0xff, 0x8c, 0x00}, + }; + + +struct sc_reg_access call_mute[] = { + {0x220, 0x00, MASK5|MASK6}, + {0x221, 0x00, MASK5|MASK6}, + {0x223, 0x00, 0xff}, + }; + + + +struct sc_reg_access call_unmute[] = { + {0x220, 0x40, MASK5|MASK6}, + {0x221, 0x40, MASK5|MASK6}, + {0x223, 0x20, 0xff}, + }; + +struct sc_reg_access output_headset_pb[] = { + {0xfd, 0x10, 0x00}, + {0xfc, 0x10, 0x00}, + {0xff, 0x8c, 0x00}, + }; + +struct sc_reg_access output_speaker_pb[] = { + {0xfd, 0x10, 0x00}, + {0xfc, 0x10, 0x00}, + {0xff, 0xb0, 0x00}, + }; + +/* Print out MAX Codec registers */ +void mx_print_regs_(void) +{ + struct sc_reg_access sc_access[1] = {{0,},}; + + int i = 0; + int retval = 0; + + printk("Printing out MX Audio registers:\n"); + printk("REG VALUE\n"); + + sc_access[0].reg_addr = 0xF4; + retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); + printk("0x%x 0x%x\n", sc_access[0].reg_addr, sc_access[0].value); + + + for( i = 0xf9; i <= 0xff; i++ ) + { + sc_access[0].reg_addr = i; + retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); + printk("0x%x 0x%x\n", sc_access[0].reg_addr, + sc_access[0].value); + } + + for( i = 0x200; i <= 0x227; i++ ) + { + sc_access[0].reg_addr = i; + retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); + printk("0x%x 0x%x\n", sc_access[0].reg_addr, + sc_access[0].value); + } + + +} + + + +int set_voice_call(bool start) +{ + int retval = 0; + struct sc_reg_access *reg_table; + int table_len=0; + + if(start){ + /*start voice call*/ + printk("set_voice_call table_len = %d\n",sizeof(voice_call_on)/4); + + if (koski_jack) + { + reg_table = voice_call_on_hs; + table_len = sizeof(voice_call_on_hs)/4; + } + else + { + reg_table = voice_call_on; + table_len = sizeof(voice_call_on)/4; + } + koski_voice_call = 1; + } + else{ + printk("set_voice_call table_len\n",sizeof(voice_call_off)/4); + reg_table = voice_call_off; + table_len = sizeof(voice_call_off)/4; + koski_voice_call = 0; + } + + retval = sst_sc_reg_access(reg_table, PMIC_WRITE, table_len); + + if (0 != retval) { + /* pmic communication fails */ + pr_debug("pmic commn failed \n"); + return retval; + } + + return retval; +} + + +int set_output_device(enum output_devices output_dev) +{ + int retval = 0; + struct sc_reg_access *reg_table; + int table_len=0; + + if (output_dev>=OUTPUT_ERROR){ + pr_debug("Invalid output devices\n"); + return -1; + } + + switch (output_dev){ + case OUTPUT_RECEIVER: + printk("set output OUTPUT_RECEIVER\n"); + reg_table = output_receiver; + table_len = sizeof(output_receiver)/4; + break; + case OUTPUT_SPEAKER: + printk("set output OUTPUT_SPEAKER\n"); + if(!koski_voice_call) + { + reg_table = output_speaker_pb; + table_len = sizeof(output_speaker_pb)/4; + } + else + { + printk("Koski voice call\n"); + //reg_table = output_speaker; + reg_table = voice_call_on; + //table_len = sizeof(output_speaker)/4; + table_len = sizeof(voice_call_on)/4; + } + break; + case OUTPUT_BLUETOOTH: + printk("set output OUTPUT_BLUETOOTH\n"); + reg_table = output_bluetooth; + table_len = sizeof(output_bluetooth)/4; + break; + case OUTPUT_HEADSET: + printk("set output OUTPUT_HEADSET\n"); + if(!koski_voice_call) + { + reg_table = output_headset_pb; + table_len = sizeof(output_headset_pb)/4; + } + else + { + printk("Koski voice call\n"); + //reg_table = output_headset; + reg_table = voice_call_on_hs; + //table_len = sizeof(output_headset)/4; + table_len = sizeof(voice_call_on_hs)/4; + } + break; + default: + printk("set output OUTPUT_ERROR\n"); + return -1; + break; + } + + + retval = sst_sc_reg_access(reg_table, PMIC_WRITE, table_len); + + if (0 != retval) { + /* pmic communication fails */ + printk("pmic commn failed \n"); + return retval; + } + //mx_print_regs_(); + return retval; +} + + + +int load_reg_table(struct sc_reg_access *reg_table,int table_len) +{ + int retval = 0; + + printk("load_reg_table table len=%d\n",table_len); + + retval = sst_sc_reg_access(reg_table, PMIC_READ_MODIFY, table_len); + + if (0 != retval) { + /* pmic communication fails */ + printk("pmic commn failed \n"); + return retval; + } + mx_print_regs_(); + return retval; +} + +int configure_koski_audio(int conf_type) +{ + int output_type,audio_type; + output_type = (conf_type)>>8; + audio_type = (conf_type) & 0x00ff; + + printk("configure_koski_audio audio_type:%d output_type:%d\n",audio_type,output_type); + + if (audio_type== VOICE_CALL_ON) + { + set_voice_call(true); + } + else if (audio_type==VOICE_CALL_OFF) + set_voice_call(false); + else if (audio_type==FM_RADIO_ON) + set_voice_call(false); + else if (audio_type==FM_RADIO_OFF) + set_voice_call(false); + else if (audio_type==VOICE_CALL_MUTE) + { + printk("VOICE_CALL_MUTE \n"); + load_reg_table(call_mute,TABLE_LEN(call_mute)); + } + else if (audio_type==VOICE_CALL_UNMUTE) + { + printk("VOICE_CALL_UNMUTE \n"); + load_reg_table(call_unmute,TABLE_LEN(call_unmute)); + } + if (output_type!=0) + set_output_device(output_type); + + return 0; +} + + + + + Index: linux-2.6.33/sound/pci/sst/koski_audio_conf.h =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/koski_audio_conf.h @@ -0,0 +1,59 @@ +#ifndef __KOSKI_AUDIO_CONF_H +#define __KOSKI_AUDIO_CONF_H +/* + * koski_audio_conf.c - koski audio configure part for audio engine + * + * Copyright (C) 2009 aavamobile + * Authors: Samuli.konttila <samuli.konttila@aavamobile.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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#define KOSKI_JACK_DETECTION + +enum koski_conf_type { + VOICE_CALL_ON=1, + VOICE_CALL_OFF, + FM_RADIO_ON, + FM_RADIO_OFF, + VOICE_CALL_MUTE, + VOICE_CALL_UNMUTE, +}; + +enum output_devices { + OUTPUT_RECEIVER=1, + OUTPUT_SPEAKER, + OUTPUT_BLUETOOTH, + OUTPUT_HEADSET, + OUTPUT_ERROR +}; + + + + + +int configure_koski_audio(int conf_type); +int set_voice_call(bool start); + +int set_output_device(enum output_devices output_dev); + +#endif + + + + Index: linux-2.6.33/drivers/spi/hh2serial.c =================================================================== --- linux-2.6.33.orig/drivers/spi/hh2serial.c +++ linux-2.6.33/drivers/spi/hh2serial.c @@ -1,7 +1,17 @@ +/****************************************************************************** + + Copyright (c) 2009 + Infineon Technologies AG + Am Campeon 1-12; 81726 Munich, Germany + + For licensing information, see the file 'LICENSE' in the root folder of + this software module. + +******************************************************************************/ /* - * HH2 SPI Serial driver + * HH2 SPI Serial driver Version 0.2 Beta * - * Copyright (C) 2009 Markus Burvall (Markus.Burvall@swedenconnectivity.com) + * Written by: 2009 Markus Burvall (Markus.Burvall@swedenconnectivity.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 @@ -10,15 +20,10 @@ */ -#define DEBUG 1 - -//#define HH2_TTY_ECHO -//#define HH2_TTY_SEND_POLL -//#define HH2_NO_SPI #define HH2SERIAL_SPI_16BIT -//#define HH2SERIAL_ENABLE_DEBUG #define HH2SERIAL_SPI_POLL +/*#define HH2SERIAL_SHOW_ERRORS*/ #include <linux/kernel.h> #include <linux/module.h> @@ -66,6 +71,7 @@ struct hh2serial_dev { atomic_t tty_need_read; atomic_t spi_irq_pending; int mthread_up; + int hhRxBufferBytes; }; static const char driver_name[] = "hh2serial"; @@ -89,13 +95,7 @@ static struct hh2serial_dev priv0; #define GPSD_DREAD 0xC0 /* bit 7 and 6 */ #define GPSD_CRWRITE 0x00 /* All zero */ -#ifdef HH2SERIAL_SPI_16BIT -/* HH2 DATA OPERATIONS */ -#define GPSD_16BIT_SRREAD 0x8000 /* bit 7 */ -#define GPSD_16BIT_DWRITE 0x4000 /* bit 6 */ -#define GPSD_16BIT_DREAD 0xC000 /* bit 7 and 6 */ -#define GPSD_16BIT_CRWRITE 0x0000 /* All zero */ -#endif + /* HH2 STATUS REGISTER */ #define GPSS_TCNT 0x1F /* bits [4..0] */ @@ -192,9 +192,7 @@ int hh2serial_spi_get_rx_len(struct hh2s buf_ptr = x.rx_buf; #ifdef HH2SERIAL_ENABLE_DEBUG - printk(KERN_INFO "hh2serial RD:%02X, %02X\n", - *buf_ptr, - buf_ptr[1]); + printk(KERN_INFO "hh2serial RD:%02X, %02X\n", *buf_ptr, buf_ptr[1]); #endif #ifndef HH2SERIAL_SPI_16BIT @@ -203,33 +201,56 @@ int hh2serial_spi_get_rx_len(struct hh2s ret = *buf_ptr & GPSS_TCNT; /* Check buffer overrun or underrun errors */ +#ifdef HH2SERIAL_SHOW_ERRORS if (*buf_ptr & GPSS_TERR) printk(KERN_INFO "hh2serial HH2 transmitter underrun!\n"); if (*buf_ptr & GPSS_RERR) printk(KERN_INFO "hh2serial HH2 receiver overrun!\n"); - +#endif + if (*buf_ptr & GPSS_REMPTY) + { + hh2serial->hhRxBufferBytes = HH2SERIAL_SPI_MAX_BYTES; +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial HH2 rx empty!\n"); +#endif + } #else /* 16 bit second byte is status register */ /* Available bytes */ ret = buf_ptr[1] & GPSS_TCNT; /* Check buffer overrun or underrun errors */ +#ifdef HH2SERIAL_SHOW_ERRORS if (buf_ptr[1] & GPSS_TERR) printk(KERN_INFO "hh2serial HH2 transmitter underrun!\n"); if (buf_ptr[1] & GPSS_RERR) printk(KERN_INFO "hh2serial HH2 receiver overrun!\n"); #endif + + if (buf_ptr[1] & GPSS_REMPTY) + { + hh2serial->hhRxBufferBytes = HH2SERIAL_SPI_MAX_BYTES; +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial HH2 rx empty!\n"); +#endif + } +#endif /* Take care of errors */ /* FIX ME */ #ifdef HH2SERIAL_ENABLE_DEBUG - printk(KERN_INFO "hh2serial SR:%02X, rx len %d\n", - buf_ptr[1], - ret); + printk(KERN_INFO "hh2serial SR:%02X, rx len %d\n", buf_ptr[1], ret); #endif } + else + { +#ifdef HH2SERIAL_SHOW_ERRORS +printk(KERN_INFO "hh2serial Rd_status, spi_sync failed: %d\n",ret); +#endif + ret = 0; + } kfree(local_buf); return ret; @@ -332,11 +353,22 @@ int hh2serial_spi_read(struct hh2serial_ available_rd = *buf_ptr & GPSS_TCNT; /* Check buffer overrun or underrun errors */ +#ifdef HH2SERIAL_SHOW_ERRORS if (*buf_ptr & GPSS_TERR) printk(KERN_INFO "hh2serial HH2 transmitter underrun!\n"); if (*buf_ptr & GPSS_RERR) printk(KERN_INFO "hh2serial HH2 receiver overrun!\n"); +#endif + + if (*buf_ptr & GPSS_REMPTY) + { + hh2serial->hhRxBufferBytes = HH2SERIAL_SPI_MAX_BYTES; +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial HH2 rx empty!\n"); +#endif + + } #else /* 16 bit second byte is status register */ /* Every other byte is status register */ @@ -345,6 +377,7 @@ int hh2serial_spi_read(struct hh2serial_ available_rd = (buf_ptr[len_inc_hdr-1] & GPSS_TCNT) - 1; /* Check buffer overrun or underrun errors */ +#ifdef HH2SERIAL_SHOW_ERRORS if (buf_ptr[len_inc_hdr-1] & GPSS_TERR) printk(KERN_INFO "hh2serial HH2 transmitter underrun!\n"); @@ -352,6 +385,14 @@ int hh2serial_spi_read(struct hh2serial_ printk(KERN_INFO "hh2serial HH2 receiver overrun!\n"); #endif + if (buf_ptr[len_inc_hdr-1] & GPSS_REMPTY) + { + hh2serial->hhRxBufferBytes = HH2SERIAL_SPI_MAX_BYTES; +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial HH2 rx empty!\n"); +#endif + } +#endif #ifdef HH2SERIAL_ENABLE_DEBUG printk(KERN_INFO "hh2serial_spi_read len inc hdr wr:%d, avail rd %d, cs_change:%d\n", @@ -388,6 +429,13 @@ int hh2serial_spi_read(struct hh2serial_ #endif } + else + { +#ifdef HH2SERIAL_SHOW_ERRORS +printk(KERN_INFO "hh2serial spi_read, spi_sync failed: %d\n",status); +#endif + + } kfree(local_buf); return status; @@ -435,8 +483,8 @@ int hh2serial_spi_write(struct hh2serial x.len = len_inc_hdr; spi_message_add_tail(&x, &message); - /* Allocate and make room for 1 byte header */ - local_buf = kzalloc(HH2SERIAL_BUFSIZE+1, GFP_KERNEL); + /* Allocate and make room for 1 byte header(RX and TX) */ + local_buf = kzalloc(HH2SERIAL_BUFSIZE+2, GFP_KERNEL); if (!local_buf) return -ENOMEM; @@ -453,7 +501,6 @@ int hh2serial_spi_write(struct hh2serial int byte_index = 2; while (byte_index < len_inc_hdr) { - local_buf[byte_index] = txbuf[byte_index]; local_buf[byte_index+1] = GPSD_DWRITE; byte_index = byte_index + 2; @@ -495,24 +542,55 @@ int hh2serial_spi_write(struct hh2serial available_rd = *buf_ptr & GPSS_TCNT; /* Check buffer overrun or underrun errors */ +#ifdef HH2SERIAL_SHOW_ERRORS if (*buf_ptr & GPSS_TERR) printk(KERN_INFO "hh2serial HH2 transmitter underrun!\n"); if (*buf_ptr & GPSS_RERR) printk(KERN_INFO "hh2serial HH2 receiver overrun!\n"); +#endif + if (*buf_ptr & GPSS_REMPTY) + { + /* Buffer was empty but len bytes has been written after that */ + hh2serial->hhRxBufferBytes = HH2SERIAL_SPI_MAX_BYTES-len; +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial HH2 rx empty!\n"); +#endif + } + else + { + hh2serial->hhRxBufferBytes -= len; + } #else + /* FIXME_Only last status is interesting or? */ + /* Might have to check every status register to se if empty */ /* 16 bit second byte is status register */ /* Available bytes */ - available_rd = buf_ptr[1] & GPSS_TCNT; + available_rd = buf_ptr[len_inc_hdr-1] & GPSS_TCNT; /* Check buffer overrun or underrun errors */ - if (buf_ptr[1] & GPSS_TERR) +#ifdef HH2SERIAL_SHOW_ERRORS + if (buf_ptr[len_inc_hdr-1] & GPSS_TERR) printk(KERN_INFO "hh2serial HH2 transmitter underrun!\n"); - if (buf_ptr[1] & GPSS_RERR) + if (buf_ptr[len_inc_hdr-1] & GPSS_RERR) printk(KERN_INFO "hh2serial HH2 receiver overrun!\n"); #endif + if (buf_ptr[len_inc_hdr-1] & GPSS_REMPTY) + { + /* Buffer was empty but one byte has been written after that */ + hh2serial->hhRxBufferBytes = HH2SERIAL_SPI_MAX_BYTES-1; +#ifdef HH2SERIAL_ENABLE_DEBUG + printk(KERN_INFO "hh2serial HH2 rx empty!\n"); +#endif + } + else + { + /* Only 8 bit of every 16 is data */ + hh2serial->hhRxBufferBytes -= (len/2); + } +#endif #ifdef HH2SERIAL_ENABLE_DEBUG printk(KERN_INFO "hh2serial_spi_write:%02X, %02X\n", @@ -526,9 +604,14 @@ int hh2serial_spi_write(struct hh2serial *spiAvailData = available_rd; - } + else + { +#ifdef HH2SERIAL_SHOW_ERRORS +printk(KERN_INFO "hh2serial spi_write, spi_sync failed: %d\n",status); +#endif + } kfree(local_buf); @@ -616,61 +699,77 @@ static inline void hh2serial_write_circ_ #ifdef HH2SERIAL_ENABLE_DEBUG printk(KERN_INFO "Bytes in circ buffer: %d\n", left); #endif - while (left) { - /* MrB Change below to 1 and word length to 16 to write 16 bit - word by word */ -#ifndef HH2SERIAL_SPI_16BIT - len = (left >= HH2SERIAL_SPI_MAX_BYTES) ? HH2SERIAL_SPI_MAX_BYTES : left; -#else - len = (left >= HH2SERIAL_SPI_MAX_BYTES) ? HH2SERIAL_SPI_MAX_BYTES : left; -#endif - - memset(obuf, 0, len); - memset(ibuf, 0, len); - for (i = 0; i < len; i++) { + while (left || (rxlen > 0)) { - obuf[i] = (u8)xmit->buf[xmit->tail]; - - xmit->tail = (xmit->tail + 1) & - (UART_XMIT_SIZE - 1); - } -#ifndef HH2SERIAL_SPI_16BIT + if (left) + { + /* FIXME len = MIN(left , hhRxBufferBytes) + if len 0 is then only read status register and read */ + + len = (left >= priv->hhRxBufferBytes) ? priv->hhRxBufferBytes : left; + + + if (len > 0) + { + memset(obuf, 0, len); + memset(ibuf, 0, len); + for (i = 0; i < len; i++) { + + obuf[i] = (u8)xmit->buf[xmit->tail]; + + xmit->tail = (xmit->tail + 1) & + (UART_XMIT_SIZE - 1); + } + #ifndef HH2SERIAL_SPI_16BIT + + /* FIXME check status */ + hh2serial_spi_write(priv, (u8 *)obuf, + &rxlen, len); + + #else + /* len * 2 since 16 bits instead of 8 bits */ + /* FIXME check status */ + hh2serial_spi_write(priv, (u8 *)obuf, + &rxlen, len*2); + + #endif + left -= len; + } + else /* Read rx len */ + { + #ifdef HH2SERIAL_SHOW_ERRORS + printk(KERN_INFO "hh2serial wr buf2spi, rxBuf full?\n"); + #endif + rxlen = hh2serial_spi_get_rx_len(priv); - hh2serial_spi_write(priv, (u8 *)obuf, - &rxlen, len); -#else - /* len * 2 since 16 bits instead of 8 bits */ - hh2serial_spi_write(priv, (u8 *)obuf, - &rxlen, len*2); - -#endif - left -= len; - } + } + } #ifdef HH2SERIAL_ENABLE_DEBUG printk(KERN_INFO "hh2serial: Bytes avail to read: %d\n", rxlen); #endif /* Read if available bytes */ /* FIXME: Could add a maximum read loop here */ - while (rxlen > 0) - { - - len = rxlen; -#ifndef HH2SERIAL_SPI_16BIT - hh2serial_spi_read(priv, (u8 *)ibuf, &rxlen, len); -#else - hh2serial_spi_read(priv, (u8 *)ibuf, &rxlen, len*2); -#endif - - for (i = 0, j = 0; i < len; i++) { - valid_str[j++] = (u8)(ibuf[i]); - } + if (rxlen > 0) + { - if (j) - hh2serial_write2tty(priv, valid_str, j); - - priv->port.icount.tx += len; - } + len = rxlen; + #ifndef HH2SERIAL_SPI_16BIT + hh2serial_spi_read(priv, (u8 *)ibuf, &rxlen, len); + #else + hh2serial_spi_read(priv, (u8 *)ibuf, &rxlen, len*2); + #endif + + for (i = 0, j = 0; i < len; i++) { + valid_str[j++] = (u8)(ibuf[i]); + } + + if (j) + hh2serial_write2tty(priv, valid_str, j); + + priv->port.icount.tx += len; + } + } } } #endif @@ -793,7 +892,7 @@ static int hh2serial_main_thread(void *_ /* Read from tty send to spi */ #ifdef HH2SERIAL_ENABLE_DEBUG printk(KERN_INFO "hh2serial: Read from tty send to spi\n"); -#endif +#endif /* Read from tty send to spi */ /* Receive data from spi send to UART */ @@ -1153,11 +1252,13 @@ static int hh2serial_startup(struct uart struct hh2serial_dev *priv = container_of(port, struct hh2serial_dev, port); FUNC_ENTER(); + /* Initialize RxBuffer to 0 */ + priv->hhRxBufferBytes = 0; #ifdef HH2SERIAL_SPI_POLL priv->poll_thread = kthread_run(hh2serial_poll_thread, priv, "hh2serial_poll"); if (IS_ERR(priv->poll_thread)) { - printk(KERN_INFO "hh2serial Failed to start poll thread: %ld", + printk(KERN_INFO "hh2serial Failed to start poll thread: %ld", PTR_ERR(priv->poll_thread)); } #endif
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor