/*
- * Atmel maXTouch Touchscreen driver
+ * Atmel maXTouch Touchscreen Controller Driver
*
- * Copyright (C) 2010 Samsung Electronics Co.Ltd
- * Author: Joonyoung Shim <jy0922.shim@samsung.com>
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * Copyright (C) 2010 Atmel Corporation
+ * Copyright (C) 2010 Ulf Samuelsson (ulf@atmel.com)
+ * Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.com>
+ * Contains changes by Wind River Systems, 2010-09-29
+ *
+ * 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/init.h>
-#include <linux/delay.h>
-#include <linux/firmware.h>
#include <linux/i2c.h>
-#include <linux/i2c/atmel_mxt_ts.h>
-#include <linux/input/mt.h>
#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/earlysuspend.h>
+#include <linux/pm_runtime.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+#include <linux/atmel_mxt_ts.h>
+
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <linux/time.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <asm/io.h>
+/* 1-->WINTEK 0-->SINTEK */
+#define TOUCH_PANEL_IS_WINTEK 0
+#define MAGIC_PRODUCT_T38_BYTE7_WINTEK 0x02
+#define MAGIC_PRODUCT_T38_BYTE7_SINTEK 0x03
+#if TOUCH_PANEL_IS_WINTEK
+#define MAGIC_PRODUCT_T38_BYTE7 MAGIC_PRODUCT_T38_BYTE7_WINTEK
+#else /* SINTEK */
+#define MAGIC_PRODUCT_T38_BYTE7 MAGIC_PRODUCT_T38_BYTE7_SINTEK
+#endif
-/* Version */
-#define MXT_VER_20 20
-#define MXT_VER_21 21
-#define MXT_VER_22 22
-
-/* Slave addresses */
-#define MXT_APP_LOW 0x4a
-#define MXT_APP_HIGH 0x4b
-#define MXT_BOOT_LOW 0x24
-#define MXT_BOOT_HIGH 0x25
-
-/* Firmware */
-#define MXT_FW_NAME "maxtouch.fw"
-
-/* Registers */
-#define MXT_FAMILY_ID 0x00
-#define MXT_VARIANT_ID 0x01
-#define MXT_VERSION 0x02
-#define MXT_BUILD 0x03
-#define MXT_MATRIX_X_SIZE 0x04
-#define MXT_MATRIX_Y_SIZE 0x05
-#define MXT_OBJECT_NUM 0x06
-#define MXT_OBJECT_START 0x07
-
-#define MXT_OBJECT_SIZE 6
-
-/* Object types */
-#define MXT_DEBUG_DIAGNOSTIC 37
-#define MXT_GEN_MESSAGE 5
-#define MXT_GEN_COMMAND 6
-#define MXT_GEN_POWER 7
-#define MXT_GEN_ACQUIRE 8
-#define MXT_TOUCH_MULTI 9
-#define MXT_TOUCH_KEYARRAY 15
-#define MXT_TOUCH_PROXIMITY 23
-#define MXT_PROCI_GRIPFACE 20
-#define MXT_PROCG_NOISE 22
-#define MXT_PROCI_ONETOUCH 24
-#define MXT_PROCI_TWOTOUCH 27
-#define MXT_PROCI_GRIP 40
-#define MXT_PROCI_PALM 41
-#define MXT_SPT_COMMSCONFIG 18
-#define MXT_SPT_GPIOPWM 19
-#define MXT_SPT_SELFTEST 25
-#define MXT_SPT_CTECONFIG 28
-#define MXT_SPT_USERDATA 38
-#define MXT_SPT_DIGITIZER 43
-#define MXT_SPT_MESSAGECOUNT 44
-
-/* MXT_GEN_COMMAND field */
-#define MXT_COMMAND_RESET 0
-#define MXT_COMMAND_BACKUPNV 1
-#define MXT_COMMAND_CALIBRATE 2
-#define MXT_COMMAND_REPORTALL 3
-#define MXT_COMMAND_DIAGNOSTIC 5
-
-/* MXT_GEN_POWER field */
-#define MXT_POWER_IDLEACQINT 0
-#define MXT_POWER_ACTVACQINT 1
-#define MXT_POWER_ACTV2IDLETO 2
-
-/* MXT_GEN_ACQUIRE field */
-#define MXT_ACQUIRE_CHRGTIME 0
-#define MXT_ACQUIRE_TCHDRIFT 2
-#define MXT_ACQUIRE_DRIFTST 3
-#define MXT_ACQUIRE_TCHAUTOCAL 4
-#define MXT_ACQUIRE_SYNC 5
-#define MXT_ACQUIRE_ATCHCALST 6
-#define MXT_ACQUIRE_ATCHCALSTHR 7
-
-/* MXT_TOUCH_MULTI field */
-#define MXT_TOUCH_CTRL 0
-#define MXT_TOUCH_XORIGIN 1
-#define MXT_TOUCH_YORIGIN 2
-#define MXT_TOUCH_XSIZE 3
-#define MXT_TOUCH_YSIZE 4
-#define MXT_TOUCH_BLEN 6
-#define MXT_TOUCH_TCHTHR 7
-#define MXT_TOUCH_TCHDI 8
-#define MXT_TOUCH_ORIENT 9
-#define MXT_TOUCH_MOVHYSTI 11
-#define MXT_TOUCH_MOVHYSTN 12
-#define MXT_TOUCH_NUMTOUCH 14
-#define MXT_TOUCH_MRGHYST 15
-#define MXT_TOUCH_MRGTHR 16
-#define MXT_TOUCH_AMPHYST 17
-#define MXT_TOUCH_XRANGE_LSB 18
-#define MXT_TOUCH_XRANGE_MSB 19
-#define MXT_TOUCH_YRANGE_LSB 20
-#define MXT_TOUCH_YRANGE_MSB 21
-#define MXT_TOUCH_XLOCLIP 22
-#define MXT_TOUCH_XHICLIP 23
-#define MXT_TOUCH_YLOCLIP 24
-#define MXT_TOUCH_YHICLIP 25
-#define MXT_TOUCH_XEDGECTRL 26
-#define MXT_TOUCH_XEDGEDIST 27
-#define MXT_TOUCH_YEDGECTRL 28
-#define MXT_TOUCH_YEDGEDIST 29
-#define MXT_TOUCH_JUMPLIMIT 30
-
-/* MXT_PROCI_GRIPFACE field */
-#define MXT_GRIPFACE_CTRL 0
-#define MXT_GRIPFACE_XLOGRIP 1
-#define MXT_GRIPFACE_XHIGRIP 2
-#define MXT_GRIPFACE_YLOGRIP 3
-#define MXT_GRIPFACE_YHIGRIP 4
-#define MXT_GRIPFACE_MAXTCHS 5
-#define MXT_GRIPFACE_SZTHR1 7
-#define MXT_GRIPFACE_SZTHR2 8
-#define MXT_GRIPFACE_SHPTHR1 9
-#define MXT_GRIPFACE_SHPTHR2 10
-#define MXT_GRIPFACE_SUPEXTTO 11
-
-/* MXT_PROCI_NOISE field */
-#define MXT_NOISE_CTRL 0
-#define MXT_NOISE_OUTFLEN 1
-#define MXT_NOISE_GCAFUL_LSB 3
-#define MXT_NOISE_GCAFUL_MSB 4
-#define MXT_NOISE_GCAFLL_LSB 5
-#define MXT_NOISE_GCAFLL_MSB 6
-#define MXT_NOISE_ACTVGCAFVALID 7
-#define MXT_NOISE_NOISETHR 8
-#define MXT_NOISE_FREQHOPSCALE 10
-#define MXT_NOISE_FREQ0 11
-#define MXT_NOISE_FREQ1 12
-#define MXT_NOISE_FREQ2 13
-#define MXT_NOISE_FREQ3 14
-#define MXT_NOISE_FREQ4 15
-#define MXT_NOISE_IDLEGCAFVALID 16
-
-/* MXT_SPT_COMMSCONFIG */
-#define MXT_COMMS_CTRL 0
-#define MXT_COMMS_CMD 1
-
-/* MXT_SPT_CTECONFIG field */
-#define MXT_CTE_CTRL 0
-#define MXT_CTE_CMD 1
-#define MXT_CTE_MODE 2
-#define MXT_CTE_IDLEGCAFDEPTH 3
-#define MXT_CTE_ACTVGCAFDEPTH 4
-#define MXT_CTE_VOLTAGE 5
-
-#define MXT_VOLTAGE_DEFAULT 2700000
-#define MXT_VOLTAGE_STEP 10000
-
-/* Define for MXT_GEN_COMMAND */
-#define MXT_BOOT_VALUE 0xa5
-#define MXT_BACKUP_VALUE 0x55
-#define MXT_BACKUP_TIME 25 /* msec */
-#define MXT_RESET_TIME 65 /* msec */
-
-#define MXT_FWRESET_TIME 175 /* msec */
-
-/* Command to unlock bootloader */
-#define MXT_UNLOCK_CMD_MSB 0xaa
-#define MXT_UNLOCK_CMD_LSB 0xdc
-
-/* Bootloader mode status */
-#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */
-#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */
-#define MXT_FRAME_CRC_CHECK 0x02
-#define MXT_FRAME_CRC_FAIL 0x03
-#define MXT_FRAME_CRC_PASS 0x04
-#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */
-#define MXT_BOOT_STATUS_MASK 0x3f
-
-/* Touch status */
-#define MXT_SUPPRESS (1 << 1)
-#define MXT_AMP (1 << 2)
-#define MXT_VECTOR (1 << 3)
-#define MXT_MOVE (1 << 4)
-#define MXT_RELEASE (1 << 5)
-#define MXT_PRESS (1 << 6)
-#define MXT_DETECT (1 << 7)
-
-/* Touch orient bits */
-#define MXT_XY_SWITCH (1 << 0)
-#define MXT_X_INVERT (1 << 1)
-#define MXT_Y_INVERT (1 << 2)
-
-/* Touchscreen absolute values */
-#define MXT_MAX_AREA 0xff
-
-#define MXT_MAX_FINGER 10
-
-struct mxt_info {
- u8 family_id;
- u8 variant_id;
- u8 version;
- u8 build;
- u8 matrix_xsize;
- u8 matrix_ysize;
- u8 object_num;
+/* Routines for memory access within a 16 bit address space */
+static void mxt_calibrate(struct i2c_client *client);
+static int mxt_read_block(struct i2c_client *client, u16 addr, u16 length,
+ u8 *value);
+static int mxt_write_byte(struct i2c_client *client, u16 addr, u8 value);
+static int mxt_write_block(struct i2c_client *client, u16 addr, u16 length,
+ u8 *value);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+void mxt_early_suspend(struct early_suspend *h);
+void mxt_late_resume(struct early_suspend *h);
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static int intel_mid_add_debugfs(void);
+static unsigned char reg_add = 0;
+static unsigned char reg_add_offset = 0;
+#endif
+
+#define DRIVER_VERSION "0.9a"
+#define MULTI_TOUCH_ENABLED 1
+#define MXT_RESET_DELAY 65 /* ms */
+static int debug = DEBUG_INFO;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activate debugging output");
+
+static int stored_size[MXT_MAX_NUM_TOUCHES];
+static int stored_x[MXT_MAX_NUM_TOUCHES];
+static int stored_y[MXT_MAX_NUM_TOUCHES];
+static int iForceUpdateConfigValue = 0;
+static struct mxt_data *mxt_es;
+/* For backup NVM */
+u8 mxt_debug = 1;
+u16 T6_addr;
+u16 T38_addr;
+u8 v20_T38[] = { 11, 10, 26, 1, 0, 0, 0, MAGIC_PRODUCT_T38_BYTE7 };
+u8 arg1 = 0;
+u8 arg2 = 0;
+u8 mxt_cmd = 0;
+u16 low_boundary = 0;
+u16 high_boundary = 0;
+u8 slave_addr = MXT1386_I2C_ADDR1;
+
+/* Device Info descriptor */
+/* Parsed from maXTouch "Id information" inside device */
+struct mxt_device_info {
+ u8 family_id;
+ u8 variant_id;
+ u8 major;
+ u8 minor;
+ u8 build;
+ u8 num_objs;
+ u8 x_size;
+ u8 y_size;
+ char family_name[16]; /* Family name */
+ char variant_name[16]; /* Variant name */
+ u16 num_nodes; /* Number of sensor nodes */
};
+/* object descriptor table, parsed from maXTouch "object table" */
struct mxt_object {
- u8 type;
- u16 start_address;
- u8 size;
- u8 instances;
- u8 num_report_ids;
-
- /* to map object and message */
- u8 max_reportid;
+ u16 chip_addr;
+ u8 type;
+ u8 size;
+ u8 instances;
+ u8 num_report_ids;
};
-struct mxt_message {
- u8 reportid;
- u8 message[7];
- u8 checksum;
-};
-struct mxt_finger {
- int status;
- int x;
- int y;
- int area;
+/* Mapping from report id to object type and instance */
+struct report_id_map {
+ u8 object;
+ u8 instance;
+/*
+ * This is the first report ID belonging to object. It enables us to
+ * find out easily the touch number: each touch has different report
+ * ID (which are assigned to touches in increasing order). By
+ * subtracting the first report ID from current, we get the touch
+ * number.
+ */
+ u8 first_rid;
};
-/* Each client has this additional data */
+
+/* Driver datastructure */
struct mxt_data {
- struct i2c_client *client;
- struct input_dev *input_dev;
- const struct mxt_platform_data *pdata;
- struct mxt_object *object_table;
- struct mxt_info info;
- struct mxt_finger finger[MXT_MAX_FINGER];
- unsigned int irq;
- unsigned int max_x;
- unsigned int max_y;
+ struct i2c_client *client;
+ struct mutex dev_mutex;
+ struct input_dev *touch_input;
+ struct input_dev *key_input;
+ char touch_phys_name[32];
+ char key_phys_name[32];
+ int irq;
+
+ u16 last_read_addr;
+ bool new_msgs;
+
+ int valid_irq_counter;
+ int invalid_irq_counter;
+ int irq_counter;
+ int message_counter;
+ int read_fail_counter;
+
+ int bytes_to_read;
+
+ u8 xpos_format;
+ u8 ypos_format;
+
+ u8 numtouch;
+
+ struct mxt_device_info device_info;
+
+ u32 info_block_crc;
+ u32 configuration_crc;
+ u16 report_id_count;
+ struct report_id_map *rid_map;
+ struct mxt_object *object_table;
+
+ u16 msg_proc_addr;
+ u8 message_size;
+
+ u16 max_x_val;
+ u16 max_y_val;
+ u16 orientation;
+
+ void (*init_hw)(void);
+ void (*exit_hw)(void);
+ u8 (*valid_interrupt)(void);
+ u8 (*read_chg)(void);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ u8 T7[3];
+ struct early_suspend es;
+ bool suspended;
+#endif
+ /* Put only non-touch messages to buffer if this is set */
+ char nontouch_msg_only;
+
+ int prev_key;
+
+ int mxt_intr_gpio;
+ int mxt_reset_gpio;
+
+ u8 *message_buf;
+ int recal_flag;
+ u64 timestamp;
+ u16 finger_pressed;
+ u8 finger_count;
+ u8 first_pressed;
+ u8 grip_suppression;
+ u8 face_suppression;
+
+ struct point_info pre_data[10];
+};
+
+/*
+ * This struct is used for i2c transfers.
+ */
+struct mxt_i2c_byte_transfer {
+ __le16 le_addr;
+ u8 data;
+} __attribute__ ((packed));
+
+#define I2C_RETRY_COUNT 5
+#define I2C_PAYLOAD_SIZE 254
+
+/*
+ * Check whether we have multi-touch enabled kernel; if not, report just the
+ * first touch (on mXT224, the maximum is 10 simultaneous touches).
+ * Because just the 1st one is reported, it might seem that the screen is not
+ * responding to touch if the first touch is removed while the screen is being
+ * touched by another finger, so beware.
+ *
+ * TODO: investigate if there is any standard set of input events that upper
+ * layers are expecting from a touchscreen? These can however be different for
+ * different platforms, and customers may have different opinions too about
+ * what should be interpreted as right-click, for example.
+ *
+ */
+
+static const u8 *obj_typ_name[] = {
+ [0] = "Reserved",
+ [5] = "GEN_MESSAGEPROCESSOR_T5",
+ [6] = "GEN_COMMANDPROCESSOR_T6",
+ [7] = "GEN_POWERCONFIG_T7",
+ [8] = "GEN_ACQUIRECONFIG_T8",
+ [9] = "TOUCH_MULTITOUCHSCREEN_T9",
+ [15] = "TOUCH_KEYARRAY_T15",
+ [18] = "SPT_COMMSCONFIG_T18",
+ [19] = "SPT_GPIOPWM_T19",
+ [20] = "PROCI_GRIPFACESUPPRESSION_T20",
+ [22] = "PROCG_NOISESUPPRESSION_T22",
+ [23] = "TOUCH_PROXIMITY_T23",
+ [24] = "PROCI_ONETOUCHGESTUREPROCESSOR_T24",
+ [25] = "SPT_SELFTEST_T25",
+ [27] = "PROCI_TWOTOUCHGESTUREPROCESSOR_T27",
+ [28] = "SPT_CTECONFIG_T28",
+ [37] = "DEBUG_DIAGNOSTICS_T37",
+ [38] = "SPT_USER_DATA_T38",
+ [40] = "PROCI_GRIPSUPPRESSION_T40",
+ [41] = "PROCI_PALMSUPPRESSION_T41",
+ [42] = "PROCI_FACESUPPRESSION_T42",
+ [43] = "SPT_DIGITIZER_T43",
+ [44] = "SPT_MESSAGECOUNT_T44",
};
-static bool mxt_object_readable(unsigned int type)
+/*
+ * All entries spare upto 255
+ */
+#define RESERVED_T255 255u
+void mxt_config_init(struct mxt_data *mxt)
{
- switch (type) {
- case MXT_GEN_MESSAGE:
- case MXT_GEN_COMMAND:
- case MXT_GEN_POWER:
- case MXT_GEN_ACQUIRE:
- case MXT_TOUCH_MULTI:
- case MXT_TOUCH_KEYARRAY:
- case MXT_TOUCH_PROXIMITY:
- case MXT_PROCI_GRIPFACE:
- case MXT_PROCG_NOISE:
- case MXT_PROCI_ONETOUCH:
- case MXT_PROCI_TWOTOUCH:
- case MXT_PROCI_GRIP:
- case MXT_PROCI_PALM:
- case MXT_SPT_COMMSCONFIG:
- case MXT_SPT_GPIOPWM:
- case MXT_SPT_SELFTEST:
- case MXT_SPT_CTECONFIG:
- case MXT_SPT_USERDATA:
- return true;
- default:
- return false;
+ int err = 0;
+ dev_info(&mxt->client->dev, "In function %s", __func__);
+ /* Fix for current comsumption. */
+ u8 v20_T7[] = { 32, 16, 50 };
+ u8 v20_T8[] = { 10, 0, 15, 15, 0, 0, 5, 45, 10, 192 };
+#if TOUCH_PANEL_IS_WINTEK
+ u8 v20_T9[] = { 143, 0, 0, 27, 42, 0, 16, 60, 3, 3, 0, 0, 3, 14, 10, 20,
+ 20, 10, 31, 3, 255, 4, 0, 0, 0, 0, 0, 0, 64, 0, 15, 15,
+ 49, 52 };
+#else /* SINTEK */
+ u8 v20_T9[] = { 143, 0, 0, 28, 42, 0, 16, 60, 3, 3, 0, 0, 3, 14, 10, 20,
+ 20, 10, 31, 3, 255, 4, 0, 0, 0, 0, 0, 0, 64, 0, 15, 15,
+ 49, 52 };
+#endif
+
+ u8 v20_T15[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ u8 v20_T18[] = { 0, 0 };
+ u8 v20_T22[] = { 5, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 15, 25, 35, 255, 255,
+ 0 };
+ u8 v20_T24[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0 };
+ u8 v20_T25[] = { 3, 0, 248, 42, 112, 23, 0, 0, 0, 0, 0, 0, 0, 0 };
+ u8 v20_T27[] = { 0, 0, 0, 0, 0, 0, 0 };
+ u8 v20_T28[] = { 0, 0, 0, 8, 16, 60 };
+ u8 v20_T40[] = { 0, 0, 0, 0, 0 };
+ u8 v20_T41[] = { 1, 0, 0, 35, 5, 20, 170 };
+ u8 v20_T43[] = { 0, 0, 0, 0, 0, 0 };
+
+ if (slave_addr == MXT1386_I2C_ADDR2) { /* 0X4C means Wintek and 0x4D means Sintek */
+ v20_T9[3] = 28;
+ v20_T22[8] = 20;
}
-}
-static bool mxt_object_writable(unsigned int type)
-{
- switch (type) {
- case MXT_GEN_COMMAND:
- case MXT_GEN_POWER:
- case MXT_GEN_ACQUIRE:
- case MXT_TOUCH_MULTI:
- case MXT_TOUCH_KEYARRAY:
- case MXT_TOUCH_PROXIMITY:
- case MXT_PROCI_GRIPFACE:
- case MXT_PROCG_NOISE:
- case MXT_PROCI_ONETOUCH:
- case MXT_PROCI_TWOTOUCH:
- case MXT_PROCI_GRIP:
- case MXT_PROCI_PALM:
- case MXT_SPT_GPIOPWM:
- case MXT_SPT_SELFTEST:
- case MXT_SPT_CTECONFIG:
- return true;
- default:
- return false;
+ u8 i = 0, max_objs = 0, j = 0;
+ u16 addr;
+ struct mxt_object *obj_index;
+
+ dev_dbg(&mxt->client->dev, "In function %s", __func__);
+ max_objs = mxt->device_info.num_objs;
+ obj_index = mxt->object_table;
+
+ for (i = 0; i < max_objs; i++) {
+ addr = obj_index->chip_addr;
+ for (j = 1; j < I2C_RETRY_COUNT; j++) {
+ err = 1;
+ switch (obj_index->type) {
+ case MXT_GEN_POWERCONFIG_T7:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T7), v20_T7);
+ break;
+ case MXT_GEN_ACQUIRECONFIG_T8:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T8), v20_T8);
+ break;
+ case MXT_TOUCH_MULTITOUCHSCREEN_T9:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T9), v20_T9);
+ dev_dbg(&mxt->client->dev, "init multitouch object");
+ break;
+ case MXT_TOUCH_KEYARRAY_T15:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T15), v20_T15);
+ break;
+ case MXT_SPT_COMMSCONFIG_T18:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T18), v20_T18);
+ break;
+ case MXT_PROCG_NOISESUPPRESSION_T22:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T22), v20_T22);
+ break;
+ case MXT_PROCI_ONETOUCHGESTUREPROCESSOR_T24:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T24), v20_T24);
+ break;
+ case MXT_SPT_SELFTEST_T25:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T25), v20_T25);
+ break;
+ case MXT_PROCI_TWOTOUCHGESTUREPROCESSOR_T27:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T27), v20_T27);
+ break;
+ case MXT_SPT_CTECONFIG_T28:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T28), v20_T28);
+ break;
+ case MXT_USER_INFO_T38:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T38), v20_T38);
+ break;
+ case MXT_PROCI_GRIPSUPPRESSION_T40:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T40), v20_T40);
+ break;
+ case MXT_PROCI_PALMSUPPRESSION_T41:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T41), v20_T41);
+ break;
+ case MXT_SPT_DIGITIZER_T43:
+ err = mxt_write_block(mxt->client, addr, sizeof(v20_T43), v20_T43);
+ break;
+ default:
+ break;
+ }
+ if (err > 0)
+ break;
+ }
+ if (j >= I2C_RETRY_COUNT)
+ dev_info(&mxt->client->dev,
+ "touch: config init abnormal,obj_index's type is %d",
+ obj_index->type);
+
+ obj_index++;
}
+ mxt_calibrate(mxt->client);
+ dev_dbg(&mxt->client->dev, "config init Done.");
}
-static void mxt_dump_message(struct device *dev,
- struct mxt_message *message)
+/* Returns object address in mXT chip, or zero if object is not found */
+static u16 get_object_address(uint8_t object_type,
+ uint8_t instance,
+ struct mxt_object *object_table,
+ int max_objs)
{
- dev_dbg(dev, "reportid:\t0x%x\n", message->reportid);
- dev_dbg(dev, "message1:\t0x%x\n", message->message[0]);
- dev_dbg(dev, "message2:\t0x%x\n", message->message[1]);
- dev_dbg(dev, "message3:\t0x%x\n", message->message[2]);
- dev_dbg(dev, "message4:\t0x%x\n", message->message[3]);
- dev_dbg(dev, "message5:\t0x%x\n", message->message[4]);
- dev_dbg(dev, "message6:\t0x%x\n", message->message[5]);
- dev_dbg(dev, "message7:\t0x%x\n", message->message[6]);
- dev_dbg(dev, "checksum:\t0x%x\n", message->checksum);
+ uint8_t object_table_index = 0;
+ uint8_t address_found = 0;
+ uint16_t address = 0;
+ struct mxt_object *obj;
+
+ while ((object_table_index < max_objs) && !address_found) {
+ obj = &object_table[object_table_index];
+ if (obj->type == object_type) {
+ address_found = 1;
+ /* Are there enough instances defined in the FW? */
+ if (obj->instances >= instance) {
+ address = obj->chip_addr +
+ (obj->size + 1) * instance;
+ } else {
+ break;
+ }
+ }
+ object_table_index++;
+ }
+ return address;
}
-static int mxt_check_bootloader(struct i2c_client *client,
- unsigned int state)
+int BackupNVM(struct mxt_data *mxt)
{
- u8 val;
+ u8 buf[1] = {0x55};
+ u8 T38_buf[8] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+
+ /* Backup the non-volatile memory (NVM). */
+ T6_addr = get_object_address(MXT_GEN_COMMANDPROCESSOR_T6, 0,
+ mxt->object_table, mxt->device_info.num_objs);
+ T38_addr = get_object_address(MXT_USER_INFO_T38, 0,
+ mxt->object_table, mxt->device_info.num_objs);
+
+ pr_info("BackupNVM T6_addr+1=0x%4x, T38_addr=0x%4x.\n", T6_addr+1, T38_addr);
+ mxt_read_block(mxt->client, T38_addr, sizeof(T38_buf), T38_buf);
+
+ if (iForceUpdateConfigValue == 1) {
+ pr_info("%s force update. ver=%d.%d.%d.%d --> %d.%d.%d.%d\n", \
+ __func__, \
+ T38_buf[0], T38_buf[1], T38_buf[2], T38_buf[3], \
+ v20_T38[0], v20_T38[1], v20_T38[2], v20_T38[3]);
+ } else {
+ #define PRINT_T38_V20() do { \
+ pr_info("%s not update. ver=%d.%d.%d.%d (%d)> %d.%d.%d.%d (%d)\n", \
+ __func__, \
+ T38_buf[0], T38_buf[1], T38_buf[2], T38_buf[3], T38_buf[7], \
+ v20_T38[0], v20_T38[1], v20_T38[2], v20_T38[3], v20_T38[7]); \
+ } while (0)
+
+ /* Check T38 previous config version. */
+ if ((T38_buf[0] != 0 && T38_buf[1] != 0) &&
+ (T38_buf[2] != 0 && T38_buf[3] != 0) &&
+ (T38_buf[4] == 0 && T38_buf[5] == 0) &&
+ (T38_buf[6] == 0 && T38_buf[7] == MAGIC_PRODUCT_T38_BYTE7)) {
+ if (T38_buf[0] > v20_T38[0]) {
+ PRINT_T38_V20();
+ return 0;
+ } else if (T38_buf[0] == v20_T38[0]) {
+ if (T38_buf[1] > v20_T38[1]) {
+ PRINT_T38_V20();
+ return 0;
+ } else if (T38_buf[1] == v20_T38[1]) {
+ if (T38_buf[2] > v20_T38[2]) {
+ PRINT_T38_V20();
+ return 0;
+ } else if (T38_buf[2] == v20_T38[2]) {
+ if (T38_buf[3] >= v20_T38[3]) {
+ PRINT_T38_V20();
+ return 0;
+ }
+ }
+ }
+ }
+ } else {
+ if (T38_buf[7] != MAGIC_PRODUCT_T38_BYTE7) {
+ pr_info("%s PRODUCT_T38_BYTE7 diff, force update. ver=%d.%d.%d.%d(%d) --> %d.%d.%d.%d(%d)\n", \
+ __func__, \
+ T38_buf[0], T38_buf[1], T38_buf[2], T38_buf[3], T38_buf[7], \
+ v20_T38[0], v20_T38[1], v20_T38[2], v20_T38[3], v20_T38[7]);
+ }
-recheck:
- if (i2c_master_recv(client, &val, 1) != 1) {
- dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
- return -EIO;
+ }
+ #undef PRINT_T38_V20
}
- switch (state) {
- case MXT_WAITING_BOOTLOAD_CMD:
- case MXT_WAITING_FRAME_DATA:
- val &= ~MXT_BOOT_STATUS_MASK;
- break;
- case MXT_FRAME_CRC_PASS:
- if (val == MXT_FRAME_CRC_CHECK)
- goto recheck;
- break;
- default:
- return -EINVAL;
+ mxt_config_init(mxt);
+
+ if (mxt_write_block(mxt->client, T6_addr+1, sizeof(buf), buf) == 1)
+ pr_info("Backup the configurations to non-volatile memory.\n");
+ else {
+ pr_err("Backup the configurations to non-volatile memory failed!!!\n");
+ return -1;
}
- if (val != state) {
- dev_err(&client->dev, "Unvalid bootloader mode state\n");
- return -EINVAL;
+ /*Reset the IC.*/
+ if (mxt_write_block(mxt->client, T6_addr, sizeof(buf), buf) == 1)
+ pr_info("Software reset the IC.\n");
+ else {
+ pr_err("Software reset the IC failed!!!\n");
+ return -1;
}
+ msleep(400);
+ pr_info("BackupNVM done. ver=%d.%d.%d.%d\n", v20_T38[0], v20_T38[1], v20_T38[2], v20_T38[3]);
return 0;
}
-static int mxt_unlock_bootloader(struct i2c_client *client)
+/* Calculates the 24-bit CRC sum. */
+static u32 CRC_24(u32 crc, u8 byte1, u8 byte2)
{
- u8 buf[2];
+ static const u32 crcpoly = 0x80001B;
+ u32 result;
+ u32 data_word;
+
+ data_word = ((((u16) byte2) << 8u) | byte1);
+ result = ((crc << 1u) ^ data_word);
+ if (result & 0x1000000)
+ result ^= crcpoly;
+ return result;
+}
- buf[0] = MXT_UNLOCK_CMD_LSB;
- buf[1] = MXT_UNLOCK_CMD_MSB;
+/* Calibrate rz*/
+static void mxt_calibrate(struct i2c_client *client)
+{
+ u16 base_addr;
+ struct mxt_data *mxt;
- if (i2c_master_send(client, buf, 2) != 2) {
- dev_err(&client->dev, "%s: i2c send failed\n", __func__);
- return -EIO;
- }
+ mxt = i2c_get_clientdata(client);
+ dev_dbg(&mxt->client->dev, "In function %s", __func__);
- return 0;
+ base_addr = get_object_address(MXT_GEN_COMMANDPROCESSOR_T6,
+ 0, mxt->object_table,
+ mxt->device_info.num_objs);
+ base_addr += MXT_ADR_T6_CALIBRATE;
+ mxt_write_byte(mxt->client, base_addr, 0x55);
}
-static int mxt_fw_write(struct i2c_client *client,
- const u8 *data, unsigned int frame_size)
+
+/*
+ * Reads a block of bytes from given address from mXT chip. If we are
+ * reading from message window, and previous read was from message window,
+ * there's no need to write the address pointer: the mXT chip will
+ * automatically set the address pointer back to message window start.
+ */
+
+static int mxt_read_block(struct i2c_client *client,
+ u16 addr,
+ u16 length,
+ u8 *value)
{
- if (i2c_master_send(client, data, frame_size) != frame_size) {
- dev_err(&client->dev, "%s: i2c send failed\n", __func__);
- return -EIO;
+ struct i2c_adapter *adapter = client->adapter;
+ struct i2c_msg msg[2];
+ __le16 le_addr;
+ struct mxt_data *mxt;
+ int err;
+
+ mxt = i2c_get_clientdata(client);
+
+ if (mxt != NULL) {
+ if ((mxt->last_read_addr == addr) &&
+ (addr == mxt->msg_proc_addr)) {
+ pm_runtime_get_sync(&client->dev);
+ if (i2c_master_recv(client, value, length) == length)
+ err = length;
+ else
+ err = -EIO;
+ pm_runtime_put(&client->dev);
+ return err;
+ } else {
+ mxt->last_read_addr = addr;
+ }
}
- return 0;
-}
-
-static int __mxt_read_reg(struct i2c_client *client,
- u16 reg, u16 len, void *val)
+ dev_dbg(&client->dev, "Writing address pointer & reading %d bytes"
+ " in on i2c transaction...\n", length);
+
+/* FIH { */
+/* Avoid stop delay */
+#if 0
+ le_addr = cpu_to_le16(addr);
+ msg[0].addr = client->addr;
+ msg[0].flags = 0x00;
+ msg[0].len = 2;
+ msg[0].buf = (u8 *) &le_addr;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = length;
+ msg[1].buf = (u8 *) value;
+ pm_runtime_get_sync(&client->dev);
+ if (i2c_transfer(adapter, msg, 2) == 2)
+ err = length;
+ else
+ err = -EIO;
+ pm_runtime_put(&client->dev);
+#else
{
- struct i2c_msg xfer[2];
- u8 buf[2];
-
- buf[0] = reg & 0xff;
- buf[1] = (reg >> 8) & 0xff;
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 2;
- xfer[0].buf = buf;
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = len;
- xfer[1].buf = val;
-
- if (i2c_transfer(client->adapter, xfer, 2) != 2) {
- dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
- return -EIO;
+ u8 reg[2];
+ reg[0] = addr & 0xFF;
+ reg[1] = (addr>>8) & 0xFF;
+
+ err = i2c_master_send(client, (u8 *)reg, 2);
+ if (err == 2)
+ err = 0;
+ else {
+ msleep(100); /* For deep sleep mode */
+ err = i2c_master_send(client, (u8 *)reg, 2);
+ if (err == 2)
+ err = 0;
+ else
+ err = -EIO;
}
- return 0;
+ if (err < 0)
+ return err;
+
+ err = i2c_master_recv(client, (u8 *)value, length);
+ if (err == length)
+ err = length;
+ else
+ err = -EIO;
}
+#endif
+/* FIH } */
+ return err;
+}
+
-static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val)
+/* Writes one byte to given address in mXT chip. */
+
+static int mxt_write_byte(struct i2c_client *client, u16 addr, u8 value)
{
- return __mxt_read_reg(client, reg, 1, val);
+
+ struct mxt_data *mxt;
+ struct mxt_i2c_byte_transfer i2c_byte_transfer;
+ int err;
+
+ mxt = i2c_get_clientdata(client);
+ if (mxt != NULL)
+ mxt->last_read_addr = -1;
+ i2c_byte_transfer.le_addr = cpu_to_le16(addr);
+ i2c_byte_transfer.data = value;
+
+ if (i2c_master_send(client, (u8 *) &i2c_byte_transfer, 3) == 3)
+ err = 0;
+ else
+ err = -EIO;
+
+ return err;
}
-static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
+/* Writes a block of bytes (max 256) to given address in mXT chip. */
+static int mxt_write_block(struct i2c_client *client,
+ u16 addr, u16 length, u8 *value)
{
- u8 buf[3];
+ int i;
+ struct {
+ __le16 le_addr;
+ u8 data[256];
+ } i2c_block_transfer;
+ struct mxt_data *mxt;
+
+ dev_dbg(&client->dev, "Writing %d bytes to %d...", length, addr);
+ if (length > 256)
+ return -EINVAL;
- buf[0] = reg & 0xff;
- buf[1] = (reg >> 8) & 0xff;
- buf[2] = val;
+ mxt = i2c_get_clientdata(client);
+ if (mxt != NULL)
+ mxt->last_read_addr = -1;
- if (i2c_master_send(client, buf, 3) != 3) {
- dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+ memcpy(i2c_block_transfer.data, value, length);
+ i2c_block_transfer.le_addr = cpu_to_le16(addr);
+ i = i2c_master_send(client, (u8 *) &i2c_block_transfer, length + 2);
+ if (i == (length + 2))
+ return length;
+ else
return -EIO;
- }
-
- return 0;
}
-static int mxt_read_object_table(struct i2c_client *client,
- u16 reg, u8 *object_buf)
+/* Calculates the CRC value for mXT infoblock. */
+int calculate_infoblock_crc(u32 *crc_result, u8 *data, int crc_area_size)
{
- return __mxt_read_reg(client, reg, MXT_OBJECT_SIZE,
- object_buf);
+ u32 crc = 0;
+ int i;
+
+ pr_debug("atmel_mxt224 In function %s", __func__);
+ for (i = 0; i < (crc_area_size - 1); i = i + 2)
+ crc = CRC_24(crc, *(data + i), *(data + i + 1));
+ /* If uneven size, pad with zero */
+ if (crc_area_size & 0x0001)
+ crc = CRC_24(crc, *(data + i), 0);
+ /* Return only 24 bits of CRC. */
+ *crc_result = (crc & 0x00FFFFFF);
+
+ return 0;
}
-static struct mxt_object *
-mxt_get_object(struct mxt_data *data, u8 type)
+static void report_mt(struct mxt_data *mxt)
{
- struct mxt_object *object;
int i;
+ int active_touches = 0;
+
+ for (i = 0; i < mxt->numtouch; i++) {
+ if (!stored_size[i])
+ continue;
- for (i = 0; i < data->info.object_num; i++) {
- object = data->object_table + i;
- if (object->type == type)
- return object;
+ active_touches++;
+ input_report_abs(mxt->touch_input,
+ ABS_MT_TRACKING_ID,
+ i);
+ input_report_abs(mxt->touch_input,
+ ABS_MT_TOUCH_MAJOR,
+ stored_size[i]);
+ input_report_abs(mxt->touch_input,
+ ABS_MT_POSITION_X,
+ stored_x[i]);
+ input_report_abs(mxt->touch_input,
+ ABS_MT_POSITION_Y,
+ stored_y[i]);
+ input_mt_sync(mxt->touch_input);
}
- dev_err(&data->client->dev, "Invalid object type\n");
- return NULL;
+ if (active_touches == 0)
+ input_mt_sync(mxt->touch_input);
+ input_sync(mxt->touch_input);
}
-static int mxt_read_message(struct mxt_data *data,
- struct mxt_message *message)
+static void process_T9_message(u8 *message, struct mxt_data *mxt)
{
- struct mxt_object *object;
- u16 reg;
+ struct input_dev *input;
+ struct device *dev = &mxt->client->dev;
+ u8 status;
+ u16 xpos = 0xFFFF;
+ u16 ypos = 0xFFFF;
+ u8 touch_size = 255;
+ u8 touch_number;
+ u8 amplitude;
+ u8 report_id;
+ input = mxt->touch_input;
+ status = message[MXT_MSG_T9_STATUS];
+ report_id = message[0];
+
+ if (status & MXT_MSGB_T9_SUPPRESS) {
+ /* Touch has been suppressed by grip/face */
+ /* detection */
+ dev_dbg(&mxt->client->dev, "SUPRESS");
+ } else {
+/* FIH { */
+/* Correct coordinate */
+#if 0
+ xpos = message[MXT_MSG_T9_XPOSMSB] * 16 +
+ ((message[MXT_MSG_T9_XYPOSLSB] >> 4) & 0xF);
+ ypos = message[MXT_MSG_T9_YPOSMSB] * 16 +
+ ((message[MXT_MSG_T9_XYPOSLSB] >> 0) & 0xF);
+ printk(KERN_INFO "[TOUCH] X=%d, Y=%d\n", xpos, ypos);
+
+ if (mxt->max_x_val < 1024)
+ xpos = xpos >> 2;
+
+ if (mxt->max_y_val < 1024)
+ ypos = ypos >> 2;
+
+ printk(KERN_INFO "[TOUCH] [X=%d, Y=%d] max_x=%d , max_y=%d\n", xpos, ypos, mxt->max_x_val, mxt->max_y_val);
+#else
+ xpos = message[MXT_MSG_T9_XPOSMSB] * 16 +
+ ((message[MXT_MSG_T9_XYPOSLSB] >> 4) & 0xF);
+ ypos = message[MXT_MSG_T9_YPOSMSB] * 4 +
+ ((message[MXT_MSG_T9_XYPOSLSB] >> 6) & 0x3);
+ printk(KERN_INFO "[TOUCH] X=%d, Y=%d\n", xpos, ypos);
+
+#if 0
+ xpos = xpos*1024*9/12800 + 51;
+ ypos = ypos*1024*9/8000 + 51;
+ printk(KERN_INFO "[TOUCH] [X=%d, Y=%d]\n", xpos, ypos);
+#else
+ xpos = (xpos)*1024/1280;
+ if (xpos > 1024)
+ xpos = 1024;
+ if (xpos < 0)
+ xpos = 0;
+ ypos = (ypos)*1024/800;
+ if (ypos > 1024)
+ ypos = 1024;
+ if (ypos < 0)
+ ypos = 0;
+ printk(KERN_INFO "[TOUCH] [X=%d, Y=%d]\n", xpos, ypos);
+#endif
- object = mxt_get_object(data, MXT_GEN_MESSAGE);
- if (!object)
- return -EINVAL;
+#endif
- reg = object->start_address;
- return __mxt_read_reg(data->client, reg,
- sizeof(struct mxt_message), message);
-}
-static int mxt_read_object(struct mxt_data *data,
- u8 type, u8 offset, u8 *val)
-{
- struct mxt_object *object;
- u16 reg;
- object = mxt_get_object(data, type);
- if (!object)
- return -EINVAL;
- reg = object->start_address;
- return __mxt_read_reg(data->client, reg + offset, 1, val);
-}
+/* FIH } */
-static int mxt_write_object(struct mxt_data *data,
- u8 type, u8 offset, u8 val)
-{
- struct mxt_object *object;
- u16 reg;
+ touch_number = message[MXT_MSG_REPORTID] -
+ mxt->rid_map[report_id].first_rid;
- object = mxt_get_object(data, type);
- if (!object)
- return -EINVAL;
+ stored_x[touch_number] = xpos;
+ stored_y[touch_number] = ypos;
- reg = object->start_address;
- return mxt_write_reg(data->client, reg + offset, val);
-}
+ if (status & MXT_MSGB_T9_DETECT) {
+ /*
+ * TODO: more precise touch size calculation?
+ * mXT224 reports the number of touched nodes,
+ * so the exact value for touch ellipse major
+ * axis length would be 2*sqrt(touch_size/pi)
+ * (assuming round touch shape).
+ */
+ touch_size = message[MXT_MSG_T9_TCHAREA];
+ touch_size = touch_size >> 2;
+ if (!touch_size)
+ touch_size = 1;
+ stored_size[touch_number] = touch_size;
+ if (status & MXT_MSGB_T9_AMP)
+ /* Amplitude of touch has changed */
+ amplitude = message[MXT_MSG_T9_TCHAMPLITUDE];
-static void mxt_input_report(struct mxt_data *data, int single_id)
-{
- struct mxt_finger *finger = data->finger;
- struct input_dev *input_dev = data->input_dev;
- int status = finger[single_id].status;
- int finger_num = 0;
- int id;
-
- for (id = 0; id < MXT_MAX_FINGER; id++) {
- if (!finger[id].status)
- continue;
+ printk(KERN_INFO, "DETECT:%s%s%s%s",
+ ((status & MXT_MSGB_T9_PRESS) ? " PRESS" : ""),
+ ((status & MXT_MSGB_T9_MOVE) ? " MOVE" : ""),
+ ((status & MXT_MSGB_T9_AMP) ? " AMP" : ""),
+ ((status & MXT_MSGB_T9_VECTOR) ? " VECT" : ""));
+ } else if (status & MXT_MSGB_T9_RELEASE) {
+ printk(KERN_INFO, "RELEASE");
- input_mt_slot(input_dev, id);
- input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
- finger[id].status != MXT_RELEASE);
-
- if (finger[id].status != MXT_RELEASE) {
- finger_num++;
- input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
- finger[id].area);
- input_report_abs(input_dev, ABS_MT_POSITION_X,
- finger[id].x);
- input_report_abs(input_dev, ABS_MT_POSITION_Y,
- finger[id].y);
- } else {
- finger[id].status = 0;
+ /* The previously reported touch has been removed.*/
+ stored_size[touch_number] = 0;
}
- }
-
- input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
- if (status != MXT_RELEASE) {
- input_report_abs(input_dev, ABS_X, finger[single_id].x);
- input_report_abs(input_dev, ABS_Y, finger[single_id].y);
+ printk(KERN_INFO, "X=%d, Y=%d, touch number=%d, TOUCHSIZE=%d",
+ xpos, ypos, touch_number, stored_size[touch_number]);
}
-
- input_sync(input_dev);
}
-static void mxt_input_touchevent(struct mxt_data *data,
- struct mxt_message *message, int id)
+void process_key_message(u8 *message, struct mxt_data *mxt)
{
- struct mxt_finger *finger = data->finger;
- struct device *dev = &data->client->dev;
- u8 status = message->message[0];
- int x;
- int y;
- int area;
-
- /* Check the touch is present on the screen */
- if (!(status & MXT_DETECT)) {
- if (status & MXT_RELEASE) {
- dev_dbg(dev, "[%d] released\n", id);
-
- finger[id].status = MXT_RELEASE;
- mxt_input_report(data, id);
+ /*key up*/
+ if (message[1] == 0 && message[2] == 0 && message[3] == 0 && message[4] == 0) {
+ if (mxt->prev_key == 0) {
+ dev_dbg(&mxt->client->dev, "No previous key");
+ } else {
+ input_report_key(mxt->key_input, mxt->prev_key, 0);
+ dev_dbg(&mxt->client->dev,
+ "Report key %d up", mxt->prev_key);
+ mxt->prev_key = 0;
}
+ input_sync(mxt->key_input);
return;
}
- /* Check only AMP detection */
- if (!(status & (MXT_PRESS | MXT_MOVE)))
- return;
-
- x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
- y = (message->message[2] << 4) | ((message->message[3] & 0xf));
- if (data->max_x < 1024)
- x = x >> 2;
- if (data->max_y < 1024)
- y = y >> 2;
-
- area = message->message[4];
-
- dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id,
- status & MXT_MOVE ? "moved" : "pressed",
- x, y, area);
-
- finger[id].status = status & MXT_MOVE ?
- MXT_MOVE : MXT_PRESS;
- finger[id].x = x;
- finger[id].y = y;
- finger[id].area = area;
-
- mxt_input_report(data, id);
+printk(KERN_INFO "message[1]=%d, message[2]=%d, message[3]=%d, message[4]=%d", message[1], message[2], message[3], message[4]);
+
+ /*key down*/
+ if (message[1] == 128 && message[2] == 0 && message[3] == 2 && message[4] == 0) {
+ input_report_key(mxt->key_input, KEY_BACK, 1);
+ dev_dbg(&mxt->client->dev,
+ "Report BACK(%d) key DOWN.", KEY_BACK);
+ mxt->prev_key = KEY_BACK;
+ } else if (message[1] == 128 && message[2] == 0 && message[3] == 4 && message[4] == 0) {
+ input_report_key(mxt->key_input, KEY_MENU, 1);
+ dev_dbg(&mxt->client->dev,
+ "Report MENU(%d) key DOWN.", KEY_MENU);
+ mxt->prev_key = KEY_MENU;
+ } else if (message[1] == 128 && message[2] == 0 && message[3] == 8 && message[4] == 0) {
+ input_report_key(mxt->key_input, KEY_HOME, 1);
+ dev_dbg(&mxt->client->dev,
+ "Report HOME(%d) key DOWN.", KEY_HOME);
+ mxt->prev_key = KEY_HOME;
+ } else if (message[1] == 128 && message[2] == 8 && message[3] == 0 && message[4] == 0) {
+ input_report_key(mxt->key_input, KEY_SEARCH, 1);
+ dev_dbg(&mxt->client->dev,
+ "Report SEARCH(%d) key DOWN.", KEY_SEARCH);
+ mxt->prev_key = KEY_SEARCH;
+ }
+ input_sync(mxt->key_input);
}
-static irqreturn_t mxt_interrupt(int irq, void *dev_id)
+static int process_message(u8 *message, u8 object, struct mxt_data *mxt)
{
- struct mxt_data *data = dev_id;
- struct mxt_message message;
- struct mxt_object *object;
- struct device *dev = &data->client->dev;
- int id;
- u8 reportid;
- u8 max_reportid;
- u8 min_reportid;
-
- do {
- if (mxt_read_message(data, &message)) {
- dev_err(dev, "Failed to read message\n");
- goto end;
+ struct i2c_client *client;
+ u8 status;
+ u16 xpos = 0xFFFF;
+ u16 ypos = 0xFFFF;
+ u8 event;
+ u8 direction;
+ u16 distance;
+ u8 length;
+ u8 report_id;
+ static u8 error_cond;
+ client = mxt->client;
+ length = mxt->message_size;
+ report_id = message[0];
+
+ switch (object) {
+ case MXT_GEN_COMMANDPROCESSOR_T6:
+ status = message[1];
+
+ if (status & MXT_MSGB_T6_COMSERR) {
+ if ((!error_cond) & MXT_MSGB_T6_COMSERR) {
+ dev_err(&client->dev, "maXTouch checksum error\n");
+ error_cond |= MXT_MSGB_T6_COMSERR;
+ }
}
+ if (status & MXT_MSGB_T6_CFGERR) {
+ /*
+ * Configuration error. A proper configuration
+ * needs to be written to chip and backed up. Refer
+ * to protocol document for further info.
+ */
+ if ((!error_cond) & MXT_MSGB_T6_CFGERR) {
+ dev_err(&client->dev, "maXTouch configuration error\n");
+ error_cond |= MXT_MSGB_T6_CFGERR;
+ }
+ }
+ if (status & MXT_MSGB_T6_CAL) {
+ /* Calibration in action, no need to react */
+ dev_info(&client->dev, "maXTouch calibration in progress\n");
+ }
+ if (status & MXT_MSGB_T6_SIGERR) {
+ /*
+ * Signal acquisition error, something is seriously
+ * wrong, not much we can in the driver to correct
+ * this
+ */
+ if ((!error_cond) & MXT_MSGB_T6_SIGERR) {
+ dev_err(&client->dev, "maXTouch acquisition error\n");
+ error_cond |= MXT_MSGB_T6_SIGERR;
+ }
+ }
+ if (status & MXT_MSGB_T6_OFL) {
+ /*
+ * Cycle overflow, the acquisition is too short.
+ * Can happen temporarily when there's a complex
+ * touch shape on the screen requiring lots of
+ * processing.
+ */
+ dev_err(&client->dev, "maXTouch cycle overflow\n");
+ }
+ if (status & MXT_MSGB_T6_RESET) {
+ /* Chip has reseted, no need to react. */
+ dev_info(&client->dev, "maXTouch chip reset\n");
+ }
+ if (status == 0) {
+ /* Chip status back to normal. */
+ dev_info(&client->dev, "maXTouch status normal\n");
+ error_cond = 0;
+ }
+ break;
- reportid = message.reportid;
-
- /* whether reportid is thing of MXT_TOUCH_MULTI */
- object = mxt_get_object(data, MXT_TOUCH_MULTI);
- if (!object)
- goto end;
-
- max_reportid = object->max_reportid;
- min_reportid = max_reportid - object->num_report_ids + 1;
- id = reportid - min_reportid;
+ case MXT_TOUCH_KEYARRAY_T15:
+ dev_dbg(&client->dev, "key value, message[1]=%d, "
+ "message[2]=%d, message[3]=%d, message[4]=%d",
+ message[1], message[2], message[3], message[4]);
+ process_key_message(message, mxt);
+ break;
- if (reportid >= min_reportid && reportid <= max_reportid)
- mxt_input_touchevent(data, &message, id);
- else
- mxt_dump_message(dev, &message);
- } while (reportid != 0xff);
+ case MXT_TOUCH_MULTITOUCHSCREEN_T9:
+ process_T9_message(message, mxt);
+ report_mt(mxt);
+ break;
-end:
- return IRQ_HANDLED;
-}
+ case MXT_SPT_GPIOPWM_T19:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev, "Receiving GPIO message\n");
+ break;
-static int mxt_check_reg_init(struct mxt_data *data)
-{
- const struct mxt_platform_data *pdata = data->pdata;
- struct mxt_object *object;
- struct device *dev = &data->client->dev;
- int index = 0;
- int i, j, config_offset;
-
- if (!pdata->config) {
- dev_dbg(dev, "No cfg data defined, skipping reg init\n");
- return 0;
- }
+ case MXT_PROCI_GRIPFACESUPPRESSION_T20:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev, "Receiving face suppression msg\n");
+ break;
- for (i = 0; i < data->info.object_num; i++) {
- object = data->object_table + i;
+ case MXT_PROCG_NOISESUPPRESSION_T22:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev, "Receiving noise suppression msg\n");
+ status = message[MXT_MSG_T22_STATUS];
+ if (status & MXT_MSGB_T22_FHCHG) {
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev, "maXTouch: Freq changed\n");
+ }
+ if (status & MXT_MSGB_T22_GCAFERR) {
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev, "maXTouch: High noise level\n");
+ }
+ if (status & MXT_MSGB_T22_FHERR) {
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "maXTouch: Freq changed - Noise level too high\n");
+ }
+ break;
- if (!mxt_object_writable(object->type))
- continue;
+ case MXT_PROCI_ONETOUCHGESTUREPROCESSOR_T24:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "Receiving one-touch gesture msg\n");
+
+ event = message[MXT_MSG_T24_STATUS] & 0x0F;
+ xpos = message[MXT_MSG_T24_XPOSMSB] * 16 +
+ ((message[MXT_MSG_T24_XYPOSLSB] >> 4) & 0x0F);
+ ypos = message[MXT_MSG_T24_YPOSMSB] * 16 +
+ ((message[MXT_MSG_T24_XYPOSLSB] >> 0) & 0x0F);
+ xpos >>= 2;
+ ypos >>= 2;
+ direction = message[MXT_MSG_T24_DIR];
+ distance = message[MXT_MSG_T24_DIST] +
+ (message[MXT_MSG_T24_DIST + 1] << 16);
+
+ input_event(mxt->touch_input, EV_MSC, MSC_GESTURE,
+ (event << 24) | (direction << 16) | distance);
+ input_event(mxt->touch_input, EV_MSC, MSC_GESTURE,
+ (xpos << 16) | ypos);
+ break;
- for (j = 0; j < object->size + 1; j++) {
- config_offset = index + j;
- if (config_offset > pdata->config_length) {
- dev_err(dev, "Not enough config data!\n");
- return -EINVAL;
- }
- mxt_write_object(data, object->type, j,
- pdata->config[config_offset]);
+ case MXT_SPT_SELFTEST_T25:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "Receiving Self-Test msg\n");
+
+ if (message[MXT_MSG_T25_STATUS] == MXT_MSGR_T25_RUN_ALL_TESTS) {
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "maXTouch: Self-Test OK\n");
+
+ } else {
+ dev_err(&client->dev,
+ "maXTouch: Self-Test Failed [%02x]:"
+ "{%02x,%02x,%02x,%02x,%02x}\n",
+ message[MXT_MSG_T25_STATUS],
+ message[MXT_MSG_T25_STATUS + 0],
+ message[MXT_MSG_T25_STATUS + 1],
+ message[MXT_MSG_T25_STATUS + 2],
+ message[MXT_MSG_T25_STATUS + 3],
+ message[MXT_MSG_T25_STATUS + 4]
+ );
}
- index += object->size + 1;
+ break;
+
+ case MXT_PROCI_TWOTOUCHGESTUREPROCESSOR_T27:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev, "Receiving 2-touch gesture message\n");
+
+ event = message[MXT_MSG_T27_STATUS] & 0xF0;
+ xpos = message[MXT_MSG_T27_XPOSMSB] * 16 +
+ ((message[MXT_MSG_T27_XYPOSLSB] >> 4) & 0x0F);
+ ypos = message[MXT_MSG_T27_YPOSMSB] * 16 +
+ ((message[MXT_MSG_T27_XYPOSLSB] >> 0) & 0x0F);
+ xpos >>= 2;
+ ypos >>= 2;
+ direction = message[MXT_MSG_T27_ANGLE];
+ distance = message[MXT_MSG_T27_SEPARATION] +
+ (message[MXT_MSG_T27_SEPARATION + 1] << 16);
+
+ input_event(mxt->touch_input, EV_MSC, MSC_GESTURE,
+ (event << 24) | (direction << 16) | distance);
+ input_event(mxt->touch_input, EV_MSC, MSC_GESTURE,
+ (xpos << 16) | ypos);
+ break;
+ case MXT_SPT_CTECONFIG_T28:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev, "Receiving CTE message...\n");
+ status = message[MXT_MSG_T28_STATUS];
+ if (status & MXT_MSGB_T28_CHKERR)
+ dev_err(&client->dev, "maXTouch: Power-Up CRC failure\n");
+ break;
+ default:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev, "maXTouch: Unknown message!\n");
+ break;
}
return 0;
}
-static int mxt_make_highchg(struct mxt_data *data)
+/*
+ * Processes messages when the interrupt line (CHG) is asserted. Keeps
+ * reading messages until a message with report ID 0xFF is received,
+ * which indicates that there is no more new messages.
+ *
+ */
+static void mxt_worker(struct mxt_data *mxt)
{
- struct device *dev = &data->client->dev;
- struct mxt_message message;
- int count = 10;
- int error;
-
- /* Read dummy message to make high CHG pin */
- do {
- error = mxt_read_message(data, &message);
- if (error)
- return error;
- } while (message.reportid != 0xff && --count);
-
- if (!count) {
- dev_err(dev, "CHG pin isn't cleared\n");
- return -EBUSY;
+ struct i2c_client *client;
+ u8 *message;
+ u16 message_length;
+ u16 message_addr;
+ u8 report_id;
+ u8 object;
+ int error;
+ int i;
+ int intr_value;
+
+ client = mxt->client;
+ message = mxt->message_buf;
+ message_addr = mxt->msg_proc_addr;
+ message_length = mxt->message_size;
+
+ if (message_length >= 256) {
+ dev_err(&client->dev, "Message length > 256 bytes not supported\n");
+ return;
}
- return 0;
+ dev_dbg(&client->dev, "maXTouch worker active:\n");
+ do {
+ /* Read next message, reread on failure. */
+ mxt->message_counter++;
+ for (i = 1; i < I2C_RETRY_COUNT; i++) {
+ error = mxt_read_block(client, message_addr,
+ message_length - 1, message);
+ if (error >= 0)
+ break;
+ mxt->read_fail_counter++;
+ dev_err(&client->dev, "Failure reading maxTouch device\n");
+ }
+ if (error < 0)
+ return;
+
+ report_id = message[0];
+
+ if (reg_add > 0)
+ dev_info(
+ &client->dev,
+ "Atmel Debug info message[0]=%d,message[1]=%d,message[2]=%d,message[3]=%d"
+ "message[4]=%d,message[5]=%d,message[6]=%d,message[7]=%d,message_length=%d",
+ message[0], message[1], message[2], message[3],
+ message[4], message[5], message[6], message[7], message_length);
+
+ if ((report_id != MXT_END_OF_MESSAGES) && (report_id != 0)) {
+ /* Get type of object and process the message */
+ object = mxt->rid_map[report_id].object;
+ process_message(message, object, mxt);
+ }
+ /*dev_dbg(&client->dev, "chgline: %d\n", mxt->read_chg());*/
+ intr_value = gpio_get_value(mxt->mxt_intr_gpio);
+ } while (intr_value == 0);
}
-static void mxt_handle_pdata(struct mxt_data *data)
+/*
+ * The maXTouch device will signal the host about a new message by asserting
+ * the CHG line. This ISR schedules a worker routine to read the message when
+ * that happens.
+ */
+static irqreturn_t mxt_irq_handler(int irq, void *_mxt)
{
- const struct mxt_platform_data *pdata = data->pdata;
- u8 voltage;
-
- /* Set touchscreen lines */
- mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_XSIZE,
- pdata->x_line);
- mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_YSIZE,
- pdata->y_line);
-
- /* Set touchscreen orient */
- mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_ORIENT,
- pdata->orient);
-
- /* Set touchscreen burst length */
- mxt_write_object(data, MXT_TOUCH_MULTI,
- MXT_TOUCH_BLEN, pdata->blen);
-
- /* Set touchscreen threshold */
- mxt_write_object(data, MXT_TOUCH_MULTI,
- MXT_TOUCH_TCHTHR, pdata->threshold);
-
- /* Set touchscreen resolution */
- mxt_write_object(data, MXT_TOUCH_MULTI,
- MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff);
- mxt_write_object(data, MXT_TOUCH_MULTI,
- MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8);
- mxt_write_object(data, MXT_TOUCH_MULTI,
- MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff);
- mxt_write_object(data, MXT_TOUCH_MULTI,
- MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8);
-
- /* Set touchscreen voltage */
- if (pdata->voltage) {
- if (pdata->voltage < MXT_VOLTAGE_DEFAULT) {
- voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) /
- MXT_VOLTAGE_STEP;
- voltage = 0xff - voltage + 1;
- } else
- voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) /
- MXT_VOLTAGE_STEP;
-
- mxt_write_object(data, MXT_SPT_CTECONFIG,
- MXT_CTE_VOLTAGE, voltage);
+ struct mxt_data *mxt = _mxt;
+
+ mxt->irq_counter++;
+ if (mxt->valid_interrupt()) {
+ mxt->valid_irq_counter++;
+
+ mutex_lock(&mxt->dev_mutex);
+ mxt_worker(mxt);
+ mutex_unlock(&mxt->dev_mutex);
+ } else {
+ mxt->invalid_irq_counter++;
+ return IRQ_NONE;
}
+
+ return IRQ_HANDLED;
}
-static int mxt_get_info(struct mxt_data *data)
+/******************************************************************************/
+/* Initialization of driver */
+/******************************************************************************/
+
+static int __devinit mxt_identify(struct i2c_client *client,
+ struct mxt_data *mxt,
+ u8 *id_block_data)
{
- struct i2c_client *client = data->client;
- struct mxt_info *info = &data->info;
+ u8 buf[7];
int error;
- u8 val;
+ int identified;
- error = mxt_read_reg(client, MXT_FAMILY_ID, &val);
- if (error)
- return error;
- info->family_id = val;
+ identified = 0;
- error = mxt_read_reg(client, MXT_VARIANT_ID, &val);
- if (error)
- return error;
- info->variant_id = val;
+ /* Read Device info to check if chip is valid */
+ error = mxt_read_block(client, MXT_ADDR_INFO_BLOCK, MXT_ID_BLOCK_SIZE,
+ (u8 *) buf);
- error = mxt_read_reg(client, MXT_VERSION, &val);
- if (error)
- return error;
- info->version = val;
+ if (error < 0) {
+ client->addr = MXT1386_I2C_ADDR2;
+ slave_addr = MXT1386_I2C_ADDR2;
+ printk(KERN_INFO "[TOUCH] switch slave address to 0x%02x", client->addr);
+ error = mxt_read_block(client, MXT_ADDR_INFO_BLOCK, MXT_ID_BLOCK_SIZE, (u8 *) buf);
+ }
- error = mxt_read_reg(client, MXT_BUILD, &val);
- if (error)
- return error;
- info->build = val;
+ if (error < 0) {
+ mxt->read_fail_counter++;
+ dev_err(&client->dev, "Failure accessing maXTouch device\n");
+ return -EIO;
+ }
- error = mxt_read_reg(client, MXT_OBJECT_NUM, &val);
- if (error)
- return error;
- info->object_num = val;
+ memcpy(id_block_data, buf, MXT_ID_BLOCK_SIZE);
+
+ mxt->device_info.family_id = buf[0];
+ mxt->device_info.variant_id = buf[1];
+ mxt->device_info.major = ((buf[2] >> 4) & 0x0F);
+ mxt->device_info.minor = (buf[2] & 0x0F);
+ mxt->device_info.build = buf[3];
+ mxt->device_info.x_size = buf[4];
+ mxt->device_info.y_size = buf[5];
+ mxt->device_info.num_objs = buf[6];
+ mxt->device_info.num_nodes = mxt->device_info.x_size *
+ mxt->device_info.y_size;
+
+ /*
+ * Check Family & Variant Info; warn if not recognized but
+ * still continue.
+ */
+
+ /* MXT224 */
+ if (mxt->device_info.family_id == MXT224_FAMILYID) {
+ strcpy(mxt->device_info.family_name, "atmel_mxt224");
+
+ if (mxt->device_info.variant_id == MXT224_CAL_VARIANTID) {
+ strcpy(mxt->device_info.variant_name, "Calibrated");
+ } else if (mxt->device_info.variant_id ==
+ MXT224_UNCAL_VARIANTID) {
+ strcpy(mxt->device_info.variant_name, "Uncalibrated");
+ } else {
+ dev_err(&client->dev,
+ "Warning: maXTouch Variant ID [%d] not supported\n",
+ mxt->device_info.variant_id);
+ strcpy(mxt->device_info.variant_name, "UNKNOWN");
+ /* identified = -ENXIO; */
+ }
- return 0;
-}
+ /* MXT1386 */
+ } else if (mxt->device_info.family_id == MXT1386_FAMILYID) {
+/* FIH { */
+#if 0
+ strcpy(mxt->device_info.family_name, "mXT1386");
+#else
+ strcpy(mxt->device_info.family_name, "mxt1386");
+#endif
+/* FIH } */
-static int mxt_get_object_table(struct mxt_data *data)
-{
- int error;
- int i;
- u16 reg;
- u8 reportid = 0;
- u8 buf[MXT_OBJECT_SIZE];
-
- for (i = 0; i < data->info.object_num; i++) {
- struct mxt_object *object = data->object_table + i;
-
- reg = MXT_OBJECT_START + MXT_OBJECT_SIZE * i;
- error = mxt_read_object_table(data->client, reg, buf);
- if (error)
- return error;
-
- object->type = buf[0];
- object->start_address = (buf[2] << 8) | buf[1];
- object->size = buf[3];
- object->instances = buf[4];
- object->num_report_ids = buf[5];
-
- if (object->num_report_ids) {
- reportid += object->num_report_ids *
- (object->instances + 1);
- object->max_reportid = reportid;
+ if (mxt->device_info.variant_id == MXT1386_CAL_VARIANTID) {
+ strcpy(mxt->device_info.variant_name, "Calibrated");
+ } else {
+ dev_err(&client->dev,
+ "Warning: maXTouch Variant ID [%d] not "
+ "supported\n",
+ mxt->device_info.variant_id);
+ strcpy(mxt->device_info.variant_name, "UNKNOWN");
+ /* identified = -ENXIO; */
}
+ /* Unknown family ID! */
+ } else {
+ dev_err(&client->dev,
+ "Warning: maXTouch Family ID [%d] not supported\n",
+ mxt->device_info.family_id);
+ strcpy(mxt->device_info.family_name, "UNKNOWN");
+ strcpy(mxt->device_info.variant_name, "UNKNOWN");
+ /* identified = -ENXIO; */
}
- return 0;
+ dev_info(
+ &client->dev,
+ "Atmel maXTouch (Family %s (%X), Variant %s (%X)) Firmware "
+ "version [%d.%d] Build %d\n",
+ mxt->device_info.family_name,
+ mxt->device_info.family_id,
+ mxt->device_info.variant_name,
+ mxt->device_info.variant_id,
+ mxt->device_info.major,
+ mxt->device_info.minor,
+ mxt->device_info.build
+ );
+ dev_info(
+ &client->dev,
+ "Atmel maXTouch Configuration "
+ "[X: %d] x [Y: %d]\n",
+ mxt->device_info.x_size,
+ mxt->device_info.y_size
+ );
+ return identified;
}
-static int mxt_initialize(struct mxt_data *data)
+/*
+ * Reads the object table from maXTouch chip to get object data like
+ * address, size, report id. For Info Block CRC calculation, already read
+ * id data is passed to this function too (Info Block consists of the ID
+ * block and object table).
+ *
+ */
+static int __devinit mxt_read_object_table(struct i2c_client *client,
+ struct mxt_data *mxt,
+ u8 *raw_id_data)
{
- struct i2c_client *client = data->client;
- struct mxt_info *info = &data->info;
- int error;
- u8 val;
-
- error = mxt_get_info(data);
- if (error)
- return error;
-
- data->object_table = kcalloc(info->object_num,
- sizeof(struct mxt_object),
- GFP_KERNEL);
- if (!data->object_table) {
- dev_err(&client->dev, "Failed to allocate memory\n");
- return -ENOMEM;
+ u16 report_id_count;
+ u8 buf[MXT_OBJECT_TABLE_ELEMENT_SIZE];
+ u8 *raw_ib_data;
+ u8 object_type;
+ u16 object_address;
+ u16 object_size;
+ u8 object_instances;
+ u8 object_report_ids;
+ u16 object_info_address;
+ u32 crc;
+ u32 calculated_crc;
+ int i;
+ int error;
+
+ u8 object_instance;
+ u8 object_report_id;
+ u8 report_id;
+ int first_report_id;
+ int ib_pointer;
+ struct mxt_object *object_table;
+ dev_dbg(&mxt->client->dev, "maXTouch driver reading configuration\n");
+
+ object_table = kzalloc(sizeof(struct mxt_object) *
+ mxt->device_info.num_objs,
+ GFP_KERNEL);
+ if (object_table == NULL) {
+ printk(KERN_WARNING "maXTouch: Memory allocation failed!\n");
+ error = -ENOMEM;
+ goto err_object_table_alloc;
}
- /* Get object table information */
- error = mxt_get_object_table(data);
- if (error)
- return error;
-
- /* Check register init values */
- error = mxt_check_reg_init(data);
- if (error)
- return error;
-
- mxt_handle_pdata(data);
-
- /* Backup to memory */
- mxt_write_object(data, MXT_GEN_COMMAND,
- MXT_COMMAND_BACKUPNV,
- MXT_BACKUP_VALUE);
- msleep(MXT_BACKUP_TIME);
-
- /* Soft reset */
- mxt_write_object(data, MXT_GEN_COMMAND,
- MXT_COMMAND_RESET, 1);
- msleep(MXT_RESET_TIME);
-
- /* Update matrix size at info struct */
- error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val);
- if (error)
- return error;
- info->matrix_xsize = val;
-
- error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val);
- if (error)
- return error;
- info->matrix_ysize = val;
-
- dev_info(&client->dev,
- "Family ID: %d Variant ID: %d Version: %d Build: %d\n",
- info->family_id, info->variant_id, info->version,
- info->build);
-
- dev_info(&client->dev,
- "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n",
- info->matrix_xsize, info->matrix_ysize,
- info->object_num);
+ raw_ib_data = kmalloc(MXT_OBJECT_TABLE_ELEMENT_SIZE *
+ mxt->device_info.num_objs + MXT_ID_BLOCK_SIZE,
+ GFP_KERNEL);
+ if (raw_ib_data == NULL) {
+ printk(KERN_WARNING "maXTouch: Memory allocation failed!\n");
+ error = -ENOMEM;
+ goto err_ib_alloc;
+ }
- return 0;
-}
+ /* Copy the ID data for CRC calculation. */
+ memcpy(raw_ib_data, raw_id_data, MXT_ID_BLOCK_SIZE);
+ ib_pointer = MXT_ID_BLOCK_SIZE;
-static void mxt_calc_resolution(struct mxt_data *data)
-{
- unsigned int max_x = data->pdata->x_size - 1;
- unsigned int max_y = data->pdata->y_size - 1;
+ mxt->object_table = object_table;
- if (data->pdata->orient & MXT_XY_SWITCH) {
- data->max_x = max_y;
- data->max_y = max_x;
- } else {
- data->max_x = max_x;
- data->max_y = max_y;
- }
-}
+ dev_dbg(&mxt->client->dev, "maXTouch driver Memory allocated\n");
-static ssize_t mxt_object_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mxt_data *data = dev_get_drvdata(dev);
- struct mxt_object *object;
- int count = 0;
- int i, j;
- int error;
- u8 val;
+ object_info_address = MXT_ADDR_OBJECT_TABLE;
- for (i = 0; i < data->info.object_num; i++) {
- object = data->object_table + i;
+ report_id_count = 0;
+ for (i = 0; i < mxt->device_info.num_objs; i++) {
+ dev_dbg(&mxt->client->dev, "Reading maXTouch at [0x%04x]: ",
+ object_info_address);
- count += sprintf(buf + count,
- "Object Table Element %d(Type %d)\n",
- i + 1, object->type);
+ error = mxt_read_block(client, object_info_address,
+ MXT_OBJECT_TABLE_ELEMENT_SIZE, buf);
- if (!mxt_object_readable(object->type)) {
- count += sprintf(buf + count, "\n");
- continue;
+ if (error < 0) {
+ mxt->read_fail_counter++;
+ dev_err(&client->dev,
+ "maXTouch Object %d could not be read\n", i);
+ error = -EIO;
+ goto err_object_read;
}
- for (j = 0; j < object->size + 1; j++) {
- error = mxt_read_object(data,
- object->type, j, &val);
- if (error)
- return error;
-
- count += sprintf(buf + count,
- " Byte %d: 0x%x (%d)\n", j, val, val);
+ memcpy(raw_ib_data + ib_pointer, buf,
+ MXT_OBJECT_TABLE_ELEMENT_SIZE);
+ ib_pointer += MXT_OBJECT_TABLE_ELEMENT_SIZE;
+
+ object_type = buf[0];
+ object_address = (buf[2] << 8) + buf[1];
+ object_size = buf[3] + 1;
+ object_instances = buf[4] + 1;
+ object_report_ids = buf[5];
+ dev_dbg(&mxt->client->dev, "Type=%03d, Address=0x%04x, "
+ "Size=0x%02x, %d instances, %d report id's\n",
+ object_type,
+ object_address,
+ object_size,
+ object_instances,
+ object_report_ids
+ );
+
+ /* TODO: check whether object is known and supported? */
+
+ /* Save frequently needed info. */
+ if (object_type == MXT_GEN_MESSAGEPROCESSOR_T5) {
+ mxt->msg_proc_addr = object_address;
+ mxt->message_size = object_size;
}
- count += sprintf(buf + count, "\n");
- }
+ object_table[i].type = object_type;
+ object_table[i].chip_addr = object_address;
+ object_table[i].size = object_size;
+ object_table[i].instances = object_instances;
+ object_table[i].num_report_ids = object_report_ids;
+ report_id_count += object_instances * object_report_ids;
- return count;
-}
-
-static int mxt_load_fw(struct device *dev, const char *fn)
-{
- struct mxt_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- const struct firmware *fw = NULL;
- unsigned int frame_size;
- unsigned int pos = 0;
- int ret;
-
- ret = request_firmware(&fw, fn, dev);
- if (ret) {
- dev_err(dev, "Unable to open firmware %s\n", fn);
- return ret;
+ object_info_address += MXT_OBJECT_TABLE_ELEMENT_SIZE;
}
- /* Change to the bootloader mode */
- mxt_write_object(data, MXT_GEN_COMMAND,
- MXT_COMMAND_RESET, MXT_BOOT_VALUE);
- msleep(MXT_RESET_TIME);
-
- /* Change to slave address of bootloader */
- if (client->addr == MXT_APP_LOW)
- client->addr = MXT_BOOT_LOW;
- else
- client->addr = MXT_BOOT_HIGH;
+ mxt->rid_map =
+ kzalloc(sizeof(struct report_id_map) * (report_id_count + 1),
+ /* allocate for report_id 0, even if not used */
+ GFP_KERNEL);
+ if (mxt->rid_map == NULL) {
+ printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
+ error = -ENOMEM;
+ goto err_rid_map_alloc;
+ }
- ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD);
- if (ret)
- goto out;
+ mxt->message_buf = kmalloc(256, GFP_KERNEL);
+ if (mxt->message_buf == NULL) {
+ printk(KERN_WARNING "Error allocating memory\n");
+ error = -ENOMEM;
+ goto err_msg_alloc3;
+ }
- /* Unlock bootloader */
- mxt_unlock_bootloader(client);
+ mxt->report_id_count = report_id_count;
+ if (report_id_count > 254) { /* 0 & 255 are reserved */
+ dev_err(&client->dev,
+ "Too many maXTouch report id's [%d]\n",
+ report_id_count);
+ error = -ENXIO;
+ goto err_max_rid;
+ }
- while (pos < fw->size) {
- ret = mxt_check_bootloader(client,
- MXT_WAITING_FRAME_DATA);
- if (ret)
- goto out;
+ /* Create a mapping from report id to object type */
+ report_id = 1; /* Start from 1, 0 is reserved. */
+
+ /* Create table associating report id's with objects & instances */
+ for (i = 0; i < mxt->device_info.num_objs; i++) {
+ for (object_instance = 0;
+ object_instance < object_table[i].instances;
+ object_instance++) {
+ first_report_id = report_id;
+ for (object_report_id = 0;
+ object_report_id < object_table[i].num_report_ids;
+ object_report_id++) {
+ mxt->rid_map[report_id].object =
+ object_table[i].type;
+ mxt->rid_map[report_id].instance =
+ object_instance;
+ mxt->rid_map[report_id].first_rid =
+ first_report_id;
+ report_id++;
+ }
+ }
+ }
- frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
+ /* Read 3 byte CRC */
+ error = mxt_read_block(client, object_info_address, 3, buf);
+ if (error < 0) {
+ mxt->read_fail_counter++;
+ dev_err(&client->dev, "Error reading CRC\n");
+ }
- /* We should add 2 at frame size as the the firmware data is not
- * included the CRC bytes.
- */
- frame_size += 2;
+ crc = (buf[2] << 16) | (buf[1] << 8) | buf[0];
- /* Write one frame to device */
- mxt_fw_write(client, fw->data + pos, frame_size);
+ if (calculate_infoblock_crc(&calculated_crc, raw_ib_data,
+ ib_pointer)) {
+ printk(KERN_WARNING "Error while calculating CRC!\n");
+ calculated_crc = 0;
+ }
+ kfree(raw_ib_data);
- ret = mxt_check_bootloader(client,
- MXT_FRAME_CRC_PASS);
- if (ret)
- goto out;
+ dev_dbg(&mxt->client->dev, "\nReported info block CRC = 0x%6X\n", crc);
+ dev_dbg(&mxt->client->dev, "Calculated info block CRC = 0x%6X\n\n",
+ calculated_crc);
- pos += frame_size;
+ if (crc == calculated_crc) {
+ mxt->info_block_crc = crc;
+ } else {
+ mxt->info_block_crc = 0;
+ pr_err("maXTouch: Info block CRC invalid!\n");
+ }
- dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size);
+ if (debug >= DEBUG_VERBOSE) {
+
+ dev_info(&client->dev, "maXTouch: %d Objects\n",
+ mxt->device_info.num_objs);
+
+ for (i = 0; i < mxt->device_info.num_objs; i++) {
+ dev_info(&client->dev, "Type:\t\t\t[%d]: %s\n",
+ object_table[i].type,
+ obj_typ_name[object_table[i].type]);
+ dev_info(&client->dev, "\tAddress:\t0x%04X\n",
+ object_table[i].chip_addr);
+ dev_info(&client->dev, "\tSize:\t\t%d Bytes\n",
+ object_table[i].size);
+ dev_info(&client->dev, "\tInstances:\t%d\n",
+ object_table[i].instances);
+ dev_info(&client->dev, "\tReport Id's:\t%d\n",
+ object_table[i].num_report_ids);
+ }
}
-out:
- release_firmware(fw);
+ return 0;
+err_msg_alloc3:
+ kfree(mxt->message_buf);
+err_max_rid:
+ kfree(mxt->rid_map);
+err_rid_map_alloc:
+err_object_read:
+ kfree(raw_ib_data);
+err_ib_alloc:
+ kfree(object_table);
+err_object_table_alloc:
+ return error;
+}
- /* Change to slave address of application */
- if (client->addr == MXT_BOOT_LOW)
- client->addr = MXT_APP_LOW;
- else
- client->addr = MXT_APP_HIGH;
- return ret;
+static u8 mxt_valid_interrupt_dummy(void)
+{
+ return 1;
}
-static ssize_t mxt_update_fw_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static int __devinit mxt_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- struct mxt_data *data = dev_get_drvdata(dev);
- int error;
+ struct mxt_data *mxt;
+ struct mxt_platform_data *pdata;
+ struct input_dev *touch_input;
+ struct input_dev *key_input;
+ u8 *id_data;
+ int error, gpio_intr, i;
+ u16 base_addr;
+
+ pr_info("atmel_mxt224: mxt_probe\n");
+
+ if (client == NULL) {
+ pr_debug("maXTouch: client == NULL\n");
+ return -EINVAL;
+ } else if (client->adapter == NULL) {
+ pr_debug("maXTouch: client->adapter == NULL\n");
+ return -EINVAL;
+ } else if (&client->dev == NULL) {
+ pr_debug("maXTouch: client->dev == NULL\n");
+ return -EINVAL;
+ } else if (&client->adapter->dev == NULL) {
+ pr_debug("maXTouch: client->adapter->dev == NULL\n");
+ return -EINVAL;
+ } else if (id == NULL) {
+ pr_debug("maXTouch: id == NULL\n");
+ return -EINVAL;
+ }
- disable_irq(data->irq);
+ dev_dbg(&client->dev, "maXTouch driver v. %s\n", DRIVER_VERSION);
+ dev_dbg(&client->dev, "\t \"%s\"\n", client->name);
+ dev_dbg(&client->dev, "\taddr:\t0x%04x\n", client->addr);
+ dev_dbg(&client->dev, "\tirq:\t%d\n", client->irq);
+ dev_dbg(&client->dev, "\tflags:\t0x%04x\n", client->flags);
+ dev_dbg(&client->dev, "\tadapter:\"%s\"\n", client->adapter->name);
+ dev_dbg(&client->dev, "\tdevice:\t\"%s\"\n", client->dev.init_name);
+
+ /* Check if the I2C bus supports BYTE transfer */
+ error = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE);
+ if (!error) {
+ dev_err(&client->dev, "%s adapter not supported\n",
+ dev_driver_string(&client->adapter->dev));
+ return -ENODEV;
+ }
- error = mxt_load_fw(dev, MXT_FW_NAME);
- if (error) {
- dev_err(dev, "The firmware update failed(%d)\n", error);
- count = error;
- } else {
- dev_dbg(dev, "The firmware update succeeded\n");
+ dev_dbg(&client->dev, "maXTouch driver functionality OK\n");
- /* Wait for reset */
- msleep(MXT_FWRESET_TIME);
+ /* Allocate structure - we need it to identify device */
+ mxt = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
+ if (mxt == NULL) {
+ dev_err(&client->dev, "insufficient memory\n");
+ error = -ENOMEM;
+ goto err_mxt_alloc;
+ }
- kfree(data->object_table);
- data->object_table = NULL;
+ id_data = kmalloc(MXT_ID_BLOCK_SIZE, GFP_KERNEL);
+ if (id_data == NULL) {
+ dev_err(&client->dev, "insufficient memory\n");
+ error = -ENOMEM;
+ goto err_id_alloc;
+ }
- mxt_initialize(data);
+ touch_input = input_allocate_device();
+ if (!touch_input) {
+ dev_err(&client->dev, "error allocating touch input device\n");
+ error = -ENOMEM;
+ goto err_touch_input_dev_alloc;
}
- enable_irq(data->irq);
+ key_input = input_allocate_device();
+ if (!key_input) {
+ dev_err(&client->dev, "error allocating key input device");
+ error = -ENOMEM;
+ goto err_key_input_dev_alloc;
+ }
- error = mxt_make_highchg(data);
- if (error)
- return error;
+ /* Initialize Platform data */
+ pdata = client->dev.platform_data;
+ if (pdata == NULL) {
+ dev_err(&client->dev, "platform data is required!\n");
+ error = -EINVAL;
+ goto err_pdata;
+ }
+ if (debug >= DEBUG_TRACE)
+ printk(KERN_INFO "Platform OK: pdata = 0x%08x\n",
+ (unsigned int) pdata);
+
+ mutex_init(&mxt->dev_mutex);
+ mxt->read_fail_counter = 0;
+ mxt->message_counter = 0;
+ mxt->max_x_val = pdata->max_x;
+ mxt->max_y_val = pdata->max_y;
+ mxt->orientation = pdata->orientation;
+ mxt->mxt_intr_gpio = pdata->irq;
+ mxt->mxt_reset_gpio = pdata->reset;
+
+ error = gpio_request(mxt->mxt_intr_gpio, 0);
+ if (error < 0)
+ pr_err("Failed to request GPIO%d (MaxTouch-interrupt) error=%d\n",
+ mxt->mxt_intr_gpio, error);
+
+ error = gpio_direction_input(mxt->mxt_intr_gpio);
+ if (error) {
+ pr_err("Failed to set interrupt direction, error=%d\n", error);
+ gpio_free(mxt->mxt_intr_gpio);
+ }
- return count;
-}
+ error = gpio_request(mxt->mxt_reset_gpio, "MaxTouch-reset");
+ if (error < 0)
+ pr_err("Failed to request GPIO%d (MaxTouch-reset) error=%d\n",
+ mxt->mxt_reset_gpio, error);
-static DEVICE_ATTR(object, 0444, mxt_object_show, NULL);
-static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store);
+ error = gpio_direction_output(mxt->mxt_reset_gpio, 1);
+ if (error) {
+ pr_info("Failed to set reset direction, error=%d\n", error);
+ gpio_free(mxt->mxt_reset_gpio);
+ }
-static struct attribute *mxt_attrs[] = {
- &dev_attr_object.attr,
- &dev_attr_update_fw.attr,
- NULL
-};
+ /* maXTouch wants 40mSec minimum after reset to get organized */
+ gpio_set_value(mxt->mxt_reset_gpio, 1);
+ msleep(40);
+/* FIH { */
+/* Wait for interrupt and delay for unstable reset status */
+#if 1
+{
+ int i = 0;
-static const struct attribute_group mxt_attr_group = {
- .attrs = mxt_attrs,
-};
+ for (i = 0; i < 10; i++) {
+ printk(KERN_INFO "Polling GPIO%d (MaxTouch-intr). retry=%d\n", mxt->mxt_intr_gpio, i);
+ if (gpio_get_value(mxt->mxt_intr_gpio) == LOW)
+ break;
+ msleep(50);
+ }
-static void mxt_start(struct mxt_data *data)
-{
- /* Touch enable */
- mxt_write_object(data,
- MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0x83);
+ /* msleep(10000); */
}
+#endif
+/* FIH } */
-static void mxt_stop(struct mxt_data *data)
-{
- /* Touch disable */
- mxt_write_object(data,
- MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0);
-}
+ /* Get data that is defined in board specific code. */
+ mxt->init_hw = pdata->init_platform_hw;
+ mxt->exit_hw = pdata->exit_platform_hw;
+ mxt->read_chg = pdata->read_chg;
-static int mxt_input_open(struct input_dev *dev)
-{
- struct mxt_data *data = input_get_drvdata(dev);
+ if (pdata->valid_interrupt != NULL)
+ mxt->valid_interrupt = pdata->valid_interrupt;
+ else
+ mxt->valid_interrupt = mxt_valid_interrupt_dummy;
- mxt_start(data);
+ if (mxt->init_hw != NULL)
+ mxt->init_hw();
- return 0;
-}
+ if (debug >= DEBUG_TRACE)
+ printk(KERN_INFO "maXTouch driver identifying chip\n");
-static void mxt_input_close(struct input_dev *dev)
-{
- struct mxt_data *data = input_get_drvdata(dev);
+ if (mxt_identify(client, mxt, id_data) < 0) {
+ dev_err(&client->dev, "Chip could not be identified\n");
+ error = -ENODEV;
+ goto err_identify;
+ }
+ /* Chip is valid and active. */
+ if (debug >= DEBUG_TRACE)
+ printk(KERN_INFO "maXTouch driver allocating input device\n");
+
+ mxt->client = client;
+ mxt->touch_input = touch_input;
+ mxt->key_input = key_input;
+
+ snprintf(mxt->touch_phys_name, sizeof(mxt->touch_phys_name),
+ "%s/input0", dev_name(&client->dev));
+ snprintf(mxt->key_phys_name, sizeof(mxt->key_phys_name),
+ "%s/input1", dev_name(&client->dev));
+
+ /* Touch input parameter */
+ touch_input->name = "mxt224_touchscreen_0";
+ touch_input->phys = mxt->touch_phys_name;
+ touch_input->id.bustype = BUS_I2C;
+ touch_input->dev.parent = &client->dev;
+
+ /* Key input parameter */
+ key_input->name = "mxt224_key_0";
+ key_input->phys = mxt->key_phys_name;
+
+ /* Multitouch */
+ input_set_abs_params(touch_input, ABS_MT_POSITION_X, TS_MIN_X, TS_MAX_X, 0, 0);
+ input_set_abs_params(touch_input, ABS_MT_POSITION_Y, TS_MIN_Y, TS_MAX_Y, 0, 0);
+ input_set_abs_params(touch_input, ABS_MT_TOUCH_MAJOR, 0, MXT_MAX_TOUCH_SIZE,
+ 0, 0);
+ input_set_abs_params(touch_input, ABS_MT_TRACKING_ID, 0,
+ MXT_MAX_NUM_TOUCHES, 0, 0);
+
+ __set_bit(EV_ABS, touch_input->evbit);
+ __set_bit(EV_SYN, touch_input->evbit);
+
+ /* Function key*/
+ __set_bit(EV_KEY, key_input->evbit);
+ __set_bit(KEY_HOME, key_input->keybit);
+ __set_bit(KEY_MENU, key_input->keybit);
+ __set_bit(KEY_BACK, key_input->keybit);
+ __set_bit(KEY_SEARCH, key_input->keybit);
+
+ i2c_set_clientdata(client, mxt);
+ input_set_drvdata(touch_input, mxt);
+ input_set_drvdata(key_input, mxt);
+
+ error = input_register_device(mxt->touch_input);
+ if (error < 0) {
+ dev_err(&client->dev, "Failed to register touch input device\n");
+ goto err_register_touch_device;
+ }
- mxt_stop(data);
-}
+ error = input_register_device(mxt->key_input);
+ if (error < 0) {
+ dev_err(&client->dev, "Failed to register key input device in mxt224\n");
+ goto err_register_key_device;
+ }
-static int __devinit mxt_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- const struct mxt_platform_data *pdata = client->dev.platform_data;
- struct mxt_data *data;
- struct input_dev *input_dev;
- int error;
+ mxt->message_buf = NULL;
+ error = mxt_read_object_table(client, mxt, id_data);
+ if (error < 0)
+ goto err_read_ot;
+
+ BackupNVM(mxt);
+ msleep(40);
+
+ mxt->prev_key = 0;
+
+ if (pdata->numtouch)
+ mxt->numtouch = pdata->numtouch;
+ mxt->irq = gpio_to_irq(mxt->mxt_intr_gpio);
+ mxt->valid_irq_counter = 0;
+ mxt->invalid_irq_counter = 0;
+ mxt->irq_counter = 0;
+
+ if (mxt->irq) {
+ error = request_threaded_irq(mxt->irq, NULL,
+ mxt_irq_handler,
+ IRQF_TRIGGER_FALLING,
+ client->dev.driver->name,
+ mxt);
+ if (error < 0) {
+ dev_err(&client->dev,
+ "failed to allocate irq %d\n", mxt->irq);
+ goto err_irq;
+ }
+ }
- if (!pdata)
- return -EINVAL;
+ mutex_lock(&mxt->dev_mutex);
+ gpio_intr = gpio_get_value(mxt->mxt_intr_gpio);
+ while (gpio_intr == 0) {
+ for (i = 1; i < I2C_RETRY_COUNT; i++) {
+ error = mxt_read_block(mxt->client, mxt->msg_proc_addr,
+ mxt->message_size, mxt->message_buf);
+ if (error >= 0)
+ break;
+ dev_info(&client->dev, "flush the buffer failed in the probe function\n");
- data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!data || !input_dev) {
- dev_err(&client->dev, "Failed to allocate memory\n");
- error = -ENOMEM;
- goto err_free_mem;
- }
+ }
+ gpio_intr = gpio_get_value(mxt->mxt_intr_gpio);
- input_dev->name = "Atmel maXTouch Touchscreen";
- input_dev->id.bustype = BUS_I2C;
- input_dev->dev.parent = &client->dev;
- input_dev->open = mxt_input_open;
- input_dev->close = mxt_input_close;
-
- data->client = client;
- data->input_dev = input_dev;
- data->pdata = pdata;
- data->irq = client->irq;
-
- mxt_calc_resolution(data);
-
- __set_bit(EV_ABS, input_dev->evbit);
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(BTN_TOUCH, input_dev->keybit);
-
- /* For single touch */
- input_set_abs_params(input_dev, ABS_X,
- 0, data->max_x, 0, 0);
- input_set_abs_params(input_dev, ABS_Y,
- 0, data->max_y, 0, 0);
-
- /* For multi touch */
- input_mt_init_slots(input_dev, MXT_MAX_FINGER);
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
- 0, MXT_MAX_AREA, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_X,
- 0, data->max_x, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
- 0, data->max_y, 0, 0);
-
- input_set_drvdata(input_dev, data);
- i2c_set_clientdata(client, data);
-
- error = mxt_initialize(data);
- if (error)
- goto err_free_object;
-
- error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
- pdata->irqflags, client->dev.driver->name, data);
- if (error) {
- dev_err(&client->dev, "Failed to register interrupt\n");
- goto err_free_object;
+ }
+ mutex_unlock(&mxt->dev_mutex);
+
+ /* Store the number of multi-touches allowed, default was 0. */
+ base_addr = get_object_address(MXT_TOUCH_MULTITOUCHSCREEN_T9,
+ 0, mxt->object_table, mxt->device_info.num_objs);
+ base_addr += MXT_ADR_T9_NUMTOUCH;
+
+ error = mxt_write_byte(mxt->client, base_addr, mxt->numtouch);
+ if (error < 0) {
+ dev_err(&client->dev,
+ "Multi-touch init failure, T9 object, error = %d\n",
+ error);
+ goto err_irq;
}
- error = mxt_make_highchg(data);
- if (error)
- goto err_free_irq;
+ kfree(id_data);
- error = input_register_device(input_dev);
- if (error)
- goto err_free_irq;
+#ifdef CONFIG_DEBUG_FS
+ error = intel_mid_add_debugfs();
+ if (error < 0)
+ printk(KERN_ERR "failed to initialize intel_mid debugfs\n");
- error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
- if (error)
- goto err_unregister_device;
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ mxt->suspended = false;
+ mxt->T7[0] = 32;
+ mxt->T7[1] = 15;
+ mxt->T7[2] = 50;
+ mxt->es.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ mxt->es.suspend = mxt_early_suspend;
+ mxt->es.resume = mxt_late_resume;
+ register_early_suspend(&mxt->es);
+ mxt_es = mxt;
+#endif
return 0;
-err_unregister_device:
- input_unregister_device(input_dev);
- input_dev = NULL;
-err_free_irq:
- free_irq(client->irq, data);
-err_free_object:
- kfree(data->object_table);
-err_free_mem:
- input_free_device(input_dev);
- kfree(data);
+err_irq:
+ kfree(mxt->rid_map);
+ kfree(mxt->object_table);
+err_read_ot:
+err_register_key_device:
+err_register_touch_device:
+err_identify:
+err_pdata:
+ input_free_device(key_input);
+err_key_input_dev_alloc:
+ input_free_device(touch_input);
+err_touch_input_dev_alloc:
+ kfree(id_data);
+err_id_alloc:
+ if (mxt->exit_hw != NULL)
+ mxt->exit_hw();
+ kfree(mxt);
+err_mxt_alloc:
return error;
}
static int __devexit mxt_remove(struct i2c_client *client)
{
- struct mxt_data *data = i2c_get_clientdata(client);
+ struct mxt_data *mxt;
+
+ mxt = i2c_get_clientdata(client);
+
+ if (mxt != NULL) {
+ if (mxt->exit_hw != NULL)
+ mxt->exit_hw();
+
+ if (mxt->irq)
+ free_irq(mxt->irq, mxt);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&mxt->es);
+#endif
+ input_unregister_device(mxt->key_input);
+ input_unregister_device(mxt->touch_input);
+ kfree(mxt->rid_map);
+ kfree(mxt->object_table);
+ kfree(mxt->message_buf);
+ }
+ kfree(mxt);
- sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
- free_irq(data->irq, data);
- input_unregister_device(data->input_dev);
- kfree(data->object_table);
- kfree(data);
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev, "Touchscreen unregistered\n");
return 0;
}
-#ifdef CONFIG_PM
+#if defined(CONFIG_PM)
static int mxt_suspend(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct mxt_data *data = i2c_get_clientdata(client);
- struct input_dev *input_dev = data->input_dev;
-
- mutex_lock(&input_dev->mutex);
+ struct mxt_data *mxt = dev_get_drvdata(dev);
- if (input_dev->users)
- mxt_stop(data);
+ dev_dbg(dev, "In function %s", __func__);
- mutex_unlock(&input_dev->mutex);
+ if (device_may_wakeup(dev))
+ enable_irq_wake(mxt->irq);
return 0;
}
static int mxt_resume(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct mxt_data *data = i2c_get_clientdata(client);
- struct input_dev *input_dev = data->input_dev;
+ struct mxt_data *mxt = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "In function %s", __func__);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(mxt->irq);
+
+ return 0;
+}
+#else
+#define mxt_suspend NULL
+#define mxt_resume NULL
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+void mxt_early_suspend(struct early_suspend *h)
+{
+ u16 pwcfg_addr;
+ u8 buf[3] = {0, 0, 0};
+ u8 err;
- /* Soft reset */
- mxt_write_object(data, MXT_GEN_COMMAND,
- MXT_COMMAND_RESET, 1);
+ disable_irq(mxt_es->irq);
- msleep(MXT_RESET_TIME);
+ mutex_lock(&mxt_es->dev_mutex);
- mutex_lock(&input_dev->mutex);
- if (input_dev->users)
- mxt_start(data);
+ pwcfg_addr = get_object_address(MXT_GEN_POWERCONFIG_T7,
+ 0, mxt_es->object_table,
+ mxt_es->device_info.num_objs);
+ mxt_read_block(mxt_es->client, pwcfg_addr, 3, mxt_es->T7);
+ err = mxt_write_block(mxt_es->client, pwcfg_addr, 3, buf);
+ if (err > 0)
+ dev_info(&mxt_es->client->dev, "Driver enter deep sleep mode.");
+ else
+ dev_info(&mxt_es->client->dev,
+ "Driver can't enter deep sleep mode [%d].", err);
+ /* clear touch state when suspending */
+ memset(stored_size, 0, mxt_es->numtouch * sizeof(stored_size[0]));
+ report_mt(mxt_es);
- mutex_unlock(&input_dev->mutex);
+ mxt_es->suspended = true;
- return 0;
+ mutex_unlock(&mxt_es->dev_mutex);
}
+void mxt_late_resume(struct early_suspend *h)
+{
+ int gpio_intr;
+ u16 pwcfg_addr;
+ u8 err;
+ u8 i;
+
+ enable_irq(mxt_es->irq);
+ mutex_lock(&mxt_es->dev_mutex);
+
+ pwcfg_addr = get_object_address(MXT_GEN_POWERCONFIG_T7,
+ 0,
+ mxt_es->object_table,
+ mxt_es->device_info.num_objs);
+/* FIH { */
+#if 0
+ err = mxt_write_block(mxt_es->client, pwcfg_addr, 3, mxt_es->T7);
+#else
+ for (i = 0; i < I2C_RETRY_COUNT; i++) {
+ err = mxt_write_block(mxt_es->client, pwcfg_addr, 3, mxt_es->T7);
+ if (err == 3)
+ break;
+ msleep(100);
+ }
+#endif
+/* FIH } */
+ if (err > 0)
+ dev_info(&mxt_es->client->dev, "resume from early suspend");
+ else
+ dev_info(&mxt_es->client->dev, "fail to late resume");
+
+ msleep(25);
+ gpio_intr = gpio_get_value(mxt_es->mxt_intr_gpio);
+ while (gpio_intr == 0) {
+ for (i = 1; i < I2C_RETRY_COUNT; i++) {
+ err = mxt_read_block(mxt_es->client,
+ mxt_es->msg_proc_addr,
+ mxt_es->message_size,
+ mxt_es->message_buf);
+ if (err >= 0)
+ break;
+ dev_info(&mxt_es->client->dev, "flush the buffer failed in the resume function\n");
+
+ }
+ gpio_intr = gpio_get_value(mxt_es->mxt_intr_gpio);
+
+ }
+ mxt_calibrate(mxt_es->client);
+ mxt_es->suspended = false;
+ mutex_unlock(&mxt_es->dev_mutex);
+
+}
+#endif
+
static const struct dev_pm_ops mxt_pm_ops = {
- .suspend = mxt_suspend,
- .resume = mxt_resume,
+ .suspend = mxt_suspend,
+ .resume = mxt_resume,
};
-#endif
-static const struct i2c_device_id mxt_id[] = {
- { "qt602240_ts", 0 },
- { "atmel_mxt_ts", 0 },
- { "mXT224", 0 },
+static const struct i2c_device_id mxt_idtable[] = {
+ {TOUCH_DEVICE_NAME, 1,},
{ }
};
-MODULE_DEVICE_TABLE(i2c, mxt_id);
+
+MODULE_DEVICE_TABLE(i2c, mxt_idtable);
static struct i2c_driver mxt_driver = {
.driver = {
- .name = "atmel_mxt_ts",
- .owner = THIS_MODULE,
+ .name = TOUCH_DEVICE_NAME,
+ .owner = THIS_MODULE,
#ifdef CONFIG_PM
- .pm = &mxt_pm_ops,
+ .pm = &mxt_pm_ops,
#endif
},
+
+ .id_table = mxt_idtable,
.probe = mxt_probe,
.remove = __devexit_p(mxt_remove),
- .id_table = mxt_id,
};
static int __init mxt_init(void)
{
- return i2c_add_driver(&mxt_driver);
+ int err;
+
+ err = i2c_add_driver(&mxt_driver);
+ if (err) {
+ printk(KERN_WARNING "Adding maXTouch driver(Here for mxt224) failed "
+ "(errno = %d)\n", err);
+ } else {
+ printk(KERN_INFO "Successfully added driver %s\n",
+ mxt_driver.driver.name);
+ }
+ return err;
}
-static void __exit mxt_exit(void)
+static void __exit mxt_cleanup(void)
{
i2c_del_driver(&mxt_driver);
}
-module_init(mxt_init);
-module_exit(mxt_exit);
-/* Module information */
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver");
+late_initcall(mxt_init);
+module_exit(mxt_cleanup);
+
+MODULE_AUTHOR("Iiro Valkonen");
+MODULE_DESCRIPTION("Driver for Atmel maXTouch Touchscreen Controller");
MODULE_LICENSE("GPL");
+
+
+#ifdef CONFIG_DEBUG_FS
+/******************************************************************************
+debugfs for touch driver, contain four debug files:
+
+1.sread
+ can be read and write.
+ write: set the address of a single register to be accessed;
+ read: return the content of the register.
+2.swrite
+ can be write only.
+ write: set the value of the corresponding register, and the register's
+ address is determined by 'write' operation of 'single_read' file.
+3.sread_offset
+ can be write only
+ write:set the offset of the register
+*********************************************************************************/
+
+
+static int intel_mid_single_read_get(void *data, u64 *val)
+{
+ int ret, gpio_intr, gpio_rst;
+ u16 addr;
+ addr = get_object_address(reg_add,
+ 0,
+ mxt_es->object_table,
+ mxt_es->device_info.num_objs)+reg_add_offset;
+
+ ret = mxt_read_block(mxt_es->client, addr, 1, (u8 *)val);
+ if (ret < 0)
+ printk(KERN_ERR "intel_touch debugfs read_set err add=0x%x\n", reg_add);
+ gpio_intr = gpio_get_value(mxt_es->mxt_intr_gpio);
+ gpio_rst = gpio_get_value(mxt_es->mxt_reset_gpio);
+ printk("touch interrupt value = %d,gpio_rst = %d\n", gpio_intr, gpio_rst);
+ return 0;
+}
+
+static int intel_mid_single_read_set(void *data, u64 val)
+{
+ reg_add = (u8)val;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(intel_mid_single_read_reg_fops,
+ intel_mid_single_read_get,
+ intel_mid_single_read_set, "0x%02llx\n");
+
+static int intel_mid_single_read_set_offset(void *data, u64 val)
+{
+ reg_add_offset = (u8)val;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(intel_mid_single_read_reg_offset_fops,
+ NULL, intel_mid_single_read_set_offset,
+ "0x%02llx\n");
+
+static int intel_mid_single_write_set(void *data, u64 val)
+{
+ int ret;
+ u16 addr;
+ uint8_t data1[1] = {0};
+ data1[0] = (u8)val;
+ addr = get_object_address(reg_add,
+ 0,
+ mxt_es->object_table,
+ mxt_es->device_info.num_objs)+reg_add_offset;
+
+ ret = mxt_write_block(mxt_es->client, addr, 1, data1);
+
+ if (ret < 0)
+ printk(KERN_ERR "intel_touch debugfs write err add=0x%x\n", reg_add);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(intel_mid_single_write_reg_fops,
+ NULL, intel_mid_single_write_set, "0x%02llx\n");
+
+
+static int intel_mid_add_debugfs(void)
+{
+ struct dentry *root;
+
+ root = debugfs_create_dir("touch_debug", NULL);
+
+ if (IS_ERR(root))
+ /* Don't complain -- debugfs just isn't enabled */
+ return -1;
+ if (!root)
+ /* Complain -- debugfs is enabled, but it failed to
+ * create the directory. */
+ goto err;
+
+ if (!debugfs_create_file("sread", S_IWUGO|S_IRUGO, root, NULL, &intel_mid_single_read_reg_fops))
+ goto err;
+
+ if (!debugfs_create_file ("sread_offset", S_IWUGO|S_IRUGO, root, NULL, &intel_mid_single_read_reg_offset_fops))
+ goto err;
+
+ if (!debugfs_create_file ("swrite", S_IWUGO, root, NULL, &intel_mid_single_write_reg_fops))
+ goto err;
+
+ return 0;
+err:
+ debugfs_remove_recursive(root);
+ printk(KERN_ERR "failed to initialize intel_mid debugfs\n");
+ return -1;
+}
+
+#endif
#include "mdfld_dsi_pkg_sender.h"
#include "psb_drv.h"
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+#include "mdfld_dsi_lvds_bridge.h"
+static void dsi_debug_MIPI_reg(struct drm_device *dev);
+static void mipi_set_properties(struct mdfld_dsi_config *dsi_config, int pipe);
+static void mdfld_mipi_set_video_timing(struct mdfld_dsi_config *dsi_config,
+ int pipe);
+static void mdfld_mipi_config(struct mdfld_dsi_config *dsi_config, int pipe);
+static void mdfld_set_pipe_timing(struct mdfld_dsi_config *dsi_config,
+ int pipe);
+static u32 mode_hdisplay;
+static u32 mode_vdisplay;
+#endif
+
+/* Local functions */
+static void mdfld_dsi_dpi_shut_down(struct mdfld_dsi_dpi_output *output, int pipe);
+bool dsi_device_ready = true;
+struct drm_encoder *gencoder;
extern struct drm_device *gpDrmDevice;
#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY
DRM_INFO("MIPI: HS CMD FIFO was never cleared!\n");
}
+static void mdfld_wait_for_PIPEA_DISABLE(struct drm_device *dev, u32 pipe)
+{
+ u32 pipeconf_reg = PIPEACONF;
+ int timeout = 0;
+
+ if (pipe == 2)
+ pipeconf_reg = PIPECCONF;
+
+ udelay(500);
+
+ /* This will time out after approximately 2+ seconds */
+ while ((timeout < 20000) && (REG_READ(pipeconf_reg) & 0x40000000)) {
+ udelay(100);
+ timeout++;
+ }
+
+ if (timeout == 20000)
+ DRM_INFO("MIPI: PIPE was not disabled!\n");
+}
+
+static void mdfld_wait_for_DPI_CTRL_FIFO(struct drm_device *dev, u32 pipe)
+{
+ u32 gen_fifo_stat_reg = MIPIA_GEN_FIFO_STAT_REG;
+ int timeout = 0;
+
+ if (pipe == 2)
+ gen_fifo_stat_reg += MIPIC_REG_OFFSET;
+
+ udelay(500);
+
+ /* This will time out after approximately 2+ seconds */
+ while ((timeout < 20000) && ((REG_READ(gen_fifo_stat_reg) & DPI_FIFO_EMPTY)
+ != DPI_FIFO_EMPTY)) {
+ udelay(100);
+ timeout++;
+ }
+
+ if (timeout == 20000)
+ DRM_INFO("MIPI: DPI FIFO was never cleared!\n");
+}
+
static void mdfld_wait_for_SPL_PKG_SENT(struct drm_device *dev, u32 pipe)
{
u32 intr_stat_reg = MIPIA_INTR_STAT_REG;
mdelay(40);
}
}
-
+#endif
+#if defined(CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY) || defined(CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE)
void dsi_set_device_ready_state(struct drm_device *dev, int state, int pipe)
{
u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0;
} while (pp_sts_reg & PP_ON);
}
}
+#endif
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY
void dsi_set_pipe_plane_enable_state(struct drm_device *dev,
int state,
int pipe)
disp_init = 0;
}
+#endif /*-- CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY --// */
+/*DIV5-MM-DISPLAY-NC-LCM_INIT-00-]- */
+
+
+#if defined(CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY) || defined(CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE)
+
+void dsi_set_pipe_plane_enable_state(struct drm_device *dev, int state, int pipe)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ u32 pipeconf_reg = PIPEACONF;
+ u32 dspcntr_reg = DSPACNTR;
+ u32 mipi_reg = MIPI;
+ u32 reg_offset = 0;
+
+ u32 pipeconf = dev_priv->pipeconf;
+ u32 dspcntr = dev_priv->dspcntr;
+ u32 mipi = MIPI_PORT_EN | PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX;
+
+ printk(KERN_ALERT "[DISPLAY TRK] %s: state = %d, pipe = %d\n", __func__, state, pipe);
+
+ if (pipe) {
+ pipeconf_reg = PIPECCONF;
+ dspcntr_reg = DSPCCNTR;
+ mipi_reg = MIPI_C;
+ reg_offset = MIPIC_REG_OFFSET;
+ } else {
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+ mipi &= (~0x03);
+#else
+ mipi |= 2;
+#endif
+ }
+
+ if (state) {
+ int retry;
+ /*Set up pipe */
+ REG_WRITE(pipeconf_reg, pipeconf);
+
+ /*Wait for pipe enabling*/
+ retry = 10000;
+ while (--retry && !(REG_READ(pipeconf_reg) & BIT30))
+ udelay(3);
+
+ if (!retry)
+ printk(KERN_ALERT "Fatal Error: Failed to enable pipe\n");
+
+
+ /*Set up display plane */
+ REG_WRITE(dspcntr_reg, dspcntr);
+ } else {
+ /*Disable PIPE */
+ REG_WRITE(pipeconf_reg, 0);
+ mdfld_wait_for_PIPEA_DISABLE(dev, pipe);
+ mdfld_wait_for_DPI_CTRL_FIFO(dev, pipe);
+
+ }
+}
static void mdfld_dsi_configure_down(struct mdfld_dsi_encoder * dsi_encoder, int pipe)
{
PSB_DEBUG_ENTRY("[DISPLAY] %s: DPI Panel is Already Off\n", __func__);
return;
}
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+ dsi_lvds_toshiba_bridge_panel_off();
+ udelay(100);
+ dsi_lvds_deinit_lvds_bridge(dev);
+ /* dsi_lvds_suspend_lvds_bridge(dev); */
+#else
+ mdfld_deinit_TOSHIBA_MIPI(dev); /* De-init MIPI bridge and Panel */
+ dsi_set_bridge_reset_state(1); /* Pull Low Reset */
+#endif
+ dsi_set_pipe_plane_enable_state(dev, 0, pipe); /* Disable pipe and plane */
- mdfld_deinit_TOSHIBA_MIPI(dev); //De-init MIPI bridge and Panel
- dsi_set_bridge_reset_state(1); //Pull Low Reset
-
- dsi_set_pipe_plane_enable_state(dev, 0, pipe); //Disable pipe and plane
-
-// dsi_set_ptarget_state(dev, 0); //Disable PTARGET
+ /* dsi_set_ptarget_state(dev, 0); */ /* Disable PTARGET */
- mdfld_dsi_dpi_shut_down(dpi_output, pipe); //Send shut down command
+ mdfld_dsi_dpi_shut_down(dpi_output, pipe); /* Send shut down command */
- dsi_set_device_ready_state(dev, 0, pipe); //Clear device ready state
+ dsi_set_device_ready_state(dev, 0, pipe); /* Clear device ready state */
dev_priv->dpi_panel_on = false;
}
}
/* For resume path sequence */
-// dsi_set_pipe_plane_enable_state(dev, 0, pipe);
-// dsi_set_ptarget_state(dev, 0);
+ /* dsi_set_pipe_plane_enable_state(dev, 0, pipe); */
+ /* dsi_set_ptarget_state(dev, 0); */
mdfld_dsi_dpi_shut_down(dpi_output, pipe);
dsi_set_device_ready_state(dev, 0, pipe); //Clear Device Ready Bit
dsi_set_device_ready_state(dev, 1, pipe); //Set device ready state
- dsi_set_bridge_reset_state(0); //Pull High Reset
- mdfld_init_TOSHIBA_MIPI(dev); //Init MIPI Bridge and Panel
- mdfld_dsi_dpi_turn_on(dpi_output, pipe); //Send turn on command
-// dsi_set_ptarget_state(dev, 1); //Enable PTARGET
- dsi_set_pipe_plane_enable_state(dev, 1, pipe); //Enable plane and pipe
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+ dsi_lvds_toshiba_bridge_panel_on();
+ udelay(100);
+ /* dsi_lvds_set_bridge_reset_state(0); */
+ dsi_lvds_configure_lvds_bridge(dev);
+#else
+ dsi_set_bridge_reset_state(0); /* Pull High Reset */
+ mdfld_init_TOSHIBA_MIPI(dev); /* Init MIPI Bridge and Panel */
+#endif
+
+ mdfld_dsi_dpi_turn_on(dpi_output, pipe); /* Send turn on command */
+ /* dsi_set_ptarget_state(dev, 1); */ /* Enable PTARGET */
+ dsi_set_pipe_plane_enable_state(dev, 1, pipe); /* Enable plane and pipe */
dev_priv->dpi_panel_on = true;
}
dpi_timing->hbp_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_hbp, num_lane, bpp);
dpi_timing->hfp_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_hfp, num_lane, bpp);
dpi_timing->hactive_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_hactive, num_lane, bpp);
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+ dpi_timing->vsync_count = pclk_vsync;
+ dpi_timing->vbp_count = pclk_vbp;
+ dpi_timing->vfp_count = pclk_vfp;
+#else
dpi_timing->vsync_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_vsync, num_lane, bpp);
dpi_timing->vbp_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_vbp, num_lane, bpp);
dpi_timing->vfp_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_vfp, num_lane, bpp);
#endif
-
+#endif
PSB_DEBUG_ENTRY("DPI timings: %d, %d, %d, %d, %d, %d, %d\n",
dpi_timing->hsync_count, dpi_timing->hbp_count,
dpi_timing->hfp_count, dpi_timing->hactive_count,
REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004);
/*TODO: figure out how to setup these registers*/
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+ REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x2A0c6008);
+#else
REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c3408);
-
+#endif
REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), (0xa << 16) | 0x14);
#endif
+
/*set device ready*/
REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001);
}
REG_WRITE(regs->device_ready_reg, ctx->device_ready);
return 0;
}
+static void mdfld_dsi_dpi_shut_down(struct mdfld_dsi_dpi_output *output, int pipe)
+{
+ struct drm_device *dev = output->dev;
+ /* struct drm_psb_private * dev_priv = dev->dev_private; */
+ u32 reg_offset = 0;
+
+ PSB_DEBUG_ENTRY("pipe %d panel state %d\n", pipe, output->panel_on);
+
+ /*if output is on, or mode setting didn't happen, ignore this*/
+ if ((!output->panel_on) || output->first_boot) {
+ output->first_boot = 0;
+ return;
+ }
+
+ if (pipe)
+ reg_offset = MIPIC_REG_OFFSET;
+
+ /* Wait for dpi fifo to empty */
+ mdfld_wait_for_DPI_CTRL_FIFO(dev, pipe);
+
+ /* Clear the special packet interrupt bit if set */
+ if (REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT)
+ REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset), DSI_INTR_STATE_SPL_PKG_SENT);
+
+ if (REG_READ(MIPIA_DPI_CONTROL_REG + reg_offset) == DSI_DPI_CTRL_HS_SHUTDOWN) {
+ PSB_DEBUG_ENTRY("try to send the same package again, abort!");
+ goto shutdown_out;
+ }
+
+ REG_WRITE((MIPIA_DPI_CONTROL_REG + reg_offset), DSI_DPI_CTRL_HS_SHUTDOWN);
+
+shutdown_out:
+ output->panel_on = 0;
+ output->first_boot = 0;
+
+ /* FIXME the following is disabled to WA the X slow start issue for TMD panel */
+ /* if(pipe == 2) */
+ /* dev_priv->dpi_panel_on2 = false; */
+ /* else if (pipe == 0) */
+ /* dev_priv->dpi_panel_on = false; */
+}
/**
* Power on sequence for video mode MIPI panel.
void mdfld_dsi_dpi_set_power(struct drm_encoder *encoder, bool on)
{
struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder);
+ struct mdfld_dsi_dpi_output *dpi_output =
+ MDFLD_DSI_DPI_OUTPUT(dsi_encoder);
struct mdfld_dsi_config *dsi_config =
mdfld_dsi_encoder_get_config(dsi_encoder);
int pipe = mdfld_dsi_encoder_get_pipe(dsi_encoder);
OSPM_UHB_FORCE_POWER_ON))
return;
-#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY
- if(on) {
+#if defined(CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY) || defined(CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE)
+ if (on) {
if (get_panel_type(dev, pipe) == TMD_VID){
- if(dsi_device_ready) {
+ if (dsi_device_ready) {
ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND);
return;
}
mdfld_dsi_tpo_ic_init(dsi_config, pipe);
}
- if(pipe == 2) {
+ if (pipe == 2) {
dev_priv->dpi_panel_on2 = true;
- }
- else {
+ } else {
dev_priv->dpi_panel_on = true;
}
} else {
if (get_panel_type(dev, pipe) == TMD_VID) {
- if(!dsi_device_ready) {
+ if (!dsi_device_ready) {
ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND);
return;
}
REG_READ(mipi_reg);
}
- if(pipe == 2) {
+ if (pipe == 2)
dev_priv->dpi_panel_on2 = false;
- }
- else {
+ else
dev_priv->dpi_panel_on = false;
- }
-
}
#else
dsi_encoder = MDFLD_DSI_ENCODER(encoder);
dpi_output = MDFLD_DSI_DPI_OUTPUT(dsi_encoder);
-#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY
+#if defined(CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY)
mdfld_dsi_dpi_set_power(encoder, false);
+#elif defined(CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE)
+ mdfld_dsi_dpi_set_power(encoder, true);
#else
/*Everything is ready, commit DSI hw context to HW*/
__mdfld_dsi_dpi_set_power(encoder, true);
PSB_DEBUG_ENTRY("[DISPLAY] DSPACNTR = %x\n", temp_val);
*/
}
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+/* This functionality was implemented in FW in iCDK */
+/* But removed in DV0 and later. So need to add here. */
+static void mipi_set_properties(struct mdfld_dsi_config *dsi_config, int pipe)
+{
+ struct drm_device *dev = dsi_config->dev;
+ u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0;
+
+ printk(KERN_ALERT "[DISPLAY] Enter %s\n", __func__);
+
+ REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018); /*0xB104 */
+ REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff); /*0xB008 */
+ REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), 0xffffff); /*0xB010 */
+ REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffffff); /*0xB014 */
+ REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x14); /*0xB018 */
+ REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0xff); /*0xB01C */
+ REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x25); /*0xB044 */
+ REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0xf0); /*0xB050 */
+ REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000000); /*0xB05C */
+ REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004); /*0xB060 */
+ REG_WRITE((MIPIA_DBI_BW_CTRL_REG + reg_offset), 0x00000820); /*0xB084 */
+ REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), (0xa << 16) | 0x14); /*B088 */
+}
+
+static void mdfld_mipi_set_video_timing(struct mdfld_dsi_config *dsi_config,
+ int pipe)
+{
+ struct drm_device *dev = dsi_config->dev;
+ u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0;
+ struct mdfld_dsi_dpi_timing dpi_timing;
+ struct drm_display_mode *mode = dsi_config->mode;
+
+ printk(KERN_ALERT "[DISPLAY] Enter %s\n", __func__);
+
+ mdfld_dsi_dpi_timing_calculation(mode, &dpi_timing, dsi_config->lane_count, dsi_config->bpp);
+
+ REG_WRITE((MIPIA_DPI_RESOLUTION_REG + reg_offset), mode->vdisplay << 16 | mode->hdisplay); /*0xB020 */
+ REG_WRITE((MIPIA_HSYNC_COUNT_REG + reg_offset), dpi_timing.hsync_count & DSI_DPI_TIMING_MASK); /*0xB028 */
+ REG_WRITE((MIPIA_HBP_COUNT_REG + reg_offset), dpi_timing.hbp_count & DSI_DPI_TIMING_MASK); /*0xB02C */
+ REG_WRITE((MIPIA_HFP_COUNT_REG + reg_offset), dpi_timing.hfp_count & DSI_DPI_TIMING_MASK); /*0xB030 */
+ REG_WRITE((MIPIA_HACTIVE_COUNT_REG + reg_offset), dpi_timing.hactive_count & DSI_DPI_TIMING_MASK); /*0xB034 */
+ REG_WRITE((MIPIA_VSYNC_COUNT_REG + reg_offset),
+ dpi_timing.vsync_count & DSI_DPI_TIMING_MASK); /*0xB038 */
+ REG_WRITE((MIPIA_VBP_COUNT_REG + reg_offset),
+ dpi_timing.vbp_count & DSI_DPI_TIMING_MASK); /*0xB03C */
+ REG_WRITE((MIPIA_VFP_COUNT_REG + reg_offset),
+ dpi_timing.vfp_count & DSI_DPI_TIMING_MASK); /*0xB040 */
+}
+
+static void mdfld_mipi_config(struct mdfld_dsi_config *dsi_config, int pipe)
+{
+ struct drm_device *dev = dsi_config->dev;
+ u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0;
+ int lane_count = dsi_config->lane_count;
+
+ printk(KERN_ALERT "[DISPLAY] Enter %s\n", __func__);
+
+ if (pipe) {
+ REG_WRITE(MIPI, 0x00000002); /*0x61190 */
+ REG_WRITE(MIPI_C, 0x80000000); /*0x62190 */
+ } else {
+ REG_WRITE(MIPI_C, 0x00); /*0x62190 */
+ REG_WRITE(MIPI, 0x80010000); /*0x61190 */
+ }
+ REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset),
+ 0x150A600F); /*0xB080 */
+
+
+ REG_WRITE((MIPIA_VIDEO_MODE_FORMAT_REG + reg_offset),
+ 0x0000000F); /*0xB058 */
+
+ /*lane_count = 3 */
+ REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), (0x00000200 | lane_count)); /*0xB00C */
+
+ mdfld_mipi_set_video_timing(dsi_config, pipe);
+}
+
+static void mdfld_set_pipe_timing(struct mdfld_dsi_config *dsi_config,
+ int pipe)
+{
+ struct drm_device *dev = dsi_config->dev;
+ struct drm_display_mode *mode = dsi_config->mode;
+
+ printk(KERN_ALERT "[DISPLAY] Enter %s\n", __func__);
+ /*0x60000 */
+ REG_WRITE(HTOTAL_A, ((mode->htotal - 1) << 16) | (mode->hdisplay - 1));
+ /*0x60004 */
+ REG_WRITE(HBLANK_A, ((mode->htotal - 1) << 16) | (mode->hdisplay - 1));
+ /*0x60008 */
+ REG_WRITE(HSYNC_A, ((mode->hsync_end - 1) << 16) |
+ (mode->hsync_start - 1));
+ /*0x6000C */
+ REG_WRITE(VTOTAL_A, ((mode->vtotal - 1) << 16) | (mode->vdisplay - 1));
+ /*0x60010 */
+ REG_WRITE(VBLANK_A, ((mode->vtotal - 1) << 16) | (mode->vdisplay - 1));
+ /*0x60014 */
+ REG_WRITE(VSYNC_A, ((mode->vsync_end - 1) << 16) |
+ (mode->vsync_start - 1));
+ /*0x6001C */
+ REG_WRITE(PIPEASRC, ((mode->hdisplay - 1) << 16) |
+ (mode->vdisplay - 1));
+}
+
+#endif
/**
* Setup DPI timing for video mode MIPI panel.
struct drm_display_mode *adjusted_mode)
{
struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder);
+ struct mdfld_dsi_dpi_output *dpi_output =
+ MDFLD_DSI_DPI_OUTPUT(dsi_encoder);
struct mdfld_dsi_config *dsi_config =
mdfld_dsi_encoder_get_config(dsi_encoder);
-#ifdef MIPI_DEBUG_LOG
struct drm_device *dev = dsi_config->dev;
-#endif
+ struct drm_psb_private *dev_priv = dev->dev_private;
int pipe = mdfld_dsi_encoder_get_pipe(dsi_encoder);
u32 pipeconf_reg = PIPEACONF;
u32 dspcntr_reg = DSPACNTR;
u32 mipi_reg = MIPI;
u32 reg_offset = 0;
-
+ u32 pipeconf = dev_priv->pipeconf;
+ u32 dspcntr = dev_priv->dspcntr;
u32 mipi = MIPI_PORT_EN | PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX;
PSB_DEBUG_ENTRY("set mode %dx%d on pipe %d",
mipi_reg = MIPI_C;
reg_offset = MIPIC_REG_OFFSET;
} else {
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+ mipi &= (~0x03); /* Use all four lanes */
+#else
mipi |= 2;
+#endif
}
-#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY
+#if defined(CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY) || defined(CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE)
/*start up display island if it was shutdown*/
if (!ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND,
OSPM_UHB_FORCE_POWER_ON))
return;
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+ /* The following logic is required to do reset the bridge and configure. */
+ /* This also start DSI clock at 200MHz */
+ {
+ int timeout = 0;
+
+ dsi_lvds_set_bridge_reset_state(0); /*Pull High Reset */
+ dsi_lvds_toshiba_bridge_panel_on();
+ udelay(100);
+ /* Now start the DSI clock */
+ REG_WRITE(MRST_DPLL_A, 0x00); /*0xF014 */
+ REG_WRITE(MRST_FPA0, 0xC1); /*0xF040 */
+ REG_WRITE(MRST_DPLL_A, 0x00800000); /*0xF014 */
+ udelay(500);
+ REG_WRITE(MRST_DPLL_A, 0x80800000); /*0xF014 */
+ /*Wait for DSI PLL to lock */
+ while ((timeout < 20000) && !(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) {
+ udelay(150);
+ timeout++;
+ }
+ if (timeout == 20000)
+ printk(KERN_ALERT "[DISPLAY] DSI PLL Locked timeout\n");
+ REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x2A0c6008); /*0xB080 */
+
+ mipi_set_properties(dsi_config, pipe);
+ mdfld_mipi_config(dsi_config, pipe);
+ mdfld_set_pipe_timing(dsi_config, pipe);
+
+ REG_WRITE(DSPABASE, 0x00); /*0x70184 */
+
+ REG_WRITE(DSPASTRIDE, (mode->hdisplay * 4)); /*0x70188 */
+
+ REG_WRITE(DSPASIZE, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); /*0x70190 */
+
+ REG_WRITE(DSPACNTR, 0x98000000); /*0x70180 */
+
+ REG_WRITE(DSPASURF, 0x00); /*0x7019C */
+
+ REG_WRITE(VGACNTRL, 0x80000000); /*0x71400 */
+
+ REG_WRITE(DEVICE_READY_REG, 0x00000001); /*0xB000 */
+
+ REG_WRITE(mipi_reg, 0x80810000); /*0x61190 */
+ }
+#endif
+
/*set up mipi port FIXME: do at init time */
REG_WRITE(mipi_reg, mipi);
REG_READ(mipi_reg);
mdfld_dsi_dpi_controller_init(dsi_config, pipe);
if (get_panel_type(dev, pipe) == TMD_VID) {
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+ dsi_lvds_configure_lvds_bridge(dev); /*Configure MIPI Bridge and Panel */
+ dsi_device_ready = true;
+ dev_priv->dpi_panel_on = true;
+#else
/*Pull High Reset*/
dsi_set_bridge_reset_state(0);
/*Init MIPI Bridge and Panel*/
mdfld_init_TOSHIBA_MIPI(dev);
dsi_device_ready = true;
+#endif
} else {
/*turn on DPI interface*/
mdfld_dsi_dpi_turn_on(dpi_output, pipe);
dpi_output->panel_on);
if (get_panel_type(dev, pipe) == TMD_VID) {
- /*mdfld_dsi_dpi_turn_on(dpi_output, pipe);*/
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+ mdfld_dsi_dpi_turn_on(dpi_output, pipe);
+#endif
+ /* mdfld_dsi_dpi_turn_on(dpi_output, pipe); */
} else {
/* init driver ic */
mdfld_dsi_tpo_ic_init(dsi_config, pipe);
pipe = dsi_connector->pipe;
#ifndef CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY
+#ifndef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
/*panel hard-reset*/
if (p_funcs->reset) {
ret = p_funcs->reset(dsi_config, RESET_FROM_BOOT_UP);
if (p_funcs->dsi_controller_init)
p_funcs->dsi_controller_init(dsi_config, pipe, 0);
#else
+ dsi_connector->status = connector_status_connected;
+#endif
+#else
/* Enable MIPI panel by default for PR1
* platform where panel detection code
* doesn't ready.
return NULL;
}
-#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY
+#if defined(CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY) || defined(CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE)
if (dsi_connector->pipe)
dpi_output->panel_on = 0;
#endif
fixed_mode = dsi_config->fixed_mode;
#ifndef CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY
+#ifndef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
/*detect power status of the connected panel*/
if (p_funcs->get_panel_power_state) {
ret = p_funcs->get_panel_power_state(dsi_config, pipe);
dsi_config->dsi_hw_context.panel_on = 0;
}
#endif
+#endif
/*create drm encoder object*/
connector = &dsi_connector->base.base;
encoder = &dpi_output->base.base;
gpio_request(GPIO_MIPI_BRIDGE_RESET, "display");
gpio_request(GPIO_MIPI_PANEL_RESET , "display");
#endif
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+ {
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ dev_priv->dpi_panel_on = false;
+ gencoder = encoder;
+ dsi_lvds_init_lvds_bridge(dev);
+ }
+#endif
PSB_DEBUG_ENTRY("successfully\n");
void __user *argp = (void __user*)arg;
#if defined(CONFIG_SUPPORT_TMD_MIPI_600X1024_DISPLAY) \
|| defined(CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY) \
- || defined(CONFIG_SUPPORT_MIPI_H8C7_DISPLAY)
-
+ || defined(CONFIG_SUPPORT_MIPI_H8C7_DISPLAY) \
+ || defined(CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE)
struct drm_encoder *encoder = gencoder;
#endif
switch (cmd) {
#if defined(CONFIG_SUPPORT_TMD_MIPI_600X1024_DISPLAY) \
|| defined(CONFIG_SUPPORT_TOSHIBA_MIPI_DISPLAY) \
- || defined(CONFIG_SUPPORT_MIPI_H8C7_DISPLAY)
-
+ || defined(CONFIG_SUPPORT_MIPI_H8C7_DISPLAY) \
+ || defined(CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE)
case IOCTL_LCM_POWER_ON:
mdfld_dsi_dpi_set_power(encoder, 1);
break;
--- /dev/null
+/*
+ * Copyright © 2011 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.
+ *
+ */
+
+#include "mdfld_dsi_dpi.h"
+#include "mdfld_output.h"
+#include "mdfld_dsi_pkg_sender.h"
+
+#define CONFIG_LVDS_HARD_RESET
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+
+/*GPIO Pins */
+#define GPIO_MIPI_BRIDGE_RESET 115
+
+#define GPIO_MIPI_LCD_BL_EN 112 /* DV1.0 GP_CORE_016 (+96 = GPIO number), 6S6P_BL_EN */
+#define GPIO_MIPI_LCD_VADD 110
+/* All these pins removed on DV1.0 */
+#define GPIO_MIPI_LCD_BIAS_EN -1
+#define GPIO_MIPI_PANEL_RESET -1
+#define GPIO_MIPI_LCD_STBYB -1
+#define GPIO_MIPI_LCD_COLOR_EN -1
+
+int lvds_disp_init;
+
+struct dsi_lvds_bridge_instance {
+ struct i2c_client *client;
+ struct work_struct wqueue;
+ struct work_struct pre_init_work;
+ struct work_struct test_work;
+} *dsi_lvds_inst;
+
+
+static int DSI_I2C_ByteRead(u16 reg, int count);
+static int DSI_I2C_ByteWrite(u16 reg, u32 data, int count);
+
+static bool lvds_suspend_state;
+
+/************************************************************************** *\
+ * FUNCTION: dsi_lvds_suspend_lvds_bridge
+ *
+ * DESCRIPTION: This function is called by psb power management
+ * during early suspend.
+\* ************************************************************************* */
+void dsi_lvds_suspend_lvds_bridge(struct drm_device *dev)
+{
+ u32 temp;
+
+ printk(KERN_INFO "[DISPLAY ] Enter %s\n", __func__);
+
+ if (gpio_direction_output(GPIO_MIPI_LCD_BL_EN, 0))
+ gpio_set_value_cansleep(GPIO_MIPI_LCD_BL_EN, 0);
+ mdelay(1);
+ if (gpio_direction_output(GPIO_MIPI_LCD_VADD, 0))
+ gpio_set_value_cansleep(GPIO_MIPI_LCD_VADD, 0);
+#ifdef CONFIG_LVDS_HARD_RESET
+ if (gpio_direction_output(GPIO_MIPI_BRIDGE_RESET, 0))
+ gpio_set_value_cansleep(GPIO_MIPI_BRIDGE_RESET, 0);
+#else
+ /* Put the panel in ULPS mode for S0ix. */
+ temp = REG_READ(DEVICE_READY_REG);
+ temp &= ~ULPS_MASK;
+ temp |= ENTERING_ULPS;
+ REG_WRITE(DEVICE_READY_REG, temp);
+ temp = REG_READ(DEVICE_READY_REG);
+
+ /* LP Hold */
+ temp = REG_READ(MIPI);
+ temp &= ~LP_OUTPUT_HOLD;
+ REG_WRITE(MIPI, temp);
+ temp = REG_READ(MIPI);
+
+ mdelay(1);
+#endif
+ lvds_suspend_state = true;
+}
+
+/************************************************************************** *\
+ * FUNCTION: dsi_lvds_resumed_lvds_bridge
+ *
+ * DESCRIPTION: This function is called by psb power management
+ * during late resume.
+\* ************************************************************************* */
+void dsi_lvds_resume_lvds_bridge(struct drm_device *dev)
+{
+ u32 temp;
+ printk(KERN_INFO "[DISPLAY ] Enter %s\n", __func__);
+ if (gpio_direction_output(GPIO_MIPI_LCD_BL_EN, 1))
+ gpio_set_value_cansleep(GPIO_MIPI_LCD_BL_EN, 1);
+ /* VADD */
+ if (gpio_direction_output(GPIO_MIPI_LCD_VADD, 1))
+ gpio_set_value_cansleep(GPIO_MIPI_LCD_VADD, 1);
+ mdelay(10);
+
+#ifdef CONFIG_LVDS_HARD_RESET
+ /* RESET */
+ if (gpio_direction_output(GPIO_MIPI_BRIDGE_RESET, 1))
+ gpio_set_value_cansleep(GPIO_MIPI_BRIDGE_RESET, 1);
+ msleep(20);
+ if (gpio_direction_output(GPIO_MIPI_BRIDGE_RESET, 0))
+ gpio_set_value_cansleep(GPIO_MIPI_BRIDGE_RESET, 0);
+ msleep(20);
+ if (gpio_direction_output(GPIO_MIPI_BRIDGE_RESET, 1))
+ gpio_set_value_cansleep(GPIO_MIPI_BRIDGE_RESET, 1);
+ mdelay(20);
+#else
+ /* LP Hold Release */
+ temp = REG_READ(MIPI);
+ temp |= LP_OUTPUT_HOLD_RELEASE;
+ REG_WRITE(MIPI, temp);
+ mdelay(1);
+ temp = REG_READ(MIPI);
+
+
+ /* Set DSI host to exit from Utra Low Power State */
+ temp = REG_READ(DEVICE_READY_REG);
+
+ temp &= ~ULPS_MASK;
+ temp |= EXITING_ULPS;
+ REG_WRITE(DEVICE_READY_REG, temp);
+ mdelay(1);
+ temp = REG_READ(DEVICE_READY_REG);
+
+ temp &= ~ULPS_MASK;
+ REG_WRITE(DEVICE_READY_REG, temp);
+ mdelay(1);
+ temp = REG_READ(DEVICE_READY_REG);
+
+#endif
+ lvds_suspend_state = false;
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: dsi_lvds_set_bridge_reset_state
+ *
+ * DESCRIPTION: This function uses GPIO to force in and out of reset state.
+\* ************************************************************************* */
+void dsi_lvds_set_bridge_reset_state(int state)
+{
+ printk(KERN_INFO "[DISPLAY ] %s: state = %d, gpio = %d\n", __func__, state, gpio_get_value(GPIO_MIPI_BRIDGE_RESET));
+
+ if (state) {
+ if (gpio_direction_output(GPIO_MIPI_BRIDGE_RESET, 0))
+ gpio_set_value_cansleep(GPIO_MIPI_BRIDGE_RESET, 0);
+ mdelay(10);
+ } else {
+
+ if (gpio_direction_output(GPIO_MIPI_BRIDGE_RESET, 0))
+ gpio_set_value_cansleep(GPIO_MIPI_BRIDGE_RESET, 0); /*Pull MIPI Bridge reset pin to Low */
+ mdelay(20);
+ if (gpio_direction_output(GPIO_MIPI_BRIDGE_RESET, 1))
+ gpio_set_value_cansleep(GPIO_MIPI_BRIDGE_RESET, 1); /*Pull MIPI Bridge reset pin to High */
+ mdelay(40);
+ }
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: dsi_lvds_configure_lvds_bridge
+ *
+ * DESCRIPTION: This function uses I2C interface to set bridge registers.
+ * to configure timings and MIPI lanes.
+\* ************************************************************************* */
+void dsi_lvds_configure_lvds_bridge(struct drm_device *dev)
+{
+ if (lvds_disp_init) {
+ printk(KERN_ALERT "[DISPLAY ] %s is already initialized\n", __func__);
+ return;
+ }
+
+ printk(KERN_INFO "[DISPLAY ]%s: Enter\n", __func__);
+
+ DSI_I2C_ByteWrite(0x013C, 0x00050006, 6); /*PPI_TX_RX_TA, BTA parameters */
+ DSI_I2C_ByteRead(0x013C, 4);
+ DSI_I2C_ByteWrite(0x0114, 0x00000004, 6); /*PPI_LPTXTIMCNT */
+ DSI_I2C_ByteWrite(0x0164, 0x00000001, 6); /*PPI_D0S_CLRSIPOCOUNT */
+ DSI_I2C_ByteWrite(0x0168, 0x00000001, 6); /*PPI_D1S_CLRSIPOCOUNT */
+ DSI_I2C_ByteWrite(0x016c, 0x00000001, 6); /*PPI_D2S_CLRSIPOCOUNT */
+ DSI_I2C_ByteWrite(0x0170, 0x00000001, 6); /*PPI_D3S_CLRSIPOCOUNT */
+ /*Enabling MIPI & PPI lanes, Enable 4 lanes */
+ DSI_I2C_ByteWrite(0x0134, 0x0000001F, 6); /*PPI_LANEENABLE */
+ DSI_I2C_ByteWrite(0x0210, 0x0000001F, 6); /*DSI_LANEENABLE */
+ DSI_I2C_ByteWrite(0x0104, 0x00000001, 6); /*PPI_SARTPPI */
+ DSI_I2C_ByteWrite(0x0204, 0x00000001, 6); /*DSI_SARTPPI */
+
+ /*Setting LVDS output frequency */
+ DSI_I2C_ByteWrite(0x04A0, 0x00048006, 6); /*LVDS PHY Register 0 (LVPHY0) */
+
+ /*Calculating video panel control settings */
+ /*Setting video panel control register */
+ DSI_I2C_ByteWrite(0x0450, 0x00000120, 6); /*VPCTRL, Video Path Control, VTGen=ON */
+
+ /*Setting display timing registers */
+ DSI_I2C_ByteWrite(0x0454, 0x00280028, 6); /*HTIM1, HBPR=100, HPW=10 */
+
+ /* value = ((hsync_start - hdisplay)<<16 | hdisplay) */
+ DSI_I2C_ByteWrite(0x0458, ((1360 - 1280)<<16 | 1280), 6); /*HTIM2, HFPR=190, HDISPR=1024 */
+ DSI_I2C_ByteWrite(0x045c, 0x00080007, 6); /*VTIM1, VBPR=8, VPW=7 */
+
+ /* value = ((vsync_start - vdisplay)<<16 | vdisplay */
+ DSI_I2C_ByteWrite(0x0460, ((808 - 8)<<16 | 800), 6); /*VTIM2, VFPR=8, VDISPR=800 */
+ DSI_I2C_ByteWrite(0x0464, 0x00000001, 6); /*VFUEN */
+
+ /*Setting LVDS bit arrangement */
+ DSI_I2C_ByteWrite(0x0480, 0x05040302, 6); /*LVMX0003 */
+ DSI_I2C_ByteWrite(0x0484, 0x0A070106, 6); /*LVMX0407 */
+ DSI_I2C_ByteWrite(0x0488, 0x09080C0B, 6); /*LVMX0811 */
+ DSI_I2C_ByteWrite(0x048C, 0x120F0E0D, 6); /*LVMX1215 */
+ DSI_I2C_ByteWrite(0x0490, 0x14131110, 6); /*LVMX1619 */
+ DSI_I2C_ByteWrite(0x0494, 0x1B171615, 6); /*LVMX2023 */
+ DSI_I2C_ByteWrite(0x0498, 0x001A1918, 6); /*LVMX2427 */
+ DSI_I2C_ByteWrite(0x049c, 0x00000001, 6); /*LVCFG */
+
+
+ DSI_I2C_ByteWrite(0x0288, 0xFFFFFFFF, 6); /*DSI_INTCLR */
+
+ lvds_disp_init = 1;
+
+ printk(KERN_INFO "[DISPLAY]%s: Exit\n", __func__);
+
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: dsi_lvds_toshiba_bridge_panel_off
+ *
+ * DESCRIPTION: This function uses GPIO to turn OFF panel.
+\* ************************************************************************* */
+void dsi_lvds_toshiba_bridge_panel_off(void)
+{
+ printk(KERN_INFO "[DISPLAY ] %s\n", __func__);
+
+ if (gpio_direction_output(GPIO_MIPI_LCD_BL_EN, 0))
+ gpio_set_value_cansleep(GPIO_MIPI_LCD_BL_EN, 0);
+ mdelay(1);
+
+ /* if (gpio_direction_output(GPIO_MIPI_LCD_VADD, 0))
+ * gpio_set_value_cansleep(GPIO_MIPI_LCD_VADD, 0);
+ */
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: dsi_lvds_toshiba_bridge_panel_on
+ *
+ * DESCRIPTION: This function uses GPIO to turn ON panel.
+\* ************************************************************************* */
+void dsi_lvds_toshiba_bridge_panel_on(void)
+{
+ printk(KERN_INFO "[DISPLAY ] %s\n", __func__);
+
+ if (gpio_direction_output(GPIO_MIPI_LCD_VADD, 1))
+ gpio_set_value_cansleep(GPIO_MIPI_LCD_VADD, 1);
+ mdelay(50);
+
+ if (gpio_direction_output(GPIO_MIPI_LCD_BL_EN, 1))
+ gpio_set_value_cansleep(GPIO_MIPI_LCD_BL_EN, 1);
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: dsi_lvds_init_lvds_bridge
+ *
+ * DESCRIPTION: This function does all one time init. Currently defining
+ * GPIOs only.
+\* ************************************************************************* */
+void dsi_lvds_init_lvds_bridge(struct drm_device *dev)
+{
+ int ret;
+ ret = gpio_request(GPIO_MIPI_LCD_VADD, "display");
+ if (ret)
+ printk(KERN_ALERT "[DISPLAY]%s: GPIO request failed [%d]\n",
+ __func__, GPIO_MIPI_LCD_VADD);
+
+ ret = gpio_request(GPIO_MIPI_BRIDGE_RESET, "display");
+ if (ret)
+ printk(KERN_ALERT "[DISPLAY]%s: GPIO request failed [%d]\n",
+ __func__, GPIO_MIPI_BRIDGE_RESET);
+
+ ret = gpio_request(GPIO_MIPI_LCD_BL_EN, "display");
+ if (ret)
+ printk(KERN_ALERT "[DISPLAY]%s: GPIO request failed [%d]\n",
+ __func__, GPIO_MIPI_LCD_BL_EN);
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: dsi_lvds_deinit_lvds_bridge
+ *
+ * DESCRIPTION: This function does is called during deinit time.
+ *
+\* ************************************************************************* */
+void dsi_lvds_deinit_lvds_bridge(struct drm_device *dev)
+{
+ if (!lvds_disp_init) {
+ printk(KERN_ALERT "[DISPLAY ] %s has not initialized\n", __func__);
+ return;
+ }
+
+ printk(KERN_INFO "[DISPLAY ] Enter %s\n", __func__);
+
+#ifdef CONFIG_LVDS_HARD_RESET
+ lvds_disp_init = 0;
+#endif
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: dsi_lvds_bridge_get_display_params
+ *
+ * DESCRIPTION: This function is a callback to get the panel resolution and
+ * timing settings.
+ *
+\* ************************************************************************* */
+void dsi_lvds_bridge_get_display_params(struct drm_display_mode *mode)
+{
+ mode->hdisplay = 1280;
+ mode->vdisplay = 800;
+ mode->hsync_start = mode->hdisplay + 80;
+ mode->hsync_end = mode->hsync_start + 40;
+ mode->htotal = mode->hsync_end + 40;
+ /* Original settings
+ mode->vsync_start = mode->vdisplay + 8;
+ mode->vsync_end = mode->vsync_start + 7;
+ mode->vtotal = mode->vsync_end + 8;
+ mode->clock = 16500;
+ */
+ mode->vsync_start = mode->vdisplay + 14;
+ mode->vsync_end = mode->vsync_start + 10;
+ mode->vtotal = mode->vsync_end + 14;
+ mode->clock = 33324;
+ printk(KERN_INFO "[DISPLAY]: hdisplay(w) is %d\n", mode->hdisplay);
+ printk(KERN_INFO "[DISPLAY]: vdisplay(h) is %d\n", mode->vdisplay);
+ printk(KERN_INFO "[DISPLAY]: HSS is %d\n", mode->hsync_start);
+ printk(KERN_INFO "[DISPLAY]: HSE is %d\n", mode->hsync_end);
+ printk(KERN_INFO "[DISPLAY]: htotal is %d\n", mode->htotal);
+ printk(KERN_INFO "[DISPLAY]: VSS is %d\n", mode->vsync_start);
+ printk(KERN_INFO "[DISPLAY]: VSE is %d\n", mode->vsync_end);
+ printk(KERN_INFO "[DISPLAY]: vtotal is %d\n", mode->vtotal);
+ printk(KERN_INFO "[DISPLAY]: clock is %d\n", mode->clock);
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: __DSI_I2C_ByteRead
+ *
+ * DESCRIPTION: Local functions to process read req for I2C registers
+ *
+\* ************************************************************************* */
+static int __DSI_I2C_ByteRead(u16 reg, int count)
+{
+ char rxData[4] = {0};
+ char regData[2] = {0};
+ struct i2c_msg msgs[] = {
+ {
+ .addr = dsi_lvds_inst->client->addr,
+ .flags = 0,
+ .len = 2,
+ },
+ {
+ .addr = dsi_lvds_inst->client->addr,
+ .flags = I2C_M_RD,
+ .len = count - 2,
+ },
+ };
+
+ regData[0] = (reg & 0xFF00) >> 8;
+ regData[1] = reg & 0xFF;
+
+ msgs[0].buf = regData;
+ msgs[1].buf = rxData;
+
+ printk(KERN_INFO "Register: 0x%x\n", reg);
+ if (i2c_transfer(dsi_lvds_inst->client->adapter, msgs, 2) < 0) {
+ printk(KERN_ERR "[DISPLAY] %s: transfer error\n", __func__);
+ return -EIO;
+ } else if (count > 2) {
+ int i = 0;
+ for (i = count - 3; i > -1; --i)
+ printk(KERN_INFO "%02x ", rxData[i]);
+ printk(KERN_INFO "\n");
+ return rxData[0];
+ }
+ return 0;
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: DSI_I2C_ByteRead
+ *
+ * DESCRIPTION: Local functions to read I2C registers
+ *
+\* ************************************************************************* */
+static int DSI_I2C_ByteRead(u16 reg, int count)
+{
+ if (dsi_lvds_inst->client)
+ return __DSI_I2C_ByteRead(reg, count);
+ else
+ return -EIO;
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: __DSI_I2C_ByteWrite
+ *
+ * DESCRIPTION: Local functions to process write req for I2C registers
+ *
+\* ************************************************************************* */
+static int __DSI_I2C_ByteWrite(u16 reg, u32 data, int count)
+{
+ char txData[6] = {0};
+ struct i2c_msg msg[] = {
+ {
+ .addr = dsi_lvds_inst->client->addr,
+ .flags = 0,
+ .len = count,
+ },
+ };
+
+ /*Set the register */
+ txData[0] = (reg & 0xFF00) >> 8;
+ txData[1] = reg & 0xFF;
+
+ if (count == 6) {
+ /*Set the data */
+ txData[2] = (data & 0xFF);
+ txData[3] = (data & 0xFF00) >> 8;
+ txData[4] = (data & 0xFF0000) >> 16;
+ txData[5] = (data & 0xFF000000) >> 24;
+ } else {
+ /* Not valid for this bridge chipset */
+ }
+
+ printk(KERN_INFO "[DISPLAY] %s: addr = %x, reg = %x, data = %x\n",
+ __func__, dsi_lvds_inst->client->addr, reg, data);
+
+ msg[0].buf = txData;
+
+ if (i2c_transfer(dsi_lvds_inst->client->adapter, msg, 1) < 0) {
+ printk(KERN_ERR "[DISPLAY] %s: transfer error\n", __func__);
+ return -EIO;
+ } else {
+ return 0;
+ }
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: DSI_I2C_ByteWrite
+ *
+ * DESCRIPTION: Local functions to issue write req for I2C registers
+ *
+\* ************************************************************************* */
+static int DSI_I2C_ByteWrite(u16 reg, u32 data, int count)
+{
+ if (dsi_lvds_inst->client)
+ return __DSI_I2C_ByteWrite(reg, data, count);
+ else
+ return -EIO;
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: dsi_lvds_bridge_probe
+ *
+ * DESCRIPTION: Probe function for LVDS bridge.
+ *
+\* ************************************************************************* */
+static int dsi_lvds_bridge_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ printk(KERN_INFO "[DISPLAY] %s: Enter\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ printk(KERN_ERR "[DISPLAY] %s: Check I2C functionality failed.\n", __func__);
+ return -ENODEV;
+ }
+
+ dsi_lvds_inst = kzalloc(sizeof(struct dsi_lvds_bridge_instance), GFP_KERNEL);
+
+ if (dsi_lvds_inst == NULL) {
+ printk(KERN_ERR "[DISPLAY] %s: Can not allocate memory.\n", __func__);
+ return -ENOMEM;
+ }
+
+ dsi_lvds_inst->client = client;
+
+ i2c_set_clientdata(client, dsi_lvds_inst);
+
+ dsi_lvds_inst->client->addr = 0x0F;
+
+ printk(KERN_INFO "[DISPLAY] %s: Exit\n", __func__);
+
+ return 0;
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: dsi_lvds_bridge_remove
+ *
+ * DESCRIPTION: Function to free driver instance.
+ *
+\* ************************************************************************* */
+static int dsi_lvds_bridge_remove(struct i2c_client *client)
+{
+ printk(KERN_INFO "[DISPLAY] %s\n", __func__);
+
+ dev_set_drvdata(&client->dev, 0);
+ kfree(dsi_lvds_inst);
+ return 0;
+}
+
+static const struct i2c_device_id dsi_lvds_bridge_id[] = {
+ { "i2c_disp_brig", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, dsi_lvds_bridge_id);
+
+static struct i2c_driver dsi_lvds_bridge_i2c_driver = {
+ .driver = {
+ .name = "i2c_disp_brig",
+ },
+ .id_table = dsi_lvds_bridge_id,
+ .probe = dsi_lvds_bridge_probe,
+ .remove = dsi_lvds_bridge_remove,
+};
+
+static int dsi_lvds_dev_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+
+ printk(KERN_INFO "[DISPLAY] %s: MIPI DSI driver IOCTL, cmd = %d.\n", __func__, cmd);
+
+ return 0;
+}
+
+static const struct file_operations mipi_dsi_dev_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = dsi_lvds_dev_ioctl,
+};
+
+static struct miscdevice dsi_lvds_bridge_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mipi_dsi",
+ .fops = &mipi_dsi_dev_fops,
+};
+
+/* ************************************************************************* *\
+ * FUNCTION: dsi_lvds_bridge_init
+ *
+ * DESCRIPTION: Driver Init function for lvds bridge
+ *
+\* ************************************************************************* */
+static int __init dsi_lvds_bridge_init(void)
+{
+ int ret = 0;
+
+ printk(KERN_INFO "[DISPLAY] %s: Enter\n", __func__);
+
+ ret = i2c_add_driver(&dsi_lvds_bridge_i2c_driver);
+
+ printk(KERN_INFO "[DISPLAY] %s: Exit, ret = %d\n", __func__, ret);
+
+ return 0;
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: dsi_lvds_bridge_exit
+ *
+ * DESCRIPTION: Driver exit function for lvds bridge
+ *
+\* ************************************************************************* */
+static void __exit dsi_lvds_bridge_exit(void)
+{
+ printk(KERN_INFO "[DISPLAY] %s\n", __func__);
+
+ misc_deregister(&dsi_lvds_bridge_dev);
+
+ i2c_del_driver(&dsi_lvds_bridge_i2c_driver);
+}
+
+
+module_init(dsi_lvds_bridge_init);
+module_exit(dsi_lvds_bridge_exit);
+
+#endif