blob: aeed997552b8f79280fd5ddfb0883990c447be9a [file] [log] [blame]
/*
*
* MNH MIPI APIs
* Copyright (c) 2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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/delay.h>
#include "mnh-hwio.h"
#include "mnh-hwio-mipi-rx.h"
#include "mnh-hwio-mipi-tx.h"
#include "mnh-hwio-mipi-top.h"
#include "mnh-hwio-bases.h"
#include "mnh-mipi.h"
#define REF_FREQ_KHZ 25000
#define TOP_IN(reg) HW_IN(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, reg)
#define TOP_MASK(reg, fld) HWIO_MIPI_TOP_##reg##_##fld##_FLDMASK
#define TOP_OUTf(reg, fld, val) \
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, reg, fld, val)
#define TOP_OUTf_LOCAL(reg, fld, val, curr) \
((curr & ~HWIO_MIPI_TOP_##reg##_##fld##_FLDMASK) | \
((val << HWIO_MIPI_TOP_##reg##_##fld##_FLDSHFT) & \
HWIO_MIPI_TOP_##reg##_##fld##_FLDMASK))
#define TX_IN(reg) HW_IN(baddr, MIPI_TX, reg)
#define TX_MASK(reg, fld) HWIO_MIPI_TX_##reg##_##fld##_FLDMASK
#define RX_IN(reg) HW_IN(baddr, MIPI_RX, reg)
#define RX_MASK(reg, fld) HWIO_MIPI_RX_##reg##_##fld##_FLDMASK
struct mipi_rate_config {
uint32_t rate;
uint32_t freq_range_code;
uint32_t osc_freq_code;
};
struct mnh_mipi_vco_range_cntrl {
uint32_t freq_kHz;
uint8_t cntrl;
};
/* MIPI Timing override registers */
struct mipi_dev_ovr_cfg {
uint32_t rate;
/* Timing override registers */
uint32_t reg_0x5A;
uint32_t reg_0x5B;
uint32_t reg_0x5C;
uint32_t reg_0x5D;
uint32_t reg_0x5E;
uint32_t reg_0x5F;
uint32_t reg_0x61;
uint32_t reg_0x62;
uint32_t reg_0x63;
uint32_t reg_0x64;
uint32_t reg_0x65;
/* PHY_STOP_WAIT_TIME */
uint32_t wait_time; /* Table A-4 High-speed exit */
bool use_ovrd;
};
/* must be in ascending order */
static struct mipi_rate_config mipi_rate_configs[] = {
/* rate freq_range_code osc_freq_code */
{ 80, 0x00, 0x1b6},
{ 90, 0x10, 0x1b6},
{ 100, 0x20, 0x1b6},
{ 110, 0x30, 0x1b6},
{ 120, 0x01, 0x1b6},
{ 130, 0x11, 0x1b6},
{ 140, 0x21, 0x1b6},
{ 150, 0x31, 0x1b6},
{ 160, 0x02, 0x1b6},
{ 170, 0x12, 0x1b6},
{ 180, 0x22, 0x1b6},
{ 190, 0x32, 0x1b6},
{ 205, 0x03, 0x1b6},
{ 220, 0x13, 0x1b6},
{ 235, 0x23, 0x1b6},
{ 250, 0x33, 0x1b6},
{ 275, 0x04, 0x1b6},
{ 300, 0x14, 0x1b6},
{ 325, 0x25, 0x1b6},
{ 350, 0x35, 0x1b6},
{ 400, 0x05, 0x1b6},
{ 450, 0x16, 0x1b6},
{ 500, 0x26, 0x1b6},
{ 550, 0x37, 0x1b6},
{ 600, 0x07, 0x1b6},
{ 650, 0x18, 0x1b6},
{ 700, 0x28, 0x1b6},
{ 750, 0x39, 0x1b6},
{ 800, 0x09, 0x1b6},
{ 850, 0x19, 0x1b6},
{ 900, 0x29, 0x1b6},
{ 950, 0x3a, 0x1b6},
{ 1000, 0x0a, 0x1b6},
{ 1050, 0x1a, 0x1b6},
{ 1100, 0x2a, 0x1b6},
{ 1150, 0x3b, 0x1b6},
{ 1200, 0x0b, 0x1b6},
{ 1250, 0x1b, 0x1b6},
{ 1300, 0x2b, 0x1b6},
{ 1350, 0x3c, 0x1b6},
{ 1400, 0x0c, 0x1b6},
{ 1450, 0x1c, 0x1b6},
{ 1500, 0x2c, 0x1b6},
{ 1550, 0x3d, 0x10f},
{ 1600, 0x0d, 0x118},
{ 1650, 0x1d, 0x121},
{ 1700, 0x2e, 0x12a},
{ 1750, 0x3e, 0x132},
{ 1800, 0x0e, 0x13b},
{ 1850, 0x1e, 0x144},
{ 1900, 0x2f, 0x14d},
{ 1950, 0x3f, 0x155},
{ 2000, 0x0f, 0x15e},
{ 2050, 0x40, 0x167},
{ 2100, 0x41, 0x170},
{ 2150, 0x42, 0x178},
{ 2200, 0x43, 0x181},
{ 2250, 0x44, 0x18a},
{ 2300, 0x45, 0x193},
{ 2350, 0x46, 0x19b},
{ 2400, 0x47, 0x1a4},
{ 2450, 0x48, 0x1ad},
{ 2500, 0x49, 0x1b6},
};
/* must be in ascending order */
static struct mnh_mipi_vco_range_cntrl vco_range_cntrls[] = {
/* freq_kHz cntrl */
{ 40000, 0x3f },
{ 52500, 0x39 },
{ 80000, 0x2f },
{ 105000, 0x29 },
{ 160000, 0x1f },
{ 210000, 0x19 },
{ 320000, 0x0f },
{ 420000, 0x09 },
{ 630000, 0x03 },
{ 1100000, 0x01 },
{ 1150000, 0x01 },
};
static struct mipi_dev_ovr_cfg mipi_dev_ovr_cfgs[] = {
/* rate 0x05A 0x05B 0x05C 0x05D 0x05E 0x05F 0x061 0x062 0x063 0x064 0x065 wait_time */
{ 340, 0x57, 0x43, 0xC2, 0x44, 0x42, 0x8A, 0x43, 0xC3, 0x44, 0x43, 0x83, 0x14, true},
{ 360, 0x57, 0x43, 0xC2, 0x44, 0x42, 0x8A, 0x43, 0xC3, 0x44, 0x43, 0x83, 0x14, false},
{ 446, 0x57, 0x45, 0xC3, 0x9D, 0x43, 0x8E, 0x45, 0xC3, 0x9D, 0x44, 0x84, 0x17, true},
{ 488, 0x58, 0x45, 0xC3, 0x03, 0x43, 0x90, 0x45, 0xC3, 0x03, 0x44, 0x85, 0x18, true},
{ 550, 0x4C, 0x48, 0xC5, 0x03, 0x44, 0x91, 0x47, 0xC6, 0x03, 0x43, 0x85, 0x1A, true},
{ 648, 0x4E, 0x48, 0xC5, 0x05, 0x47, 0x94, 0x48, 0xC5, 0x05, 0x47, 0x85, 0x1C, true},
{ 685, 0x4C, 0x48, 0xC5, 0x03, 0x44, 0x91, 0x47, 0xC6, 0x03, 0x43, 0x85, 0x1D, true},
{ 720, 0x4E, 0x49, 0xC5, 0x05, 0x48, 0x96, 0x49, 0xC6, 0x05, 0x48, 0x86, 0x1D, true},
{ 984, 0x50, 0x4D, 0xC7, 0x05, 0x48, 0xA0, 0x4D, 0xC8, 0x05, 0x4A, 0x8A, 0x26, true},
{ 1104, 0x5D, 0x4F, 0xC6, 0x09, 0x49, 0xA7, 0x4F, 0xC9, 0x07, 0x4B, 0x8B, 0x27, true},
{ 1200, 0x53, 0x51, 0xCA, 0x09, 0x4E, 0xA7, 0x50, 0xCC, 0x07, 0x4B, 0x8D, 0x2B, true},
{ 1296, 0x53, 0x51, 0xCA, 0x09, 0x4E, 0xA7, 0x50, 0xCC, 0x07, 0x4C, 0x8D, 0x2E, true},
{ 1300, 0x53, 0x51, 0xCA, 0x09, 0x4E, 0xA7, 0x50, 0xCC, 0x07, 0x4C, 0x8D, 0x2E, true},
{ 1368, 0x53, 0x51, 0xCA, 0x09, 0x4E, 0xA7, 0x50, 0xCC, 0x08, 0x4C, 0x8D, 0x2F, true},
{ 1776, 0x58, 0x59, 0xCD, 0x0B, 0x55, 0xC0, 0x50, 0xCE, 0x0B, 0x52, 0x90, 0x36, true},
{ 1850, 0x58, 0x59, 0xCD, 0x0B, 0x55, 0xC0, 0x59, 0xCE, 0x0B, 0x52, 0x98, 0x36, true},
{ 1900, 0x58, 0x59, 0xCD, 0x0B, 0x55, 0xC0, 0x59, 0xCE, 0x0B, 0x52, 0x98, 0x1B, true},
{ 2000, 0x58, 0x59, 0xCD, 0x0B, 0x55, 0xC0, 0x59, 0xCE, 0x0B, 0x52, 0x98, 0x1B, true},
{ 2100, 0x9A, 0x9B, 0xD0, 0x0E, 0x50, 0xC0, 0x39, 0xD3, 0x0C, 0x4A, 0xA7, 0x1E, true},
};
static int mipi_debug;
static void mnh_sm_mipi_rx_dphy_write_gen3(int command, int data,
uint32_t device)
{
/* Write 4-bit testcode MSB */
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL0,
PHY_TESTCLK, 0);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL1,
PHY_TESTEN, 0);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL1,
PHY_TESTEN, 1);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL0,
PHY_TESTCLK, 1);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL1,
PHY_TESTDIN, 0);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL0,
PHY_TESTCLK, 0);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL1,
PHY_TESTEN, 0);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL1,
PHY_TESTDIN, ((command & 0xF00) >> 8));
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL0,
PHY_TESTCLK, 1);
/* Write 8-bit testcode LSB */
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL0,
PHY_TESTCLK, 0);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL1,
PHY_TESTEN, 1);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL0,
PHY_TESTCLK, 1);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL1,
PHY_TESTDIN, (command & 0xFF));
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL0,
PHY_TESTCLK, 0);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL1,
PHY_TESTEN, 0);
/* Write the data */
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL1,
PHY_TESTDIN, data);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL0,
PHY_TESTCLK, 1);
udelay(1);
}
static void mnh_sm_mipi_tx_dphy_write_gen3(int command, int data,
uint32_t device)
{
/* Write 4-bit testcode MSB */
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL0,
PHY0_TESTCLK, 0);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL1,
PHY0_TESTEN, 0);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL1,
PHY0_TESTEN, 1);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL0,
PHY0_TESTCLK, 1);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL1,
PHY0_TESTDIN, 0);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL0,
PHY0_TESTCLK, 0);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL1,
PHY0_TESTEN, 0);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL1,
PHY0_TESTDIN, ((command & 0xF00) >> 8));
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL0,
PHY0_TESTCLK, 1);
/* Write 8-bit testcode LSB */
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL0,
PHY0_TESTCLK, 0);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL1,
PHY0_TESTEN, 1);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL0,
PHY0_TESTCLK, 1);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL1,
PHY0_TESTDIN, (command & 0xFF));
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL0,
PHY0_TESTCLK, 0);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL1,
PHY0_TESTEN, 0);
/* Write the data */
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL1,
PHY0_TESTDIN, data);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL0,
PHY0_TESTCLK, 1);
udelay(1);
}
/* Bugfix for b/63578602 */
static void mnh_mipi_gen3_lprxpon_wa(struct device *dev, u32 device, int enable)
{
dev_dbg(dev, "%s: dev %d\n", __func__, device);
if (enable == 1) {
mnh_sm_mipi_rx_dphy_write_gen3(0x1AE, 0x07, device);
mnh_sm_mipi_rx_dphy_write_gen3(0x1AD, 0xe0, device);
mnh_sm_mipi_rx_dphy_write_gen3(0x305, 0x06, device);
mnh_sm_mipi_rx_dphy_write_gen3(0x505, 0x06, device);
mnh_sm_mipi_rx_dphy_write_gen3(0x705, 0x06, device);
mnh_sm_mipi_rx_dphy_write_gen3(0x905, 0x06, device);
mnh_sm_mipi_rx_dphy_write_gen3(0xB05, 0x06, device);
} else {
mnh_sm_mipi_rx_dphy_write_gen3(0x1AE, 0x00, device);
mnh_sm_mipi_rx_dphy_write_gen3(0x1AD, 0x00, device);
mnh_sm_mipi_rx_dphy_write_gen3(0x305, 0x00, device);
mnh_sm_mipi_rx_dphy_write_gen3(0x505, 0x00, device);
mnh_sm_mipi_rx_dphy_write_gen3(0x705, 0x00, device);
mnh_sm_mipi_rx_dphy_write_gen3(0x905, 0x00, device);
mnh_sm_mipi_rx_dphy_write_gen3(0xB05, 0x00, device);
}
}
static int mnh_mipi_gen3_lookup_freq_code(uint32_t rate)
{
int i;
if ((rate < mipi_rate_configs[0].rate) ||
(rate > mipi_rate_configs[ARRAY_SIZE(mipi_rate_configs)-1].rate))
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(mipi_rate_configs); i++) {
if (rate < mipi_rate_configs[i].rate)
return i - 1;
}
return i - 1;
}
static int mnh_mipi_gen3_lookup_device_cfg_idx(uint32_t rate)
{
int i;
if ((rate < mipi_dev_ovr_cfgs[0].rate) ||
(rate > mipi_dev_ovr_cfgs[ARRAY_SIZE(mipi_dev_ovr_cfgs)-1].rate))
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(mipi_dev_ovr_cfgs); i++) {
if (rate <= mipi_dev_ovr_cfgs[i].rate)
return i;
}
return i - 1;
}
static void mnh_mipi_gen3_host(struct device *dev, uint32_t device,
uint32_t rate)
{
uint32_t code_index, freq_range_code, osc_freq_code;
/* only support devices 0-2 */
if (device > 2)
return;
dev_dbg(dev, "%s: dev %d, rate %d\n", __func__, device, rate);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, N_LANES, N_LANES, 0x0);
/* clear interrupts */
HW_OUT(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, INT_MSK_PHY_FATAL,
0xFFFFFFFF);
HW_OUT(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, INT_MSK_PKT_FATAL,
0xFFFFFFFF);
HW_OUT(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, INT_MSK_FRAME_FATAL,
0xFFFFFFFF);
HW_OUT(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, INT_MSK_PHY,
0xFFFFFFFF);
HW_OUT(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, INT_MSK_PKT,
0xFFFFFFFF);
HW_OUT(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, INT_MSK_LINE,
0xFFFFFFFF);
/* enable clock to controller */
if (device == 0)
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, CSI_CLK_CTRL,
CSI2_RX0_CG, 0x0);
else if (device == 1)
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, CSI_CLK_CTRL,
CSI2_RX1_CG, 0x0);
else
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, CSI_CLK_CTRL,
CSI2_RX2_CG, 0x0);
/* disable the PHY before changing settings */
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_SHUTDOWNZ,
PHY_SHUTDOWNZ, 0x0);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, DPHY_RSTZ, DPHY_RSTZ,
0x0);
HW_OUT(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL0, 0x1);
udelay(1);
HW_OUT(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_TEST_CTRL0, 0x0);
/* get the PHY settings */
code_index = mnh_mipi_gen3_lookup_freq_code(rate);
if (code_index < 0)
return;
freq_range_code = mipi_rate_configs[code_index].freq_range_code;
osc_freq_code = mipi_rate_configs[code_index].osc_freq_code;
/* update PHY configuration */
if (device == 0)
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, RX0_DPHY_CONFIG,
0x8400 | freq_range_code);
else if (device == 1)
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, RX1_DPHY_CONFIG,
0x8400 | freq_range_code);
else
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, RX2_DPHY_CONFIG,
0x8400 | freq_range_code);
/* update PHY PLL settings */
mnh_sm_mipi_rx_dphy_write_gen3(0xe2, osc_freq_code & 0xFF, device);
mnh_sm_mipi_rx_dphy_write_gen3(0xe3, osc_freq_code >> 8, device);
mnh_sm_mipi_rx_dphy_write_gen3(0xe4, 0x01, device);
mnh_sm_mipi_rx_dphy_write_gen3(0x08, 0x20, device);
udelay(1);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, N_LANES, N_LANES, 0x3);
udelay(1);
/* b/63578602 */
mnh_mipi_gen3_lprxpon_wa(dev, device, 1);
/* release reset */
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, PHY_SHUTDOWNZ,
PHY_SHUTDOWNZ, 0x1);
udelay(20);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, DPHY_RSTZ, DPHY_RSTZ,
0x1);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(device), MIPI_RX, CSI2_RESETN,
CSI2_RESETN, 0x1);
udelay(30);
mnh_mipi_gen3_lprxpon_wa(dev, device, 0);
}
static uint8_t mnh_mipi_get_vco_cntrl(uint32_t rate)
{
int i;
/* vco frequency is half the desired rate */
uint32_t freq_kHz = rate * 1000 / 2;
for (i = 0; i < ARRAY_SIZE(vco_range_cntrls); i++) {
if (freq_kHz < vco_range_cntrls[i].freq_kHz)
return vco_range_cntrls[i - 1].cntrl;
}
return vco_range_cntrls[i].cntrl;
}
/* M is required to be between 64 and 625 */
/* N is required to be between 6 and 16 */
static void mnh_mipi_get_pll_coeffs(uint32_t rate, uint16_t *m, uint8_t *n)
{
uint32_t freq_kHz = rate * 1000 / 2; /* to not lose precision */
int vco_div_factor;
int est_ratio_scaled; /* M/N ratio scaled by 1000 to losing precision */
int iter_n, iter_m;
int best_error = INT_MAX;
int best_n = 0, best_m = 0;
int calc_ratio_scaled;
/* determine the vco division factor */
if (freq_kHz < 80000)
vco_div_factor = 8;
else if (freq_kHz < 160000)
vco_div_factor = 4;
else if (freq_kHz < 320000)
vco_div_factor = 2;
else
vco_div_factor = 1;
/* determine desired ratio, scaled by 1000 to avoid losing precision */
est_ratio_scaled = freq_kHz * 1000 * vco_div_factor / REF_FREQ_KHZ;
/* search through values of N and find ratio with least error */
for (iter_n = 4; iter_n <= 12; iter_n++) {
/* always round up so we don't get lower tx rate than we want */
iter_m = (est_ratio_scaled * iter_n + 999) / 1000;
calc_ratio_scaled = iter_m * 1000 / iter_n;
if (iter_m < 64)
continue;
else if (iter_m > 625)
break;
/* calculate the error given this M and N combo */
if ((calc_ratio_scaled - est_ratio_scaled) < best_error) {
best_n = iter_n;
best_m = iter_m;
best_error = calc_ratio_scaled - est_ratio_scaled;
}
}
/* assign the outputs */
*m = best_m;
*n = best_n;
}
static void mnh_mipi_gen3_device(struct device *dev, uint32_t device,
uint32_t rate)
{
uint32_t code_index, freq_range_code, osc_freq_code;
struct mipi_dev_ovr_cfg *dev_ovr_cfg;
uint32_t mipi_dev_cfg_index;
unsigned long data;
uint8_t vco_cntrl;
uint16_t pll_m;
uint8_t pll_n;
int i = 0;
dev_dbg(dev, "%s: dev %d, rate %d\n", __func__, device, rate);
if (!mipi_debug) {
if (rate <= 360) {
dev_dbg(dev, "%s: %d config. default 360\n",
__func__, rate);
rate = 360;
} else if (rate <= 550) { /* debug mode */
dev_dbg(dev, "%s: %d config. default 550\n",
__func__, rate);
rate = 550;
} else if (rate <= 685) { /* front cam photo */
dev_dbg(dev, "%s: %d config. default 685\n",
__func__, rate);
rate = 685;
} else if (rate <= 720) { /* main cam 60fps */
dev_dbg(dev, "%s: %d config. default 720\n",
__func__, rate);
rate = 720;
} else if (rate <= 984) { /* main cam 30fps */
dev_dbg(dev, "%s: %d config. default 984\n",
__func__, rate);
rate = 984;
} else if (rate <= 1104) { /* main cam slo.mo. 120fps */
dev_dbg(dev, "%s: %d config. default 1104\n",
__func__, rate);
rate = 1104;
} else if (rate <= 1200) {
dev_dbg(dev, "%s: %d config. default 1200\n",
__func__, rate);
rate = 1200;
} else if (rate <= 1368) {
dev_dbg(dev, "%s: %d config. default 1368\n",
__func__, rate);
rate = 1368;
} else if (rate <= 1850) {
dev_dbg(dev, "%s: %d config. default 1850\n",
__func__, rate);
rate = 1850;
}
}
/* only support devices 0-1 */
if (device > 1)
return;
if (device == 0)
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, CSI_CLK_CTRL,
CSI2_TX0_CG, 0x0);
else
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, CSI_CLK_CTRL,
CSI2_TX1_CG, 0x0);
/* mipicsi_device_hw_init */
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY_RSTZ,
PHY_SHUTDOWNZ, 0x1);
/* mipicsi_device_dphy_reset */
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY_RSTZ, PHY_RSTZ,
0x1);
udelay(1000);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY_RSTZ, PHY_RSTZ,
0x0);
HW_OUT(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, CSI2_RESETN, 0x0);
udelay(1);
HW_OUT(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, CSI2_RESETN, 0x1);
HW_OUT(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, INT_MASK_N_VPG,
0xFFFFFFFF);
HW_OUT(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, INT_MASK_N_IDI,
0xFFFFFFFF);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY_RSTZ,
PHY_SHUTDOWNZ,
0x0);
/* disable clock gating */
if (device == 0) {
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, TX0_DPHY_PLL_CNTRL,
PLL_SHADOW_CONTROL, 0x1);
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, TX0_DPHY_PLL_CNTRL,
CLK_SEL, 0x1);
} else {
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, TX1_DPHY_PLL_CNTRL,
PLL_SHADOW_CONTROL, 0x1);
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, TX1_DPHY_PLL_CNTRL,
CLK_SEL, 0x1);
}
/* disable the PHY before changing settings */
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, CSI2_RESETN,
CSI2_RESETN_RW, 1);
HW_OUT(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY_RSTZ, 0x0);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY_RSTZ,
PHY_ENABLECLK, 1);
HW_OUT(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL0, 0x1);
udelay(1);
HW_OUT(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY0_TST_CTRL0, 0x0);
/* get the PHY settings */
code_index = mnh_mipi_gen3_lookup_freq_code(rate);
if (code_index < 0)
return;
freq_range_code = mipi_rate_configs[code_index].freq_range_code;
osc_freq_code = mipi_rate_configs[code_index].osc_freq_code;
/* update PHY configuration */
if (device == 0)
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, TX0_DPHY_CONFIG,
0x8400 | freq_range_code);
else
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, TX1_DPHY_CONFIG,
0x8400 | freq_range_code);
/* configure slew rate calibration */
if (rate > 1500) {
mnh_sm_mipi_tx_dphy_write_gen3(0x26B, 0x44, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x272, 0x00, device);
} else if (rate > 1000) {
mnh_sm_mipi_tx_dphy_write_gen3(0x270, 0xD0, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x271, 0x07, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x272, 0x10, device);
} else if (rate > 500) {
mnh_sm_mipi_tx_dphy_write_gen3(0x270, 0xE2, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x271, 0x04, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x272, 0x11, device);
} else {
mnh_sm_mipi_tx_dphy_write_gen3(0x270, 0x84, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x271, 0x03, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x272, 0x11, device);
}
/* get PLL coefficients and codes */
vco_cntrl = mnh_mipi_get_vco_cntrl(rate);
mnh_mipi_get_pll_coeffs(rate, &pll_m, &pll_n);
/* mipi device configuration settings from lookup table */
mipi_dev_cfg_index = mnh_mipi_gen3_lookup_device_cfg_idx(rate);
if (mipi_dev_cfg_index < 0)
return;
dev_ovr_cfg = &mipi_dev_ovr_cfgs[mipi_dev_cfg_index];
dev_dbg(dev, "%s: Device configuration index: %d\n", __func__,
mipi_dev_cfg_index);
/* adjust the values to meet the register definition */
pll_m -= 2;
pll_n -= 1;
/* configure PLL frequency multiplication and division factors */
mnh_sm_mipi_tx_dphy_write_gen3(0x17B, ((vco_cntrl & 0x3F) << 1) | 0x81,
device);
mnh_sm_mipi_tx_dphy_write_gen3(0x178, 0x80 | ((pll_n & 0xF) << 3),
device);
mnh_sm_mipi_tx_dphy_write_gen3(0x179, pll_m & 0xFF, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x17A, (pll_m >> 8) & 0x3, device);
/* configure rate-independent PLL settings */
mnh_sm_mipi_tx_dphy_write_gen3(0x15E, 0x10, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x162, 0x04, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x16E, 0x0C, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x173, 0x02, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x174, 0x00, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x175, 0x60, device);
mnh_sm_mipi_tx_dphy_write_gen3(0x176, 0x03, device);
/* TODO: Maybe do a read-modify-write */
if (rate <= 450)
mnh_sm_mipi_tx_dphy_write_gen3(0x1AC, 1 << 4, device);
/* Wait for 15ns */
udelay(1);
/* Timing register overrides */
if (!mipi_debug && dev_ovr_cfg->use_ovrd) {
mnh_sm_mipi_tx_dphy_write_gen3(0x5A, dev_ovr_cfg->reg_0x5A,
device);
mnh_sm_mipi_tx_dphy_write_gen3(0x5B, dev_ovr_cfg->reg_0x5B,
device);
mnh_sm_mipi_tx_dphy_write_gen3(0x5C, dev_ovr_cfg->reg_0x5C,
device);
mnh_sm_mipi_tx_dphy_write_gen3(0x5D, dev_ovr_cfg->reg_0x5D,
device);
/*
* mnh_sm_mipi_tx_dphy_write_gen3(0x5E, dev_ovr_cfg->reg_0x5E,
* device);
*/
mnh_sm_mipi_tx_dphy_write_gen3(0x5F, dev_ovr_cfg->reg_0x5F,
device);
mnh_sm_mipi_tx_dphy_write_gen3(0x61, dev_ovr_cfg->reg_0x61,
device);
mnh_sm_mipi_tx_dphy_write_gen3(0x62, dev_ovr_cfg->reg_0x62,
device);
mnh_sm_mipi_tx_dphy_write_gen3(0x63, dev_ovr_cfg->reg_0x63,
device);
/*
* mnh_sm_mipi_tx_dphy_write_gen3(0x64, dev_ovr_cfg->reg_0x64,
* device);
*/
mnh_sm_mipi_tx_dphy_write_gen3(0x65, dev_ovr_cfg->reg_0x65,
device);
}
/* Enable high speed drivers (required for Tlpx < 500ns) */
/* Clock lane driver */
mnh_sm_mipi_tx_dphy_write_gen3(0x304, (1 << 2) | (1 << 3), device);
/* Data lane 0 driver */
mnh_sm_mipi_tx_dphy_write_gen3(0x504, (1 << 2) | (1 << 3), device);
/* Data lane 1 driver */
mnh_sm_mipi_tx_dphy_write_gen3(0x704, (1 << 2) | (1 << 3), device);
/* Data lane 2 driver */
mnh_sm_mipi_tx_dphy_write_gen3(0x904, (1 << 2) | (1 << 3), device);
/* Data lane 3 driver */
mnh_sm_mipi_tx_dphy_write_gen3(0xB04, (1 << 2) | (1 << 3), device);
/* Enable lanes */
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY_IF_CFG,
LANE_EN_NUM, 3);
/* Table A-4 High-Speed Transition Times: Data HS-LP */
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY_IF_CFG,
PHY_STOP_WAIT_TIME, dev_ovr_cfg->wait_time);
/* enable the PHY */
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY_RSTZ,
PHY_ENABLECLK, 1);
udelay(1);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY_RSTZ,
PHY_SHUTDOWNZ, 1);
udelay(1);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX, PHY_RSTZ, PHY_RSTZ, 1);
/* wait for data & clk lanes to reach the low-power state */
i = 0;
do {
data = HW_IN(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX,
PHY_STATUS);
udelay(10);
i++;
} while ((i < 40) && ((data & 0x1550) != 0x1550));
if ((i >= 100) && ((data & 0x1550) != 0x1550))
dev_err(dev,
"device %d could not drive lp state, status 0x%lx\n",
device, data);
/* INIT period in continuous clock mode is software controlled */
udelay(1000); /* relevant timing parameter, do not remove */
/* Enable ct clock, this takes immediate effect */
dev_dbg(dev, "%s: enabling ct. clock mode\n", __func__);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(device), MIPI_TX,
LPCLK_CTRL, PHY_TXREQCLKHS_CON, 1);
}
static int mnh_mipi_config_mux_sel(struct device *dev,
struct mnh_mipi_config *config)
{
uint32_t rxdev, txdev, mode;
uint32_t vc_en_mask, byp_tx_en_mask, ipu_en_mask;
uint32_t tx_func_mask, tx_byp_sel_mask;
uint32_t rx_mode, tx_mode;
rxdev = config->rxdev;
txdev = config->txdev;
vc_en_mask = config->vc_en_mask;
mode = config->mode;
dev_dbg(dev, "%s: MUX rxdev %d, txdev %d, vc_en_mask 0x%1x, mode %d\n",
__func__, rxdev, txdev, vc_en_mask, mode);
/*
* 0. Upper layers should issue a MUX reset prior to configuring
* the MUX
* 1. Configure RX section of the MUX unless rxdev is MIPI_RX_IPU
* in that case we are in functional mode IPU -> Tx[n] and only
* the tx_mode register must be changed
* 2. Configure TX section of the MUX unless txdev is MIPI_TX_IPU
* in that case we are in functional mode Rx[n] -> IPU and only
* the rx_mode register must be changed
*/
/*
* construct the rx mode register. This register is synchronized with
* the IDI domain when the RX clock is present. However, if the clock is
* not present, only the first write to the register will succeed.
* Therefore, use read-modify-write to construct register so we only
* need to write once.
*/
if (rxdev == MIPI_RX0)
rx_mode = HW_IN(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, RX0_MODE);
else if (rxdev == MIPI_RX1)
rx_mode = HW_IN(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, RX1_MODE);
else if (rxdev == MIPI_RX2)
rx_mode = HW_IN(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, RX2_MODE);
else
rx_mode = 0; /* rx_mode register not required for IPU source */
/*
* it doesn't actually matter which RX# is used since this is just to
* create the mask. The actual register write occurs below.
*/
rx_mode &= ~HWIO_MIPI_TOP_RX0_MODE_RX0_VC_EN_FLDMASK;
rx_mode &= ~HWIO_MIPI_TOP_RX0_MODE_RX0_BYP_TX0_EN_FLDMASK;
rx_mode &= ~HWIO_MIPI_TOP_RX0_MODE_RX0_BYP_TX1_EN_FLDMASK;
rx_mode &= ~HWIO_MIPI_TOP_RX0_MODE_RX0_IPU_EN_FLDMASK;
vc_en_mask <<= HWIO_MIPI_TOP_RX0_MODE_RX0_VC_EN_FLDSHFT;
vc_en_mask &= HWIO_MIPI_TOP_RX0_MODE_RX0_VC_EN_FLDMASK;
/* Bypass mode to one of the two Tx channels */
if (mode == MIPI_MODE_BYPASS) { /* no IPU, TXn_EN */
byp_tx_en_mask =
HWIO_MIPI_TOP_RX0_MODE_RX0_BYP_TX0_EN_FLDMASK << txdev;
ipu_en_mask = 0;
tx_func_mask = 0;
tx_byp_sel_mask = (1 + rxdev)
<< HWIO_MIPI_TOP_TX0_MODE_TX0_BYP_SEL_FLDSHFT;
/* Bypass mode with IPU sink enabled */
} else if (mode == MIPI_MODE_BYPASS_W_IPU) {
byp_tx_en_mask =
HWIO_MIPI_TOP_RX0_MODE_RX0_BYP_TX0_EN_FLDMASK << txdev;
ipu_en_mask = 1 << HWIO_MIPI_TOP_RX0_MODE_RX0_IPU_EN_FLDSHFT;
tx_func_mask = 0;
tx_byp_sel_mask = (1 + rxdev)
<< HWIO_MIPI_TOP_TX0_MODE_TX0_BYP_SEL_FLDSHFT;
/* Functional mode, direct bypass between Rx & TX disabled */
} else if (mode == MIPI_MODE_FUNCTIONAL) {
byp_tx_en_mask = 0;
ipu_en_mask = 1 << HWIO_MIPI_TOP_RX0_MODE_RX0_IPU_EN_FLDSHFT;
tx_func_mask = 1 << HWIO_MIPI_TOP_TX0_MODE_TX0_FUNC_FLDSHFT;
tx_byp_sel_mask = 0; /* do not care */
} else {
return -EINVAL;
}
rx_mode = (rx_mode | vc_en_mask | byp_tx_en_mask | ipu_en_mask);
tx_mode = (tx_func_mask | tx_byp_sel_mask);
dev_dbg(dev, "%s: rx_mode=%d | tx_mode=%d\n", __func__,
rx_mode, tx_mode);
switch (txdev) { /* Sink */
case MIPI_TX0:
switch (rxdev) { /* Source */
case MIPI_RX0: /* RX0 -> TX0 */
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, RX0_MODE, rx_mode);
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, TX0_MODE, tx_mode);
break;
case MIPI_RX1: /* RX1 -> TX0 */
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, RX1_MODE, rx_mode);
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, TX0_MODE, tx_mode);
break;
case MIPI_RX2: /* RX2 -> TX0 */
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, RX2_MODE, rx_mode);
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, TX0_MODE, tx_mode);
break;
case MIPI_RX_IPU: /* IPU -> TX0 */
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, TX0_MODE, tx_mode);
break;
default:
dev_err(dev, "%s: invalid rx device %d!\n", __func__,
rxdev);
return -EINVAL;
}
break;
case MIPI_TX1:
switch (rxdev) {
case MIPI_RX0: /* RX0 -> TX1 */
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, RX0_MODE, rx_mode);
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, TX1_MODE, tx_mode);
break;
case MIPI_RX1: /* RX1 -> TX1 */
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, RX1_MODE, rx_mode);
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, TX1_MODE, tx_mode);
break;
case MIPI_RX2: /* RX2 -> TX1 */
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, RX2_MODE, rx_mode);
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, TX1_MODE, tx_mode);
break;
case MIPI_RX_IPU:
/* Configure IPU -> Tx1 */
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, TX1_MODE, tx_mode);
break;
default:
dev_err(dev, "%s: invalid rx device %d!\n", __func__,
rxdev);
return -EINVAL;
}
break;
case MIPI_TX_IPU:
switch (rxdev) {
case MIPI_RX0: /* Rx0 -> IPU */
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, RX0_MODE, rx_mode);
break;
case MIPI_RX1:
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, RX1_MODE, rx_mode);
break;
case MIPI_RX2:
HW_OUT(HWIO_MIPI_TOP_BASE_ADDR,
MIPI_TOP, RX2_MODE, rx_mode);
break;
default:
dev_err(dev, "%s: invalid rx device %d!\n", __func__,
rxdev);
return -EINVAL;
}
break;
default:
dev_err(dev, "%s: invalid tx device %d!\n", __func__, txdev);
return -EINVAL;
}
return 0;
}
int mnh_mipi_config(struct device *dev, struct mnh_mipi_config config)
{
uint32_t txdev = config.txdev;
uint32_t rxdev = config.rxdev;
uint32_t rx_rate = config.rx_rate;
uint32_t tx_rate = config.tx_rate;
uint32_t vc_en_mask = config.vc_en_mask;
dev_dbg(dev, "%s: init rxdev %d, txdev %d, rx_rate %d, tx_rate %d, vc_en_mask 0x%1x\n",
__func__, rxdev, txdev, rx_rate, tx_rate, vc_en_mask);
/* configure rx / MIPI-source */
switch (rxdev) {
case MIPI_RX0:
case MIPI_RX1:
case MIPI_RX2:
dev_dbg(dev, "%s: configuring host controller Rx%d\n",
__func__, rxdev);
mnh_mipi_gen3_host(dev, rxdev, rx_rate);
break;
case MIPI_RX_IPU:
dev_dbg(dev, "%s: configuring IPU IDI Tx%d as MIPI source\n",
__func__, txdev);
break;
default:
dev_dbg(dev, "%s: Invalid MIPI input device\n", __func__);
break;
}
/* configure tx / MIPI-sink */
switch (txdev) {
case MIPI_TX0:
case MIPI_TX1:
dev_dbg(dev, "%s: configuring device controller Tx%d\n",
__func__, txdev);
mnh_mipi_gen3_device(dev, txdev, tx_rate);
break;
case MIPI_TX_IPU:
dev_dbg(dev, "%s: configuring IPU IDI Rx%d as MIPI sink\n",
__func__, rxdev);
break;
default:
dev_dbg(dev, "%s: Invalid MIPI output device\n", __func__);
break;
}
/* configure mux select */
mnh_mipi_config_mux_sel(dev, &config);
return 0;
}
EXPORT_SYMBOL_GPL(mnh_mipi_config);
int mnh_mipi_stop(struct device *dev, struct mnh_mipi_config config)
{
uint32_t txdev = config.txdev;
uint32_t rxdev = config.rxdev;
dev_dbg(dev, "%s: stopping rxdev %d, txdev %d\n", __func__, rxdev,
txdev);
/* Shutdown host */
mnh_mipi_stop_host(dev, rxdev);
/* Shutdown device */
mnh_mipi_stop_device(dev, txdev);
return 0;
}
EXPORT_SYMBOL_GPL(mnh_mipi_stop);
void mnh_mipi_stop_device(struct device *dev, int txdev)
{
/* shut down the PHY */
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(txdev), MIPI_TX, PHY_RSTZ,
PHY_RSTZ, 0);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(txdev), MIPI_TX, PHY_RSTZ,
PHY_SHUTDOWNZ, 0);
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(txdev), MIPI_TX, PHY_RSTZ,
PHY_ENABLECLK, 0);
/* shut down the controller logic */
HW_OUTf(HWIO_MIPI_TX_BASE_ADDR(txdev), MIPI_TX, CSI2_RESETN,
CSI2_RESETN_RW, 0);
/* enable clock gating (disable clock) */
switch (txdev) {
case MIPI_TX0:
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, CSI_CLK_CTRL,
CSI2_TX0_CG, 0x1);
break;
case MIPI_TX1:
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, CSI_CLK_CTRL,
CSI2_TX1_CG, 0x1);
break;
default:
dev_err(dev, "%s invalid mipi device!\n", __func__);
break;
}
}
EXPORT_SYMBOL_GPL(mnh_mipi_stop_device);
void mnh_mipi_stop_host(struct device *dev, int rxdev)
{
/* shut down the PHY */
/* see 7.3.1: Rx-DPHY databook */
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(rxdev), MIPI_RX, PHY_SHUTDOWNZ,
PHY_SHUTDOWNZ, 0x0);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(rxdev), MIPI_RX, DPHY_RSTZ,
DPHY_RSTZ, 0x0);
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(rxdev), MIPI_RX, PHY_TEST_CTRL0,
PHY_TESTCLR, 0x1);
/* shut down the controller logic */
HW_OUTf(HWIO_MIPI_RX_BASE_ADDR(rxdev), MIPI_RX, CSI2_RESETN,
CSI2_RESETN, 0x0);
/* enable clock gating (disable clock) */
switch (rxdev) {
case MIPI_RX0:
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, CSI_CLK_CTRL,
CSI2_RX0_CG, 0x1);
break;
case MIPI_RX1:
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, CSI_CLK_CTRL,
CSI2_RX1_CG, 0x1);
break;
case MIPI_RX2:
HW_OUTf(HWIO_MIPI_TOP_BASE_ADDR, MIPI_TOP, CSI_CLK_CTRL,
CSI2_RX2_CG, 0x1);
break;
default:
dev_err(dev, "%s invalid mipi host!\n", __func__);
break;
}
}
EXPORT_SYMBOL_GPL(mnh_mipi_stop_host);
void mnh_mipi_set_debug(int val)
{
mipi_debug = val;
}
EXPORT_SYMBOL(mnh_mipi_set_debug);
int mnh_mipi_get_top_interrupts(struct device *dev,
struct mipi_device_irq_st *int_status)
{
u32 status = 0;
switch (int_status->dev) {
case 0:
status = TOP_IN(TX0_BYPINT);
if (status & TOP_MASK(TX0_BYPINT, TX0_BYP_OF)) {
TOP_OUTf(TX0_BYPINT, TX0_BYP_OF, 1);
dev_info(dev, "TX0_BYPINT BYP_OF occurred\n");
int_status->fifo_overflow = 1;
}
break;
case 1:
status = TOP_IN(TX1_BYPINT);
if (status & TOP_MASK(TX1_BYPINT, TX1_BYP_OF)) {
TOP_OUTf(TX1_BYPINT, TX1_BYP_OF, 1);
dev_info(dev, "TX1_BYPINT BYP_OF occurred\n");
int_status->fifo_overflow = 1;
}
break;
default:
break;
}
return status;
}
int mnh_mipi_get_device_interrupts(struct device *dev,
struct mipi_device_irq_st *int_status)
{
uint32_t baddr = HWIO_MIPI_TX_BASE_ADDR(int_status->dev);
int_status->main = TX_IN(INT_ST_MAIN);
dev_info(dev, "MIPI device controller %d interrupt main: %x\n",
int_status->dev, int_status->main);
if (int_status->main & TX_MASK(INT_ST_MAIN, INT_ST_VPG)) {
int_status->vpg = TX_IN(INT_ST_VPG);
dev_info(dev, "CSI INT_ST_VPG: %x\n", int_status->vpg);
}
if (int_status->main & TX_MASK(INT_ST_MAIN, INT_ST_IDI)) {
int_status->idi = TX_IN(INT_ST_IDI);
dev_info(dev, "CSI INT_ST_IDI: %x\n", int_status->idi);
}
if (int_status->main & TX_MASK(INT_ST_MAIN, INT_ST_PHY)) {
int_status->phy = TX_IN(INT_ST_PHY);
dev_info(dev, "CSI INT_ST_PHY: %x\n", int_status->phy);
}
mnh_mipi_get_top_interrupts(dev, int_status);
return 0;
}
EXPORT_SYMBOL_GPL(mnh_mipi_get_device_interrupts);
int mnh_mipi_get_host_interrupts(struct device *dev,
struct mipi_host_irq_st *int_status)
{
uint32_t baddr = HWIO_MIPI_RX_BASE_ADDR(int_status->dev);
int_status->main = RX_IN(INT_ST_MAIN);
dev_info(dev, "MIPI host controller %d interrupt main: %x\n",
int_status->dev, int_status->main);
if (int_status->main & RX_MASK(INT_ST_MAIN, STATUS_INT_PHY_FATAL)) {
int_status->phy_fatal = RX_IN(INT_ST_PHY_FATAL);
dev_info(dev, "CSI INT PHY FATAL: %x\n",
int_status->phy_fatal);
}
if (int_status->main & RX_MASK(INT_ST_MAIN, STATUS_INT_PKT_FATAL)) {
int_status->pkt_fatal = RX_IN(INT_ST_PKT_FATAL);
dev_info(dev, "CSI INT PKT FATAL: %x\n",
int_status->pkt_fatal);
}
if (int_status->main & RX_MASK(INT_ST_MAIN, STATUS_INT_FRAME_FATAL)) {
int_status->frame_fatal = RX_IN(INT_ST_FRAME_FATAL);
dev_info(dev, "CSI INT FRAME FATAL: %x\n",
int_status->frame_fatal);
}
if (int_status->main & RX_MASK(INT_ST_MAIN, STATUS_INT_PHY)) {
int_status->phy = RX_IN(INT_ST_PHY);
dev_info(dev, "CSI INT PHY: %x\n", int_status->phy);
}
if (int_status->main & RX_MASK(INT_ST_MAIN, STATUS_INT_PKT)) {
int_status->pkt = RX_IN(INT_ST_PKT);
dev_info(dev, "CSI INT PKT: %x\n", int_status->pkt);
}
if (int_status->main & RX_MASK(INT_ST_MAIN, STATUS_INT_LINE)) {
int_status->line = RX_IN(INT_ST_LINE);
dev_info(dev, "CSI INT LINE: %x\n", int_status->line);
}
return 0;
}
EXPORT_SYMBOL_GPL(mnh_mipi_get_host_interrupts);