/* Sensor support for Samsung Tuna Board.
 *
 * Copyright (C) 2011 Google, Inc.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/mpu.h>
#include <linux/gp2a.h>
#include <linux/i2c/twl6030-madc.h>

#include "mux.h"
#include "board-tuna.h"

#define GPIO_GYRO_INT		45
#define GPIO_ACC_INT		122
#define GPIO_MAG_INT		176
#define GPIO_PS_ON		25
#define GPIO_PS_VOUT		21
#define GPIO_MSENSE_IRQ		157

#define GP2A_LIGHT_ADC_CHANNEL	4

static int gp2a_light_adc_value(void)
{
	return twl6030_get_madc_conversion(GP2A_LIGHT_ADC_CHANNEL);
}

static void gp2a_power(bool on)
{
	/* this controls the power supply rail to the gp2a IC */
	gpio_set_value(GPIO_PS_ON, on);
}

static void gp2a_gpio_init(void)
{
	int ret = gpio_request(GPIO_PS_ON, "gp2a_power_supply_on");
	if (ret) {
		pr_err("%s Failed to request gpio gp2a power supply\n",
			__func__);
		return;
	}
	/* set power pin to output, initially powered off*/
	ret = gpio_direction_output(GPIO_PS_ON, 0);
	if (ret) {
		pr_err("%s Failed in gpio_direction_output, value 0 with error %d\n",
			__func__, ret);
	}
}

static s8 orientation_back[] = {
	-1,  0,  0,
	 0,  1,  0,
	 0,  0, -1,
};

static s8 orientation_back_right_90[] = {
	 0, -1,  0,
	-1,  0,  0,
	 0,  0, -1,
};

static s8 orientation_back_left_90[] = {
	 0,  1,  0,
	 1,  0,  0,
	 0,  0, -1,
};

static s8 orientation_back_180[] = {
	 1,  0,  0,
	 0, -1,  0,
	 0,  0, -1,
};

/*
 * A correction matrix for YAS530
 * which takes care of soft iron effect in TORO
 */
static s32 compass_correction_matrix_toro[] = {
	1072,	-51,	-22,
	-30,	910,	-4,
	-23,	-63,	1024,
};

static void rotcpy(s8 dst[3 * 3], const s8 src[3 * 3])
{
	memcpy(dst, src, 3 * 3);
}

static struct mpu_platform_data mpu_data = {
	.int_config  = 0x10,
	.orientation = {  1,  0,  0,
			  0,  1,  0,
			  0,  0,  1 },
	/* accel */
	.accel = {
		.irq = OMAP_GPIO_IRQ(GPIO_ACC_INT),
		.adapt_num   = 4,
		.bus         = EXT_SLAVE_BUS_SECONDARY,
		.address     = 0x18,
		.orientation = {  1,  0,  0,
				  0,  1,  0,
				  0,  0,  1 },
	},
	/* compass */
	.compass = {
		.irq = OMAP_GPIO_IRQ(GPIO_MAG_INT),
		.adapt_num   = 4,
		.bus         = EXT_SLAVE_BUS_PRIMARY,
		.address     = 0x2E,
		.orientation = {  1,  0,  0,
				  0,  1,  0,
				  0,  0,  1 },
	},
};

static struct gp2a_platform_data gp2a_pdata = {
	.power = gp2a_power,
	.p_out = GPIO_PS_VOUT,
	.light_adc_value = gp2a_light_adc_value,
};

static struct i2c_board_info __initdata tuna_sensors_i2c4_boardinfo[] = {
	{
		I2C_BOARD_INFO("mpu3050", 0x68),
		.irq = OMAP_GPIO_IRQ(GPIO_GYRO_INT),
		.platform_data = &mpu_data,
	},
	{
		I2C_BOARD_INFO("bma250", 0x18),
		.irq = OMAP_GPIO_IRQ(GPIO_ACC_INT),
		.platform_data = &mpu_data.accel,
	},
	{
		I2C_BOARD_INFO("yas530", 0x2e),
		.irq = OMAP_GPIO_IRQ(GPIO_MAG_INT),
		.platform_data = &mpu_data.compass,
	},
	{
		I2C_BOARD_INFO("gp2a", 0x44),
		.platform_data = &gp2a_pdata,
	},
	{
		I2C_BOARD_INFO("bmp180", 0x77),
	},
};

static void omap4_tuna_fixup_orientations_maguro(int revision)
{
	if (revision >= 3) {
		rotcpy(mpu_data.orientation, orientation_back_right_90);
		rotcpy(mpu_data.accel.orientation, orientation_back_left_90);
	} else if (revision >= 2) {
		rotcpy(mpu_data.orientation, orientation_back_right_90);
		rotcpy(mpu_data.accel.orientation, orientation_back_180);
	} else if (revision == 1) {
		rotcpy(mpu_data.accel.orientation, orientation_back_left_90);
	}
}

static void omap4_tuna_fixup_orientations_toro(int revision)
{
	pr_info("HW %d", revision);
	if (revision >= 14) {
		rotcpy(mpu_data.orientation, orientation_back_left_90);
		rotcpy(mpu_data.accel.orientation, orientation_back);
		rotcpy(mpu_data.compass.orientation, orientation_back);
	} else if (revision >= 2) {
		rotcpy(mpu_data.orientation, orientation_back_left_90);
		rotcpy(mpu_data.accel.orientation, orientation_back);
		rotcpy(mpu_data.compass.orientation, orientation_back_180);
	} else if (revision >= 1) {
		rotcpy(mpu_data.orientation, orientation_back_left_90);
		rotcpy(mpu_data.accel.orientation, orientation_back_180);
		rotcpy(mpu_data.compass.orientation, orientation_back_left_90);
	}
}

void __init omap4_tuna_sensors_init(void)
{
	omap_mux_init_gpio(GPIO_GYRO_INT, OMAP_PIN_INPUT);
	omap_mux_init_gpio(GPIO_ACC_INT, OMAP_PIN_INPUT);
	omap_mux_init_gpio(GPIO_MAG_INT, OMAP_PIN_INPUT);
	omap_mux_init_gpio(GPIO_PS_ON, OMAP_PIN_OUTPUT);
	omap_mux_init_gpio(GPIO_PS_VOUT, OMAP_WAKEUP_EN | OMAP_PIN_INPUT);

	gpio_request(GPIO_GYRO_INT, "GYRO_INT");
	gpio_direction_input(GPIO_GYRO_INT);
	gpio_request(GPIO_ACC_INT, "ACC_INT");
	gpio_direction_input(GPIO_ACC_INT);
	gpio_request(GPIO_MAG_INT, "MAG_INT");
	gpio_direction_input(GPIO_MAG_INT);
	gpio_request(GPIO_MSENSE_IRQ, "MSENSE_IRQ");
	gpio_direction_output(GPIO_MSENSE_IRQ, 1);
	/* optical sensor */
	gp2a_gpio_init();

	if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO) {
		omap4_tuna_fixup_orientations_maguro(omap4_tuna_get_revision());
	} else if (omap4_tuna_get_type() == TUNA_TYPE_TORO) {
		omap4_tuna_fixup_orientations_toro(omap4_tuna_get_revision());
		mpu_data.compass.private_data = compass_correction_matrix_toro;
	}

	i2c_register_board_info(4, tuna_sensors_i2c4_boardinfo,
				ARRAY_SIZE(tuna_sensors_i2c4_boardinfo));
}
