diff options
author | Xinyu Chen <xinyu.chen@freescale.com> | 2012-04-20 13:02:04 +0800 |
---|---|---|
committer | Xinyu Chen <xinyu.chen@freescale.com> | 2012-04-20 13:02:04 +0800 |
commit | b0cf397cecdecc102b82fc844b7a9e098d841187 (patch) | |
tree | 681e410089711d7e8d13cd819004e698fa6fec14 | |
parent | eb1da050c08bb961a1e45dd51f97821d3eeb2ae9 (diff) | |
parent | a7fbbad7276b41e8b338afafcaedf8c005de5c48 (diff) |
Merge remote branch 'fsl-linux-sdk/imx_3.0.15_12.04.01' into imx_3.0.15_android
Conflicts:
arch/arm/kernel/traps.c
arch/arm/mach-mx6/board-mx6q_sabresd.c
arch/arm/mach-mx6/cpu.c
arch/arm/mach-mx6/system.c
64 files changed, 4488 insertions, 420 deletions
diff --git a/arch/arm/mach-mx5/clock.c b/arch/arm/mach-mx5/clock.c index d34a8c944fe4..bf60ffa6ef8f 100755 --- a/arch/arm/mach-mx5/clock.c +++ b/arch/arm/mach-mx5/clock.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2008-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -148,11 +148,6 @@ static int _clk_enable(struct clk *clk) reg |= MXC_CCM_CCGRx_CG_MASK << clk->enable_shift; __raw_writel(reg, clk->enable_reg); - if (clk->flags & AHB_HIGH_SET_POINT) - lp_high_freq++; - else if (clk->flags & AHB_MED_SET_POINT) - lp_med_freq++; - return 0; } @@ -173,11 +168,6 @@ static void _clk_disable(struct clk *clk) reg = __raw_readl(clk->enable_reg); reg &= ~(MXC_CCM_CCGRx_CG_MASK << clk->enable_shift); __raw_writel(reg, clk->enable_reg); - - if (clk->flags & AHB_HIGH_SET_POINT) - lp_high_freq--; - else if (clk->flags & AHB_MED_SET_POINT) - lp_med_freq--; } static void _clk_disable_inwait(struct clk *clk) diff --git a/arch/arm/mach-mx6/Kconfig b/arch/arm/mach-mx6/Kconfig index 1df24213195b..1db924a0975e 100644 --- a/arch/arm/mach-mx6/Kconfig +++ b/arch/arm/mach-mx6/Kconfig @@ -60,6 +60,7 @@ config MACH_MX6Q_ARM2 select IMX_HAVE_PLATFORM_MXC_MLB select IMX_HAVE_PLATFORM_IMX_EPDC select IMX_HAVE_PLATFORM_IMX_PXP + select IMX_HAVE_PLATFORM_IMX_PCIE help Include support for i.MX 6Quad Armadillo2 platform. This includes specific configurations for the board and its peripherals. @@ -124,6 +125,7 @@ config MACH_MX6Q_SABRESD select IMX_HAVE_PLATFORM_MXC_HDMI select IMX_HAVE_PLATFORM_IMX_ASRC select IMX_HAVE_PLATFORM_FLEXCAN + select IMX_HAVE_PLATFORM_IMX_PCIE help Include support for i.MX 6Quad SABRE SD platform. This includes specific configurations for the board and its peripherals. diff --git a/arch/arm/mach-mx6/Makefile b/arch/arm/mach-mx6/Makefile index 21f776ab598c..e76f438c9194 100644 --- a/arch/arm/mach-mx6/Makefile +++ b/arch/arm/mach-mx6/Makefile @@ -3,7 +3,9 @@ # # Object file lists. -obj-y := cpu.o mm.o system.o devices.o dummy_gpio.o irq.o bus_freq.o usb_dr.o usb_h1.o usb_h2.o usb_h3.o pm.o cpu_op-mx6.o mx6_wfi.o mx6_fec.o mx6_anatop_regulator.o cpu_regulator-mx6.o +obj-y := cpu.o mm.o system.o devices.o dummy_gpio.o irq.o bus_freq.o usb_dr.o usb_h1.o usb_h2.o usb_h3.o \ +pm.o cpu_op-mx6.o mx6_wfi.o mx6_fec.o mx6_anatop_regulator.o cpu_regulator-mx6.o \ +mx6_mmdc.o mx6_ddr_freq.o obj-$(CONFIG_ARCH_MX6) += clock.o mx6q_suspend.o obj-$(CONFIG_MACH_MX6Q_ARM2) += board-mx6q_arm2.o diff --git a/arch/arm/mach-mx6/board-mx6q_arm2.c b/arch/arm/mach-mx6/board-mx6q_arm2.c index cf6a76076c4f..ca04938126f8 100644 --- a/arch/arm/mach-mx6/board-mx6q_arm2.c +++ b/arch/arm/mach-mx6/board-mx6q_arm2.c @@ -150,6 +150,9 @@ #define MX6_ARM2_IO_EXP_GPIO1(x) (MX6_ARM2_MAX7310_1_BASE_ADDR + (x)) #define MX6_ARM2_IO_EXP_GPIO2(x) (MX6_ARM2_MAX7310_2_BASE_ADDR + (x)) +#define MX6_ARM2_PCIE_PWR_EN MX6_ARM2_IO_EXP_GPIO1(2) +#define MX6_ARM2_PCIE_RESET MX6_ARM2_IO_EXP_GPIO2(2) + #define MX6_ARM2_CAN2_STBY MX6_ARM2_IO_EXP_GPIO2(1) @@ -169,6 +172,7 @@ extern char *gp_reg_id; extern int epdc_enabled; extern void mx6_cpu_regulator_init(void); static int max17135_regulator_init(struct max17135 *max17135); +extern volatile int num_cpu_idle_lock; enum sd_pad_mode { SD_PAD_MODE_LOW_SPEED, @@ -319,15 +323,24 @@ static int __init gpmi_nand_platform_init(void) return mxc_iomux_v3_setup_multiple_pads(nand_pads, nand_pads_cnt); } -static const struct gpmi_nand_platform_data -mx6_gpmi_nand_platform_data __initconst = { +static struct gpmi_nand_platform_data +mx6_gpmi_nand_platform_data = { .platform_init = gpmi_nand_platform_init, .min_prop_delay_in_ns = 5, .max_prop_delay_in_ns = 9, .max_chip_count = 1, .enable_bbt = 1, + .enable_ddr = 0, }; +static int __init board_support_onfi_nand(char *p) +{ + mx6_gpmi_nand_platform_data.enable_ddr = 1; + return 0; +} + +early_param("onfi_support", board_support_onfi_nand); + static const struct anatop_thermal_platform_data mx6_arm2_anatop_thermal_data __initconst = { .name = "anatop_thermal", @@ -1952,6 +1965,13 @@ static struct mxc_spdif_platform_data mxc_spdif_data = { .spdif_clk = NULL, /* spdif bus clk */ }; +static const struct imx_pcie_platform_data mx6_arm2_pcie_data __initconst = { + .pcie_pwr_en = MX6_ARM2_PCIE_PWR_EN, + .pcie_rst = MX6_ARM2_PCIE_RESET, + .pcie_wake_up = -EINVAL, + .pcie_dis = -EINVAL, +}; + static int __init early_disable_mipi_dsi(char *p) { /*enable on board HDMI*/ @@ -2012,6 +2032,7 @@ static void __init mx6_arm2_init(void) spdif_pads_cnt = ARRAY_SIZE(mx6dl_arm2_spdif_pads); flexcan_pads_cnt = ARRAY_SIZE(mx6dl_arm2_can_pads); i2c3_pads_cnt = ARRAY_SIZE(mx6dl_arm2_i2c3_pads); + num_cpu_idle_lock = 0xffff0000; } BUG_ON(!common_pads); @@ -2062,6 +2083,8 @@ static void __init mx6_arm2_init(void) gp_reg_id = arm2_dvfscore_data.reg_id; mx6_arm2_init_uart(); + + imx6q_add_mipi_csi2(&mipi_csi2_pdata); imx6q_add_mxc_hdmi_core(&hdmi_core_data); @@ -2191,6 +2214,8 @@ static void __init mx6_arm2_init(void) mxc_register_device(&max17135_sensor_device, NULL); imx6dl_add_imx_epdc(&epdc_data); } + imx6q_add_pcie(&mx6_arm2_pcie_data); + imx6q_add_busfreq(); } extern void __iomem *twd_base; diff --git a/arch/arm/mach-mx6/board-mx6q_sabreauto.c b/arch/arm/mach-mx6/board-mx6q_sabreauto.c index bc32f5674cbf..273a55ab27e6 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabreauto.c +++ b/arch/arm/mach-mx6/board-mx6q_sabreauto.c @@ -139,6 +139,8 @@ static struct clk *sata_clk; static int mipi_sensor; static int can0_enable; static int uart3_en; +static int tuner_en; +extern volatile int num_cpu_idle_lock; static int __init uart3_enable(char *p) { @@ -147,6 +149,13 @@ static int __init uart3_enable(char *p) } early_param("uart3", uart3_enable); +static int __init tuner_enable(char *p) +{ + tuner_en = 1; + return 0; +} +early_param("tuner", tuner_enable); + enum sd_pad_mode { SD_PAD_MODE_LOW_SPEED, SD_PAD_MODE_MED_SPEED, @@ -660,8 +669,29 @@ static struct i2c_board_info mxc_i2c1_board_info[] __initdata = { I2C_BOARD_INFO("cs42888", 0x48), .platform_data = (void *)&cs42888_data, }, + { + I2C_BOARD_INFO("si4763_i2c", 0x63), + }, + +}; +struct platform_device mxc_si4763_audio_device = { + .name = "imx-tuner-si4763", + .id = 0, }; +struct platform_device si4763_codec_device = { + .name = "si4763", + .id = 0, +}; + +static struct imx_ssi_platform_data mx6_sabreauto_ssi1_pdata = { + .flags = IMX_SSI_DMA | IMX_SSI_SYN, +}; +static struct mxc_audio_platform_data si4763_audio_data = { + .ssi_num = 1, + .src_port = 2, + .ext_port = 5, +}; static void imx6q_sabreauto_usbotg_vbus(bool on) { if (on) @@ -1272,23 +1302,27 @@ static void __init mx6_board_init(void) iomux_v3_cfg_t *can1_pads = NULL; iomux_v3_cfg_t *mipi_sensor_pads = NULL; iomux_v3_cfg_t *i2c3_pads = NULL; + iomux_v3_cfg_t *tuner_pads = NULL; int common_pads_cnt; int can0_pads_cnt; int can1_pads_cnt; int mipi_sensor_pads_cnt; int i2c3_pads_cnt; + int tuner_pads_cnt; if (cpu_is_mx6q()) { common_pads = mx6q_sabreauto_pads; can0_pads = mx6q_sabreauto_can0_pads; can1_pads = mx6q_sabreauto_can1_pads; mipi_sensor_pads = mx6q_sabreauto_mipi_sensor_pads; + tuner_pads = mx6q_tuner_pads; common_pads_cnt = ARRAY_SIZE(mx6q_sabreauto_pads); can0_pads_cnt = ARRAY_SIZE(mx6q_sabreauto_can0_pads); can1_pads_cnt = ARRAY_SIZE(mx6q_sabreauto_can1_pads); mipi_sensor_pads_cnt = ARRAY_SIZE(mx6q_sabreauto_mipi_sensor_pads); + tuner_pads_cnt = ARRAY_SIZE(mx6q_tuner_pads); if (board_is_mx6_reva()) { i2c3_pads = mx6q_i2c3_pads_rev_a; i2c3_pads_cnt = ARRAY_SIZE(mx6q_i2c3_pads_rev_a); @@ -1301,11 +1335,13 @@ static void __init mx6_board_init(void) can0_pads = mx6dl_sabreauto_can0_pads; can1_pads = mx6dl_sabreauto_can1_pads; mipi_sensor_pads = mx6dl_sabreauto_mipi_sensor_pads; + tuner_pads = mx6dl_tuner_pads; common_pads_cnt = ARRAY_SIZE(mx6dl_sabreauto_pads); can0_pads_cnt = ARRAY_SIZE(mx6dl_sabreauto_can0_pads); can1_pads_cnt = ARRAY_SIZE(mx6dl_sabreauto_can1_pads); mipi_sensor_pads_cnt = ARRAY_SIZE(mx6dl_sabreauto_mipi_sensor_pads); + tuner_pads_cnt = ARRAY_SIZE(mx6dl_tuner_pads); if (board_is_mx6_reva()) { i2c3_pads = mx6dl_i2c3_pads_rev_a; i2c3_pads_cnt = ARRAY_SIZE(mx6dl_i2c3_pads_rev_a); @@ -1313,6 +1349,7 @@ static void __init mx6_board_init(void) i2c3_pads = mx6dl_i2c3_pads_rev_b; i2c3_pads_cnt = ARRAY_SIZE(mx6dl_i2c3_pads_rev_b); } + num_cpu_idle_lock = 0xffff0000; } BUG_ON(!common_pads); @@ -1325,9 +1362,14 @@ static void __init mx6_board_init(void) mxc_iomux_v3_setup_multiple_pads(can0_pads, can0_pads_cnt); } - BUG_ON(!can1_pads); - mxc_iomux_v3_setup_multiple_pads(can1_pads, can1_pads_cnt); + BUG_ON(!can1_pads); + mxc_iomux_v3_setup_multiple_pads(can1_pads, can1_pads_cnt); + if (tuner_en) { + BUG_ON(!tuner_pads); + mxc_iomux_v3_setup_multiple_pads(tuner_pads, + tuner_pads_cnt); + } /* assert i2c-rst */ gpio_request(SABREAUTO_I2C_EXP_RST, "i2c-rst"); @@ -1483,6 +1525,13 @@ static void __init mx6_board_init(void) imx6q_add_hdmi_soc(); imx6q_add_hdmi_soc_dai(); imx6q_add_mlb150(&mx6_sabreauto_mlb150_data); + + /* Tuner audio interface */ + imx6q_add_imx_ssi(1, &mx6_sabreauto_ssi1_pdata); + mxc_register_device(&si4763_codec_device, NULL); + mxc_register_device(&mxc_si4763_audio_device, &si4763_audio_data); + + imx6q_add_busfreq(); } extern void __iomem *twd_base; diff --git a/arch/arm/mach-mx6/board-mx6q_sabreauto.h b/arch/arm/mach-mx6/board-mx6q_sabreauto.h index d2a097ec5e17..4c74a6aba961 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabreauto.h +++ b/arch/arm/mach-mx6/board-mx6q_sabreauto.h @@ -138,6 +138,7 @@ static iomux_v3_cfg_t mx6q_sabreauto_pads[] = { MX6Q_PAD_DISP0_DAT18__IPU1_DISP0_DAT_18, MX6Q_PAD_DISP0_DAT19__IPU1_DISP0_DAT_19, MX6Q_PAD_DISP0_DAT21__IPU1_DISP0_DAT_21, + /* LITE_SENS_INT_B */ MX6Q_PAD_DISP0_DAT23__GPIO_5_17, /*PMIC INT*/ @@ -347,3 +348,10 @@ static iomux_v3_cfg_t mx6q_i2c3_pads_rev_b[] __initdata = { MX6Q_PAD_EIM_A24__GPIO_5_4, MX6Q_PAD_GPIO_16__ENET_ANATOP_ETHERNET_REF_OUT, }; +static iomux_v3_cfg_t mx6q_tuner_pads[] __initdata = { + MX6Q_PAD_DISP0_DAT16__AUDMUX_AUD5_TXC, + MX6Q_PAD_DISP0_DAT18__AUDMUX_AUD5_TXFS, + MX6Q_PAD_DISP0_DAT19__AUDMUX_AUD5_RXD, + +}; + diff --git a/arch/arm/mach-mx6/board-mx6q_sabrelite.c b/arch/arm/mach-mx6/board-mx6q_sabrelite.c index c93da957070e..e00994f6fad8 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabrelite.c +++ b/arch/arm/mach-mx6/board-mx6q_sabrelite.c @@ -1210,6 +1210,7 @@ static void __init mx6_sabrelite_board_init(void) rate = clk_round_rate(clko2, 24000000); clk_set_rate(clko2, rate); clk_enable(clko2); + imx6q_add_busfreq(); } extern void __iomem *twd_base; diff --git a/arch/arm/mach-mx6/board-mx6q_sabresd.c b/arch/arm/mach-mx6/board-mx6q_sabresd.c index 3f81e1526aa5..977ec971af6a 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabresd.c +++ b/arch/arm/mach-mx6/board-mx6q_sabresd.c @@ -68,6 +68,7 @@ #include <mach/ipu-v3.h> #include <mach/mxc_hdmi.h> #include <mach/mxc_asrc.h> +#include <mach/mipi_dsi.h> #include <asm/irq.h> #include <asm/setup.h> @@ -145,7 +146,6 @@ #define SABRESD_DI1_D0_CS IMX_GPIO_NR(6, 31) #define SABRESD_HEADPHONE_DET IMX_GPIO_NR(7, 8) -#define SABRESD_USB_HUB_RESET IMX_GPIO_NR(7, 12) #define SABRESD_PCIE_RST_B_REVB IMX_GPIO_NR(7, 12) #define SABRESD_PMIC_INT_B IMX_GPIO_NR(7, 13) #define SABRESD_PFUZE_INT IMX_GPIO_NR(7, 13) @@ -197,9 +197,12 @@ static struct clk *sata_clk; static int mma8451_position = 1; static int mag3110_position = 2; +static int disable_ldb; extern char *gp_reg_id; extern int epdc_enabled; +extern volatile int num_cpu_idle_lock; + static int max17135_regulator_init(struct max17135 *max17135); static const struct esdhc_platform_data mx6q_sabresd_sd2_data __initconst = { @@ -1165,6 +1168,28 @@ static struct imx_asrc_platform_data imx_asrc_data = { .clk_map_ver = 2, }; +static void mx6_reset_mipi_dsi(void) +{ + gpio_set_value(SABRESD_DISP_PWR_EN, 1); + gpio_set_value(SABRESD_DISP_RST_B, 1); + udelay(10); + gpio_set_value(SABRESD_DISP_RST_B, 0); + udelay(50); + gpio_set_value(SABRESD_DISP_RST_B, 1); + + /* + * it needs to delay 120ms minimum for reset complete + */ + msleep(120); +} + +static struct mipi_dsi_platform_data mipi_dsi_pdata = { + .ipu_id = 0, + .disp_id = 1, + .lcd_panel = "TRULY-WVGA", + .reset = mx6_reset_mipi_dsi, +}; + static struct ipuv3_fb_platform_data sabresd_fb_data[] = { { /*fb0*/ .disp_dev = "ldb", @@ -1481,6 +1506,21 @@ static void mx6_snvs_poweroff(void) writel(value | 0x60, mx6_snvs_base + SNVS_LPCR); } +static const struct imx_pcie_platform_data mx6_sabresd_pcie_data __initconst = { + .pcie_pwr_en = SABRESD_PCIE_PWR_EN, + .pcie_rst = SABRESD_PCIE_RST_B_REVB, + .pcie_wake_up = SABRESD_PCIE_WAKE_B, + .pcie_dis = SABRESD_PCIE_DIS_B, +}; + +static int __init early_disable_ldb(char *p) +{ + /*mipi dsi need pll3_pfd_540M as 540MHz, ldb will change to 454Mhz*/ + disable_ldb = 1; + return 0; +} + +early_param("disable_ldb", early_disable_ldb); /*! * Board specific initialization. */ @@ -1495,9 +1535,11 @@ static void __init mx6_sabresd_board_init(void) if (cpu_is_mx6q()) mxc_iomux_v3_setup_multiple_pads(mx6q_sabresd_pads, ARRAY_SIZE(mx6q_sabresd_pads)); - else if (cpu_is_mx6dl()) + else if (cpu_is_mx6dl()) { mxc_iomux_v3_setup_multiple_pads(mx6dl_sabresd_pads, ARRAY_SIZE(mx6dl_sabresd_pads)); + num_cpu_idle_lock = 0xffff0000; + } #ifdef CONFIG_FEC_1588 /* Set GPIO_16 input for IEEE-1588 ts_clk and RMII reference clock @@ -1529,7 +1571,12 @@ static void __init mx6_sabresd_board_init(void) */ if (cpu_is_mx6dl()) { ldb_data.ipu_id = 0; - ldb_data.disp_id = 1; + ldb_data.disp_id = 0; + ldb_data.sec_ipu_id = 0; + ldb_data.sec_disp_id = 1; + hdmi_core_data.disp_id = 1; + mipi_dsi_pdata.ipu_id = 0; + mipi_dsi_pdata.disp_id = 1; } imx6q_add_mxc_hdmi_core(&hdmi_core_data); @@ -1540,8 +1587,10 @@ static void __init mx6_sabresd_board_init(void) imx6q_add_ipuv3fb(i, &sabresd_fb_data[i]); imx6q_add_vdoa(); + imx6q_add_mipi_dsi(&mipi_dsi_pdata); imx6q_add_lcdif(&lcdif_data); - imx6q_add_ldb(&ldb_data); + if (!disable_ldb) + imx6q_add_ldb(&ldb_data); imx6q_add_v4l2_output(0); imx6q_add_v4l2_capture(0); imx6q_add_mipi_csi2(&mipi_csi2_pdata); @@ -1599,9 +1648,6 @@ static void __init mx6_sabresd_board_init(void) imx_asrc_data.asrc_audio_clk = clk_get(NULL, "asrc_serial_clk"); imx6q_add_asrc(&imx_asrc_data); - /* release USB Hub reset */ - gpio_set_value(SABRESD_USB_HUB_RESET, 1); - imx6q_add_mxc_pwm(0); imx6q_add_mxc_pwm(1); imx6q_add_mxc_pwm(2); @@ -1676,7 +1722,9 @@ static void __init mx6_sabresd_board_init(void) /* Register charger chips */ platform_device_register(&sabresd_max8903_charger_1); pm_power_off = mx6_snvs_poweroff; + imx6q_add_busfreq(); + imx6q_add_pcie(&mx6_sabresd_pcie_data); } extern void __iomem *twd_base; diff --git a/arch/arm/mach-mx6/board-mx6q_sabresd.h b/arch/arm/mach-mx6/board-mx6q_sabresd.h index f4c04d3111b5..8ccb3423424f 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabresd.h +++ b/arch/arm/mach-mx6/board-mx6q_sabresd.h @@ -38,9 +38,10 @@ static iomux_v3_cfg_t mx6q_sabresd_pads[] = { MX6Q_PAD_GPIO_3__CCM_CLKO2, /* J5 - Camera MCLK */ /* ECSPI1 */ - MX6Q_PAD_EIM_D17__ECSPI1_MISO, - MX6Q_PAD_EIM_D18__ECSPI1_MOSI, - + MX6Q_PAD_KEY_COL0__ECSPI1_SCLK, + MX6Q_PAD_KEY_ROW0__ECSPI1_MOSI, + MX6Q_PAD_KEY_COL1__ECSPI1_MISO, + MX6Q_PAD_KEY_ROW1__GPIO_4_9, /* ENET */ MX6Q_PAD_ENET_MDIO__ENET_MDIO, MX6Q_PAD_ENET_MDC__ENET_MDC, @@ -122,9 +123,6 @@ static iomux_v3_cfg_t mx6q_sabresd_pads[] = { MX6Q_PAD_EIM_A23__GPIO_6_6, /* J12 - Boot Mode Select */ MX6Q_PAD_NANDF_RB0__GPIO_6_10, /* AUX_5V Enable */ - /* GPIO7 */ - MX6Q_PAD_GPIO_17__GPIO_7_12, /* USB Hub Reset */ - /* I2C1, WM8958 */ MX6Q_PAD_CSI0_DAT8__I2C1_SDA, MX6Q_PAD_CSI0_DAT9__I2C1_SCL, @@ -250,7 +248,16 @@ static iomux_v3_cfg_t mx6q_sabresd_pads[] = { /*GPS AUX_3V15_EN*/ MX6Q_PAD_NANDF_WP_B__GPIO_6_9, + /* PCIE */ MX6Q_PAD_EIM_D19__GPIO_3_19, /* PCIE_PWR_EN */ + + MX6Q_PAD_GPIO_17__GPIO_7_12, /* PCIE_RST */ + MX6Q_PAD_KEY_COL4__GPIO_4_14, /* PCIE_DIS */ + + /* DISP_RST_B */ + MX6Q_PAD_NANDF_CS0__GPIO_6_11, + /* DISP_PWR_EN */ + MX6Q_PAD_NANDF_CS1__GPIO_6_14, }; static iomux_v3_cfg_t mx6q_sabresd_csi0_sensor_pads[] = { diff --git a/arch/arm/mach-mx6/board-mx6solo_sabreauto.h b/arch/arm/mach-mx6/board-mx6solo_sabreauto.h index 3829c7ac38e6..dd113bab749b 100644 --- a/arch/arm/mach-mx6/board-mx6solo_sabreauto.h +++ b/arch/arm/mach-mx6/board-mx6solo_sabreauto.h @@ -350,3 +350,8 @@ static iomux_v3_cfg_t mx6dl_i2c3_pads_rev_b[] __initdata = { MX6DL_PAD_EIM_A24__GPIO_5_4, MX6DL_PAD_GPIO_16__ENET_ANATOP_ETHERNET_REF_OUT, }; +static iomux_v3_cfg_t mx6dl_tuner_pads[] __initdata = { + MX6DL_PAD_DISP0_DAT16__AUDMUX_AUD5_TXC, + MX6DL_PAD_DISP0_DAT18__AUDMUX_AUD5_TXFS, + MX6DL_PAD_DISP0_DAT19__AUDMUX_AUD5_RXD, +}; diff --git a/arch/arm/mach-mx6/bus_freq.c b/arch/arm/mach-mx6/bus_freq.c index 36308c14c8f2..e4244afe7ae7 100644 --- a/arch/arm/mach-mx6/bus_freq.c +++ b/arch/arm/mach-mx6/bus_freq.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved. * * 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 @@ -43,58 +43,36 @@ #include <asm/mach-types.h> #include <asm/cacheflush.h> #include <asm/tlb.h> +#include "crm_regs.h" -#define LP_LOW_VOLTAGE 1050000 -#define LP_NORMAL_VOLTAGE 1250000 -#define LP_APM_CLK 24000000 -#define NAND_LP_APM_CLK 12000000 -#define AXI_A_NORMAL_CLK 166250000 -#define AXI_A_CLK_NORMAL_DIV 4 -#define AXI_B_CLK_NORMAL_DIV 5 -#define AHB_CLK_NORMAL_DIV AXI_B_CLK_NORMAL_DIV -#define EMI_SLOW_CLK_NORMAL_DIV AXI_B_CLK_NORMAL_DIV -#define NFC_CLK_NORMAL_DIV 4 -#define SPIN_DELAY 1000000 /* in nanoseconds */ -#define DDR_TYPE_DDR3 0x0 -#define DDR_TYPE_DDR2 0x1 -DEFINE_SPINLOCK(ddr_freq_lock); - -unsigned long lp_normal_rate; -unsigned long lp_med_rate; -unsigned long ddr_normal_rate; -unsigned long ddr_med_rate; -unsigned long ddr_low_rate; +#define LPAPM_CLK 24000000 +#define DDR_MED_CLK 400000000 +#define DDR3_NORMAL_CLK 528000000 -struct regulator *pll_regulator; +DEFINE_SPINLOCK(ddr_freq_lock); -struct regulator *lp_regulator; int low_bus_freq_mode; int high_bus_freq_mode; int med_bus_freq_mode; int bus_freq_scaling_initialized; -char *lp_reg_id; - static struct device *busfreq_dev; static int busfreq_suspended; /* True if bus_frequency is scaled not using DVFS-PER */ int bus_freq_scaling_is_active; -int cpu_op_nr; int lp_high_freq; int lp_med_freq; - -struct workqueue_struct *voltage_wq; -struct completion voltage_change_cmpl; +unsigned int ddr_low_rate; +unsigned int ddr_med_rate; +unsigned int ddr_normal_rate; int low_freq_bus_used(void); void set_ddr_freq(int ddr_freq); extern struct cpu_op *(*get_cpu_op)(int *op); -extern void __iomem *ccm_base; -extern void __iomem *databahn_base; extern int update_ddr_freq(int ddr_rate); @@ -103,29 +81,140 @@ struct mutex bus_freq_mutex; struct timeval start_time; struct timeval end_time; -int set_low_bus_freq(void) +static int cpu_op_nr; +static struct cpu_op *cpu_op_tbl; +static struct clk *pll2_400; +static struct clk *cpu_clk; +static unsigned int org_ldo; +static struct clk *pll3; + +static struct delayed_work low_bus_freq_handler; + +static void reduce_bus_freq_handler(struct work_struct *work) { - return 0; + unsigned long reg; + + if (low_bus_freq_mode || !low_freq_bus_used()) + return; + + while (!mutex_trylock(&bus_freq_mutex)) + msleep(1); + + /* PLL3 is used in the DDR freq change process, enable it. */ + + if (low_bus_freq_mode || !low_freq_bus_used()) { + mutex_unlock(&bus_freq_mutex); + return; + } + clk_enable(pll3); + + + update_ddr_freq(24000000); + + if (med_bus_freq_mode) + clk_disable(pll2_400); + + low_bus_freq_mode = 1; + high_bus_freq_mode = 0; + med_bus_freq_mode = 0; + + /* Power gate the PU LDO. */ + org_ldo = reg = __raw_readl(ANADIG_REG_CORE); + reg &= ~(ANADIG_REG_TARGET_MASK << ANADIG_REG1_PU_TARGET_OFFSET); + __raw_writel(reg, ANADIG_REG_CORE); + + mutex_unlock(&bus_freq_mutex); + clk_disable(pll3); + + } -int set_high_bus_freq(int high_bus_freq) +/* Set the DDR, AHB to 24MHz. + * This mode will be activated only when none of the modules that + * need a higher DDR or AHB frequency are active. + */ +int set_low_bus_freq(void) { + if (busfreq_suspended) + return 0; + + if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active) + return 0; + + /* Don't lower the frequency immediately. Instead scheduled a delayed work + * and drop the freq if the conditions still remain the same. + */ + schedule_delayed_work(&low_bus_freq_handler, usecs_to_jiffies(3000000)); return 0; } -void exit_lpapm_mode_mx6q(int high_bus_freq) +/* Set the DDR to either 528MHz or 400MHz for MX6q + * or 400MHz for MX6DL. + */ +int set_high_bus_freq(int high_bus_freq) { + if (busfreq_suspended) + return 0; -} + if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active) + return 0; + if (high_bus_freq_mode && high_bus_freq) + return 0; -void set_ddr_freq(int ddr_rate) -{ + if (med_bus_freq_mode && !high_bus_freq) + return 0; + while (!mutex_trylock(&bus_freq_mutex)) + msleep(1); + + if ((high_bus_freq_mode && (high_bus_freq || lp_high_freq)) || + (med_bus_freq_mode && !high_bus_freq && lp_med_freq && !lp_high_freq)) { + mutex_unlock(&bus_freq_mutex); + return 0; + } + clk_enable(pll3); + + /* Enable the PU LDO */ + if (low_bus_freq_mode) + __raw_writel(org_ldo, ANADIG_REG_CORE); + + if (high_bus_freq) { + update_ddr_freq(ddr_normal_rate); + if (med_bus_freq_mode) + clk_disable(pll2_400); + low_bus_freq_mode = 0; + high_bus_freq_mode = 1; + med_bus_freq_mode = 0; + } else { + clk_enable(pll2_400); + update_ddr_freq(ddr_med_rate); + low_bus_freq_mode = 0; + high_bus_freq_mode = 0; + med_bus_freq_mode = 1; + } + + mutex_unlock(&bus_freq_mutex); + clk_disable(pll3); + + return 0; } + int low_freq_bus_used(void) { + if (!bus_freq_scaling_initialized) + return 0; + + /* We only go the lowest setpoint if ARM is also + * at the lowest setpoint. + */ + if ((clk_get_rate(cpu_clk) > + cpu_op_tbl[cpu_op_nr - 1].cpu_rate) + || (cpu_op_nr == 1)) { + return 0; + } + if ((lp_high_freq == 0) && (lp_med_freq == 0)) return 1; @@ -150,6 +239,14 @@ static ssize_t bus_freq_scaling_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { + if (strncmp(buf, "1", 1) == 0) { + bus_freq_scaling_is_active = 1; + set_high_bus_freq(0); + } else if (strncmp(buf, "0", 1) == 0) { + if (bus_freq_scaling_is_active) + set_high_bus_freq(1); + bus_freq_scaling_is_active = 0; + } return size; } @@ -180,6 +277,54 @@ static DEVICE_ATTR(enable, 0644, bus_freq_scaling_enable_show, */ static int __devinit busfreq_probe(struct platform_device *pdev) { + u32 err; + + busfreq_dev = &pdev->dev; + + pll2_400 = clk_get(NULL, "pll2_pfd_400M"); + if (IS_ERR(pll2_400)) { + printk(KERN_DEBUG "%s: failed to get axi_clk\n", + __func__); + return PTR_ERR(pll2_400); + } + + cpu_clk = clk_get(NULL, "cpu_clk"); + if (IS_ERR(cpu_clk)) { + printk(KERN_DEBUG "%s: failed to get cpu_clk\n", + __func__); + return PTR_ERR(cpu_clk); + } + + pll3 = clk_get(NULL, "pll3_main_clk"); + + err = sysfs_create_file(&busfreq_dev->kobj, &dev_attr_enable.attr); + if (err) { + printk(KERN_ERR + "Unable to register sysdev entry for BUSFREQ"); + return err; + } + + cpu_op_tbl = get_cpu_op(&cpu_op_nr); + low_bus_freq_mode = 0; + high_bus_freq_mode = 1; + med_bus_freq_mode = 0; + bus_freq_scaling_is_active = 0; + bus_freq_scaling_initialized = 1; + + if (cpu_is_mx6q()) { + ddr_low_rate = LPAPM_CLK; + ddr_med_rate = DDR_MED_CLK; + ddr_normal_rate = DDR3_NORMAL_CLK; + } + if (cpu_is_mx6dl()) { + ddr_low_rate = LPAPM_CLK; + ddr_normal_rate = ddr_med_rate = DDR_MED_CLK; + } + + INIT_DELAYED_WORK(&low_bus_freq_handler, reduce_bus_freq_handler); + + mutex_init(&bus_freq_mutex); + return 0; } @@ -218,7 +363,7 @@ static void __exit busfreq_cleanup(void) bus_freq_scaling_initialized = 0; } -module_init(busfreq_init); +late_initcall(busfreq_init); module_exit(busfreq_cleanup); MODULE_AUTHOR("Freescale Semiconductor, Inc."); diff --git a/arch/arm/mach-mx6/clock.c b/arch/arm/mach-mx6/clock.c index 889447b77076..1f7e4312158d 100644 --- a/arch/arm/mach-mx6/clock.c +++ b/arch/arm/mach-mx6/clock.c @@ -29,6 +29,8 @@ #include <mach/common.h> #include <mach/clock.h> #include <mach/mxc_dvfs.h> +#include <mach/mxc_hdmi.h> +#include <mach/ahci_sata.h> #include "crm_regs.h" #include "cpu_op-mx6.h" #include "regs-anadig.h" @@ -162,11 +164,6 @@ static int _clk_enable(struct clk *clk) reg |= MXC_CCM_CCGRx_CG_MASK << clk->enable_shift; __raw_writel(reg, clk->enable_reg); - if (clk->flags & AHB_HIGH_SET_POINT) - lp_high_freq++; - else if (clk->flags & AHB_MED_SET_POINT) - lp_med_freq++; - return 0; } @@ -176,11 +173,6 @@ static void _clk_disable(struct clk *clk) reg = __raw_readl(clk->enable_reg); reg &= ~(MXC_CCM_CCGRx_CG_MASK << clk->enable_shift); __raw_writel(reg, clk->enable_reg); - - if (clk->flags & AHB_HIGH_SET_POINT) - lp_high_freq--; - else if (clk->flags & AHB_MED_SET_POINT) - lp_med_freq--; } static void _clk_disable_inwait(struct clk *clk) @@ -2164,7 +2156,6 @@ static struct clk ipu2_clk = { static struct clk usdhc_dep_clk = { .parent = &mmdc_ch0_axi_clk[0], .secondary = &mx6per1_clk, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static unsigned long _clk_usdhc_round_rate(struct clk *clk, @@ -2242,7 +2233,6 @@ static struct clk usdhc1_clk = { .round_rate = _clk_usdhc_round_rate, .set_rate = _clk_usdhc1_set_rate, .get_rate = _clk_usdhc1_get_rate, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static int _clk_usdhc2_set_parent(struct clk *clk, struct clk *parent) @@ -2300,7 +2290,6 @@ static struct clk usdhc2_clk = { .round_rate = _clk_usdhc_round_rate, .set_rate = _clk_usdhc2_set_rate, .get_rate = _clk_usdhc2_get_rate, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static int _clk_usdhc3_set_parent(struct clk *clk, struct clk *parent) @@ -2359,7 +2348,6 @@ static struct clk usdhc3_clk = { .round_rate = _clk_usdhc_round_rate, .set_rate = _clk_usdhc3_set_rate, .get_rate = _clk_usdhc3_get_rate, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static int _clk_usdhc4_set_parent(struct clk *clk, struct clk *parent) @@ -2418,7 +2406,6 @@ static struct clk usdhc4_clk = { .round_rate = _clk_usdhc_round_rate, .set_rate = _clk_usdhc4_set_rate, .get_rate = _clk_usdhc4_get_rate, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static unsigned long _clk_ssi_round_rate(struct clk *clk, @@ -2703,12 +2690,16 @@ static int _clk_ldb_di0_set_rate(struct clk *clk, unsigned long rate) static int _clk_ldb_di0_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; + int rev = mx6q_revision(); reg = __raw_readl(MXC_CCM_CS2CDR) & ~MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_MASK; mux = _get_mux6(parent, &pll5_video_main_clk, - &pll2_pfd_352M, &pll2_pfd_400M, &pll3_pfd_540M, + &pll2_pfd_352M, &pll2_pfd_400M, + (rev == IMX_CHIP_REVISION_1_0) ? + &pll3_pfd_540M : /* MX6Q TO1.0 */ + &mmdc_ch1_axi_clk[0], /* MX6Q TO1.1 and MX6DL */ &pll3_usb_otg_main_clk, NULL); reg |= (mux << MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET); @@ -2720,7 +2711,7 @@ static int _clk_ldb_di0_set_parent(struct clk *clk, struct clk *parent) static struct clk ldb_di0_clk = { __INIT_CLK_DEBUG(ldb_di0_clk) .id = 0, - .parent = &pll3_pfd_540M, + .parent = &pll2_pfd_352M, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGRx_CG6_OFFSET, .enable = _clk_enable, @@ -2770,12 +2761,16 @@ static int _clk_ldb_di1_set_rate(struct clk *clk, unsigned long rate) static int _clk_ldb_di1_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; + int rev = mx6q_revision(); reg = __raw_readl(MXC_CCM_CS2CDR) & ~MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_MASK; mux = _get_mux6(parent, &pll5_video_main_clk, - &pll2_pfd_352M, &pll2_pfd_400M, &pll3_pfd_540M, + &pll2_pfd_352M, &pll2_pfd_400M, + (rev == IMX_CHIP_REVISION_1_0) ? + &pll3_pfd_540M : /* MX6Q TO1.0 */ + &mmdc_ch1_axi_clk[0], /* MX6Q TO1.1 and MX6DL */ &pll3_usb_otg_main_clk, NULL); reg |= (mux << MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET); @@ -2787,7 +2782,7 @@ static int _clk_ldb_di1_set_parent(struct clk *clk, struct clk *parent) static struct clk ldb_di1_clk = { __INIT_CLK_DEBUG(ldb_di1_clk) .id = 0, - .parent = &pll3_pfd_540M, + .parent = &pll2_pfd_352M, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGRx_CG7_OFFSET, .enable = _clk_enable, @@ -5022,7 +5017,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "caam_clk", caam_clk[0]), _REGISTER_CLOCK(NULL, "asrc_clk", asrc_clk[0]), _REGISTER_CLOCK(NULL, "asrc_serial_clk", asrc_clk[1]), - _REGISTER_CLOCK("mxs-dma-apbh", NULL, apbh_dma_clk), + _REGISTER_CLOCK(NULL, "mxs-dma-apbh", apbh_dma_clk), _REGISTER_CLOCK(NULL, "openvg_axi_clk", openvg_axi_clk), _REGISTER_CLOCK(NULL, "gpu3d_clk", gpu3d_core_clk[0]), _REGISTER_CLOCK(NULL, "gpu2d_clk", gpu2d_core_clk[0]), @@ -5084,7 +5079,7 @@ int __init mx6_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1, unsigned long ckih2) { __iomem void *base; - int i; + int i, reg; external_low_reference = ckil; external_high_reference = ckih1; @@ -5178,6 +5173,7 @@ int __init mx6_clocks_init(unsigned long ckil, unsigned long osc, clk_set_rate(&asrc_clk[1], 7500000); /* set the GPMI clock to default frequency : 20MHz */ + clk_set_parent(&enfc_clk, &pll2_pfd_400M); clk_set_rate(&enfc_clk, enfc_clk.round_rate(&enfc_clk, 20000000)); mx6_cpu_op_init(); @@ -5213,8 +5209,8 @@ int __init mx6_clocks_init(unsigned long ckil, unsigned long osc, __raw_writel(0, MXC_CCM_CCGR6); - /* Lower the ipg_perclk frequency to 8.25MHz. */ - clk_set_rate(&ipg_perclk, 8250000); + /* Lower the ipg_perclk frequency to 6MHz. */ + clk_set_rate(&ipg_perclk, 6000000); /* Set pll2_pfd_352M frequency to 528M for gpu2d core clock */ clk_set_rate(&pll2_pfd_352M, 528000000); @@ -5248,6 +5244,35 @@ int __init mx6_clocks_init(unsigned long ckil, unsigned long osc, lp_high_freq = 0; lp_med_freq = 0; + /* Turn OFF all unnecessary PHYs. */ + if (cpu_is_mx6q()) { + /* Turn off SATA PHY. */ + base = ioremap(MX6Q_SATA_BASE_ADDR, SZ_8K); + reg = __raw_readl(base + PORT_PHY_CTL); + __raw_writel(reg | PORT_PHY_CTL_PDDQ_LOC, base + PORT_PHY_CTL); + } + + /* Turn off HDMI PHY. */ + base = ioremap(MX6Q_HDMI_ARB_BASE_ADDR, SZ_128K); + reg = __raw_readb(base + HDMI_PHY_CONF0); + __raw_writeb(reg | HDMI_PHY_CONF0_GEN2_PDDQ_MASK, base + HDMI_PHY_CONF0); + + reg = __raw_readb(base + HDMI_MC_PHYRSTZ); + __raw_writeb(reg | HDMI_MC_PHYRSTZ_DEASSERT, base + HDMI_MC_PHYRSTZ); + + iounmap(base); + + base = ioremap(MX6Q_IOMUXC_BASE_ADDR, SZ_4K); + /* Close PLL inside SATA PHY. */ + reg = __raw_readl(base + 0x34); + __raw_writel(reg | (1 << 1), base + 0x34); + + /* Close PCIE PHY. */ + reg = __raw_readl(base + 0x04); + reg |= (1 << 18); + __raw_writel(reg, base + 0x04); + iounmap(base); + return 0; } diff --git a/arch/arm/mach-mx6/cpu.c b/arch/arm/mach-mx6/cpu.c index ddb4e0cf9976..6f3765e23523 100644 --- a/arch/arm/mach-mx6/cpu.c +++ b/arch/arm/mach-mx6/cpu.c @@ -36,7 +36,7 @@ extern unsigned int num_cpu_idle_lock; void *mx6_wait_in_iram_base; void (*mx6_wait_in_iram)(void); extern void mx6_wait(void); - +extern int init_mmdc_settings(void); struct cpu_op *(*get_cpu_op)(int *op); bool enable_wait_mode; @@ -92,8 +92,6 @@ static int __init post_cpu_init(void) { unsigned int reg; void __iomem *base; - unsigned long iram_paddr, cpaddr; - iram_init(MX6Q_IRAM_BASE_ADDR, MX6Q_IRAM_SIZE); @@ -134,6 +132,7 @@ static int __init post_cpu_init(void) num_cpu_idle_lock = 0x0; + init_mmdc_settings(); return 0; } postcore_initcall(post_cpu_init); diff --git a/arch/arm/mach-mx6/devices-imx6q.h b/arch/arm/mach-mx6/devices-imx6q.h index 02ac1f409799..2c3e0c53ac06 100644 --- a/arch/arm/mach-mx6/devices-imx6q.h +++ b/arch/arm/mach-mx6/devices-imx6q.h @@ -210,3 +210,9 @@ extern const struct imx_epdc_data imx6dl_epdc_data __initconst; imx_add_imx_epdc(&imx6dl_epdc_data, pdata) extern const struct imx_vdoa_data imx6q_vdoa_data __initconst; #define imx6q_add_vdoa() imx_add_vdoa(&imx6q_vdoa_data) + +extern const struct imx_pcie_data imx6q_pcie_data __initconst; +#define imx6q_add_pcie(pdata) imx_add_pcie(&imx6q_pcie_data, pdata) + +#define imx6q_add_busfreq(pdata) imx_add_busfreq(pdata) + diff --git a/arch/arm/mach-mx6/mx6_ddr_freq.S b/arch/arm/mach-mx6/mx6_ddr_freq.S new file mode 100644 index 000000000000..766d867ee1c4 --- /dev/null +++ b/arch/arm/mach-mx6/mx6_ddr_freq.S @@ -0,0 +1,873 @@ +/* + * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/linkage.h> +#include <mach/hardware.h> + + .macro switch_to_528MHz + + /* DDR freq change to 528MHz */ + + /* check if periph_clk_sel is already set */ + ldr r0, [r6, #0x14] + and r0, r0, #0x2000000 + cmp r0, #0x2000000 + beq switch_pre_periph_clk_528 + + /* Step 1: Change periph_clk to be sourced from pll3_clk. */ + /* Ensure PLL3 is the source and set the divider to 1. */ + ldr r0, [r6, #0x18] + bic r0, r0, #0x3000 + str r0, [r6, #0x18] + + ldr r0, [r6, #0x14] + bic r0, r0, #0x38000000 + str r0, [r6, #0x14] + + /* Now switch periph_clk to pll3_main_clk. */ + ldr r0, [r6, #0x14] + orr r0, r0, #0x2000000 + str r0, [r6, #0x14] + +periph_clk_switch3: + ldr r0, [r6, #0x48] + cmp r0, #0 + bne periph_clk_switch3 + +switch_pre_periph_clk_528: + + /* Now switch pre_periph_clk to PLL2_528MHz. */ + ldr r0, [r6, #0x18] + bic r0, r0, #0xC0000 + str r0, [r6, #0x18] + + /* Set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4 (need to maintain GPT divider). */ + ldr r0, [r6, #0x14] + ldr r2, =0x3F1D00 + bic r0, r0, r2 + orr r0, r0, #0x10000 + orr r0, r0, #0xD00 + str r0, [r6, #0x14] + +wait_div_update1: + ldr r0, [r6, #0x48] + cmp r0, #0 + bne wait_div_update1 + + /* Now switch periph_clk back. */ + ldr r0, [r6, #0x14] + bic r0, r0, #0x2000000 + str r0, [r6, #0x14] + +periph_clk_switch4: + ldr r0, [r6, #0x48] + cmp r0, #0 + bne periph_clk_switch4 + + /* Change the GPT divider so that its at 6MHz. */ + ldr r0, [r6, #0x1C] + bic r0, r0, #0x3F + orr r0, r0, #0xB + str r0, [r6, #0x1C] + + .endm + + .macro switch_to_400MHz + + /* check if periph_clk_sel is already set */ + ldr r0, [r6, #0x14] + and r0, r0, #0x2000000 + cmp r0, #0x2000000 + beq switch_pre_periph_clk_400 + + /* Step 1: Change periph_clk to be sourced from pll3_clk. */ + /* Ensure PLL3 is the source and set the divider to 1. */ + ldr r0, [r6, #0x18] + bic r0, r0, #0x3000 + str r0, [r6, #0x18] + + ldr r0, [r6, #0x14] + bic r0, r0, #0x38000000 + str r0, [r6, #0x14] + + /* Now switch periph_clk to pll3_main_clk. */ + ldr r0, [r6, #0x14] + orr r0, r0, #0x2000000 + str r0, [r6, #0x14] + +periph_clk_switch5: + ldr r0, [r6, #0x48] + cmp r0, #0 + bne periph_clk_switch5 + +switch_pre_periph_clk_400: + + /* Now switch pre_periph_clk to PFD_400MHz. */ + ldr r0, [r6, #0x18] + bic r0, r0, #0xC0000 + orr r0, r0, #0x40000 + str r0, [r6, #0x18] + + /* Set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=3 (need to maintain GPT divider). */ + ldr r0, [r6, #0x14] + ldr r2, =0x3F1D00 + bic r0, r0, r2 + orr r0, r0, #0x10000 + orr r0, r0, #0x900 + str r0, [r6, #0x14] + +wait_div_update400: + ldr r0, [r6, #0x48] + cmp r0, #0 + bne wait_div_update400 + + /* Now switch periph_clk back. */ + ldr r0, [r6, #0x14] + bic r0, r0, #0x2000000 + str r0, [r6, #0x14] + +periph_clk_switch6: + ldr r0, [r6, #0x48] + cmp r0, #0 + bne periph_clk_switch6 + + /* Change the GPT divider so that its at 6MHz. */ + ldr r0, [r6, #0x1C] + bic r0, r0, #0x3F + orr r0, r0, #0xB + str r0, [r6, #0x1C] + + .endm + + .macro switch_to_50MHz + + /* Set DDR to 50MHz. */ + /* check if periph_clk_sel is already set */ + ldr r0, [r6, #0x14] + and r0, r0, #0x2000000 + cmp r0, #0x2000000 + beq switch_pre_periph_clk_50 + + /* Set the periph_clk to be sourced from PLL2_PFD_200M */ + /* Step 1: Change periph_clk to be sourced from pll3_clk. */ + /* Ensure PLL3 is the source and set the divider to 1. */ + ldr r0, [r6, #0x18] + bic r0, r0, #0x3000 + str r0, [r6, #0x18] + + ldr r0, [r6, #0x14] + bic r0, r0, #0x38000000 + str r0, [r6, #0x14] + + /* Now switch periph_clk to pll3_main_clk. */ + ldr r0, [r6, #0x14] + orr r0, r0, #0x2000000 + str r0, [r6, #0x14] + +periph_clk_switch_50: + ldr r0, [r6, #0x48] + cmp r0, #0 + bne periph_clk_switch_50 + +switch_pre_periph_clk_50: + + /* Now switch pre_periph_clk to PFD_200MHz. */ + ldr r0, [r6, #0x18] + orr r0, r0, #0xC0000 + str r0, [r6, #0x18] + + /* Set the MMDC_DIV=4, AXI_DIV = 4, AHB_DIV=6 (need to maintain GPT divider). */ + ldr r0, [r6, #0x14] + ldr r2, =0x3F1C00 + bic r0, r0, r2 + + orr r0, r0, #0x180000 + orr r0, r0, #0x10000 + + /* If changing AHB divider remember to change the IPGPER divider too below. */ + orr r0, r0, #0xC00 + str r0, [r6, #0x14] + +wait_div_update_50: + ldr r0, [r6, #0x48] + cmp r0, #0 + bne wait_div_update_50 + + /* Now switch periph_clk back. */ + ldr r0, [r6, #0x14] + bic r0, r0, #0x2000000 + str r0, [r6, #0x14] + +periph_clk_switch2: + ldr r0, [r6, #0x48] + cmp r0, #0 + bne periph_clk_switch2 + + /* Change the GPT divider so that its at 6MHz. */ + ldr r0, [r6, #0x1C] + bic r0, r0, #0x3F + orr r0, r0, #0x3 + str r0, [r6, #0x1C] + + .endm + + .macro switch_to_24MHz + /* Change the freq now */ + /* Try setting DDR to 24MHz. */ + /* Source it from the periph_clk2 */ + /* Ensure the periph_clk2 is sourced from 24MHz + and the divider is 1. */ + ldr r0, [r6, #0x18] + bic r0, r0, #0x3000 + orr r0, r0, #0x1000 + str r0, [r6, #0x18] + + ldr r0, [r6, #0x14] + bic r0, r0, #0x38000000 + str r0, [r6, #0x14] + + /* Now switch periph_clk to 24MHz. */ + ldr r0, [r6, #0x14] + orr r0, r0, #0x2000000 + str r0, [r6, #0x14] + +periph_clk_switch1: + ldr r0, [r6, #0x48] + cmp r0, #0 + bne periph_clk_switch1 + + /* Change all the dividers to 1. */ + ldr r0, [r6, #0x14] + ldr r2, =0x3F1C00 + bic r0, r0, r2 + str r0, [r6, #0x14] + + /* Wait for the divider to change. */ +wait_div_update: + ldr r0, [r6, #0x48] + cmp r0, #0 + bne wait_div_update + + /* Change the GPT divider so that its at 6MHz. */ + ldr r0, [r6, #0x1C] + bic r0, r0, #0x3F + orr r0, r0, #0x1 + str r0, [r6, #0x1C] + + .endm + +/* + * mx6_ddr_freq_change + * + * Idle the processor (eg, wait for interrupt). + * Make sure DDR is in self-refresh. + * IRQs are already disabled. + */ +ENTRY(mx6_ddr_freq_change) + + stmfd sp!, {r4,r5,r6, r7, r8, r9, r10, r11} @ Save registers + + ldr r6, =CCM_BASE_ADDR + add r6, r6, #PERIPBASE_VIRT + ldr r5, =MMDC_P0_BASE_ADDR + add r5, r5, #PERIPBASE_VIRT + ldr r7, =MX6Q_IOMUXC_BASE_ADDR + add r7, r7, #PERIPBASE_VIRT + + mov r4, r0 @save new freq requested + mov r8, r1 @save the ddr settings for the new rate + mov r9, r2 @save the mode DDR is currently in (DLL ON/OFF) + mov r11, r3 @save iomux offsets + +ddr_freq_change: + /* Make sure no TLB miss will occur when the DDR is in self refresh. */ + /* Invalidate TLB single entry to ensure that the address is not + * already in the TLB. + */ + + adr r10, ddr_freq_change @Address in this function. + + + mcr p15, 0, r10, c8, c7, 1 @//@ Make sure freq code address + @// @ is not already in TLB. + mcr p15, 0, r6, c8, c7, 1 @//@ Make sure CCM address + @//@ is not already in TLB. + mcr p15, 0, r5, c8, c7, 1 @//@ make sure MMDC address + @//@ is not already in TLB. + mcr p15, 0, r7, c8, c7, 1 @//@ make sure IOMUX address + @//@ is not already in TLB. + + mrc p15, 0, r0, c10, c0, 0 @//@ Read the TLB lockdown register + orr r0, r0, #1 @//@ Set the Preserve bit. + mcr p15, 0, r0, c10, c0, 0 @//@ Write to the lockdown register + + ldr r2, [r6] @ TLB will miss, + @CCM address will be loaded + ldr r2, [r5] @ TLB will miss, + @MMDC address will be loaded + ldr r2, [r7] @ TLB will miss, + @IOMUX will be loaded + + ldr r2, [r8] @ Get the DDR settings. + + ldr r2, [r10] @ TLB will miss + + ldr r2, [r11] @Get the IOMUX settings + + mrc p15, 0, r0, c10, c0, 0 @//@ Read the lockdown register + @//@ (victim will be incremented) + bic r0, r0, #1 @//@ Clear the preserve bit + mcr p15, 0, r0, c10, c0, 0 @//@ Write to the lockdown register + + /* Disable automatic power saving. */ + + ldr r0, [r5, #0x404] + orr r0, r0, #0x01 + str r0, [r5, #0x404] + + /* Disable MMDC power down timer. */ + /*MMDC0_MDPDC disable power down timer */ + ldr r0, [r5, #0x4] + bic r0, r0, #0xff00 + str r0, [r5, #0x4] + + /* set CON_REG */ + ldr r0, =0x8000 + str r0, [r5, #0x1C] +poll_conreq_set_1: + ldr r0, [r5, #0x1C] + and r0, r0, #0x4000 + cmp r0, #0x4000 + bne poll_conreq_set_1 + + /*setmem /32 0x021b001c = 0x00008010 //Precharge all on cs0 */ + /*setmem /32 0x021b001c = 0x00008018 //Precharge all on cs1 */ + ldr r0, =0x00008010 + str r0, [r5, #0x1C] + ldr r0, =0x00008018 + str r0, [r5, #0x1C] + + /* if requested frequency is greater than 300MHz go to DLL on mode */ + ldr r1, =300000000 + cmp r4, r1 + bge dll_on_mode + +dll_off_mode: + + /* if DLL is currently on, turn it off + cmp r9, #1 + beq continue_dll_off_1 + + /* setmem /32 0x021b001c = 0x00018031 //Rtt_NOM off + set dll off, cs 0 */ + /* setmem /32 0x021b001c = 0x00018039 //Rtt_NOM off + set dll off, cs 1 */ + ldr r0, =0x00018031 + str r0, [r5, #0x1C] + + ldr r0, =0x00018039 + str r0, [r5, #0x1C] + + ldr r1, =10 +delay1a: + ldr r2, =0 +cont1a: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont1a + sub r1, r1, #1 + cmp r1, #0 + bgt delay1a + +continue_dll_off_1: + + /* set DVFS - enter self refresh mode */ + ldr r0, [r5, #0x404] + orr r0, r0, #0x200000 + str r0, [r5, #0x404] + + /* de-assert con_req */ + mov r0, #0x0 + str r0, [r5, #0x1C] + +poll_dvfs_set_1: + ldr r0, [r5, #0x404] + and r0, r0, #0x2000000 + cmp r0, #0x2000000 + bne poll_dvfs_set_1 + + ldr r1, =24000000 + cmp r4, r1 + beq switch_freq_24 + + switch_to_50MHz + b continue_dll_off_2 + +switch_freq_24: + switch_to_24MHz + +continue_dll_off_2: + + /* set SBS - block ddr accesses */ + ldr r0, [r5, #0x410] + orr r0, r0, #0x100 + str r0, [r5, #0x410] + + /* clear DVFS - exit from self refresh mode */ + ldr r0, [r5, #0x404] + bic r0, r0, #0x200000 + str r0, [r5, #0x404] + +poll_dvfs_clear_1: + ldr r0, [r5, #0x404] + and r0, r0, #0x2000000 + cmp r0, #0x2000000 + beq poll_dvfs_clear_1 + + /* if DLL was previously on, continue DLL off routine + cmp r9, #1 + beq continue_dll_off_3 + + /* setmem /32 0x021b001c = 0x00018031 //Rtt_NOM off + set dll off, cs 0 */ + /* setmem /32 0x021b001c = 0x00018039 //Rtt_NOM off + set dll off, cs 1 */ + ldr r0, =0x00018031 + str r0, [r5, #0x1C] + + ldr r0, =0x00018039 + str r0, [r5, #0x1C] + + /* setmem /32 0x021b001c = 0x04208030 //write mode reg MR0: CL=6, wr=6 ,cs 0 */ + /* setmem /32 0x021b001c = 0x04208038 //write mode reg MR0: CL=6, wr=6 ,cs 1 */ + ldr r0, =0x08208030 + str r0, [r5, #0x1C] + + ldr r0, =0x08208038 + str r0, [r5, #0x1C] + + /* setmem /32 0x021b001c = 0x02088032 //write mode reg MR2 , CWL=6 ,cs0 */ + /* setmem /32 0x021b001c = 0x0208803A //write mode reg MR2 , CWL=6 ,cs1 */ + ldr r0, =0x00088032 + str r0, [r5, #0x1C] + + ldr r0, =0x0008803A + str r0, [r5, #0x1C] + + /* double refresh ???? + ldr r0, =0x00001800 + str r0, [r5, #0x20]*/ + + /* delay for a while. */ + ldr r1, =4 +delay_1: + ldr r2, =0 +cont_1: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont_1 + sub r1, r1, #1 + cmp r1, #0 + bgt delay_1 + + /* MMDC0_MDCFG0 see spread sheet for timings, CAS=6 */ + ldr r0, [r5, #0x0C] + bic r0, r0, #0xf + orr r0, r0, #0x3 + str r0, [r5, #0x0C] + + /* MMDC0_MDCFG1 see spread sheet for timings, tCWL=6 */ + ldr r0, [r5, #0x10] + bic r0, r0, #0x7 + orr r0, r0, #0x4 + str r0, [r5, #0x10] + + /* Enable bank interleaving, Address mirror on, WALAT = 0x1, RALAT = 0x2, DDR2_EN = 0 */ + /*setmem /32 0x021b0018 = 0x00091680 */ + ldr r0, =0x00091680 + str r0, [r5, #0x18] + + /* enable dqs pull down in the IOMUX. */ + /* + setmem /32 0x020e05a8 = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS0 - DSE=110 + setmem /32 0x020e05b0 = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS1 - DSE=110 + setmem /32 0x020e0524 = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS2 - DSE=110 + setmem /32 0x020e051c = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS3 - DSE=110 + setmem /32 0x020e0518 = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS4 - DSE=110 + setmem /32 0x020e050c = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS5 - DSE=110 + setmem /32 0x020e05b8 = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS6 - DSE=110 + setmem /32 0x020e05c0 = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS7 - DSE=110 + */ + ldr r1, [r11] @size of array + add r11, r11, #8 @skip first eight bytes in array + ldr r2, =0x3028 +update_iomux: + ldr r0, [r11, #0x0] @ offset + ldr r3, [r7, r0] + bic r3, r3, r2 @ Clear the DSE, PUE and PKE bits + orr r3, r3, #0x3000 @ Enable the Pull downs and lower the drive strength. + orr r3, r3, #0x28 + str r3, [r7, r0] + add r11, r11, #8 + sub r1, r1, #1 + cmp r1, #0 + bgt update_iomux + + /* ODT disabled */ + /* setmem /32 0x021b0818 = 0x0 // DDR_PHY_P0_MPODTCTRL */ + /* setmem /32 0x021b4818 = 0x0 // DDR_PHY_P1_MPODTCTRL */ + ldr r0, =0x0 + ldr r2, =0x818 + str r0, [r5, r2] + ldr r2, =0x4818 + str r0, [r5, r2] + + /* DQS gating disabled */ + /* setmem /32 0x021b083c = 0x233f033f */ + ldr r2, =0x83c + ldr r0, [r5, r2] + orr r0, r0, #0x20000000 + str r0, [r5, r2] + + ldr r2, =0x483c + ldr r0, [r5, r2] + orr r0, r0, #0x20000000 + str r0, [r5, r2] + + /* MMDC0_MAPSR adopt power down enable */ + /* setmem /32 0x021b0404 = 0x00011006 */ + ldr r0, [r5, #0x404] + bic r0, r0, #0x01 + str r0, [r5, #0x404] + + /* frc_msr + mu bypass*/ + ldr r0, =0x00000060 + str r0, [r5, #0x8b8] + ldr r2, =0x48b8 + str r0, [r5, r2] + ldr r0, =0x00000460 + str r0, [r5, #0x8b8] + ldr r2, =0x48b8 + str r0, [r5, r2] + ldr r0, =0x00000c60 + str r0, [r5, #0x8b8] + ldr r2, =0x48b8 + str r0, [r5, r2] + +continue_dll_off_3: + + /* clear SBS - unblock accesses to DDR */ + ldr r0, [r5, #0x410] + bic r0, r0, #0x100 + str r0, [r5, #0x410] + + mov r0, #0x0 + str r0, [r5, #0x1C] +poll_conreq_clear_1: + ldr r0, [r5, #0x1C] + and r0, r0, #0x4000 + cmp r0, #0x4000 + beq poll_conreq_clear_1 + + b done + +dll_on_mode: + /* assert DVFS - enter self refresh mode */ + ldr r0, [r5, #0x404] + orr r0, r0, #0x200000 + str r0, [r5, #0x404] + + /* de-assert CON_REQ */ + mov r0, #0x0 + str r0, [r5, #0x1C] + + /* poll DVFS ack */ +poll_dvfs_set_2: + ldr r0, [r5, #0x404] + and r0, r0, #0x2000000 + cmp r0, #0x2000000 + bne poll_dvfs_set_2 + + ldr r1, =528000000 + cmp r4, r1 + beq switch_freq_528 + + switch_to_400MHz + + b continue_dll_on + +switch_freq_528: + switch_to_528MHz + +continue_dll_on: + + /* set step-by-step mode */ + ldr r0, [r5, #0x410] + orr r0, r0, #0x100 + str r0, [r5, #0x410] + + /* clear DVFS - exit self refresh mode */ + ldr r0, [r5, #0x404] + bic r0, r0, #0x200000 + str r0, [r5, #0x404] + +poll_dvfs_clear_2: + ldr r0, [r5, #0x404] + and r0, r0, #0x2000000 + cmp r0, #0x2000000 + beq poll_dvfs_clear_2 + + /* if DLL is currently off, turn it back on */ + cmp r9, #0 + beq update_calibration + + ldr r0, =0xa5390003 + str r0, [r5, #0x800] + ldr r2, =0x4800 + str r0, [r5, r2] + + /* enable DQS gating */ + ldr r2, =0x83c + ldr r0, [r5, r2] + bic r0, r0, #0x20000000 + str r0, [r5, r2] + + ldr r2, =0x483c + ldr r0, [r5, r2] + bic r0, r0, #0x20000000 + str r0, [r5, r2] + + /* force measure */ + ldr r0, =0x00000800 + str r0, [r5, #0x8b8] + ldr r2, =0x48b8 + str r0, [r5, r2] + + /* delay for while */ + ldr r1, =4 +delay5: + ldr r2, =0 +cont5: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont5 + sub r1, r1, #1 + cmp r1, #0 + bgt delay5 + + /* Disable dqs pull down in the IOMUX. */ + /* + setmem /32 0x020e05a8 = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS0 - DSE=110 + setmem /32 0x020e05b0 = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS1 - DSE=110 + setmem /32 0x020e0524 = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS2 - DSE=110 + setmem /32 0x020e051c = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS3 - DSE=110 + setmem /32 0x020e0518 = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS4 - DSE=110 + setmem /32 0x020e050c = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS5 - DSE=110 + setmem /32 0x020e05b8 = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS6 - DSE=110 + setmem /32 0x020e05c0 = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS7 - DSE=110 + */ + ldr r1, [r11] @size of array + add r11, r11, #8 @skip first eight bytes in array +update_iomux1: + ldr r0, [r11, #0x0] @ offset + ldr r3, [r11, #0x4] + str r3, [r7, r0] @Store the original IOMUX value read during boot + add r11, r11, #8 + sub r1, r1, #1 + cmp r1, #0 + bgt update_iomux1 + + /* config ESDCTL timings to 528MHz: + @// setmem /32 0x021b000c = 0x555A7975 @// MMDC0_MDCFG0 see spread sheet for timings + @//setmem /32 0x021b0010 = 0xFF538E64 @// MMDC0_MDCFG1 see spread sheet for timings + @//setmem /32 0x021b0014 = 0x01ff00db @// MMDC0_MDCFG2 - tRRD - 4ck; tWTR - 4ck; tRTP - 4ck; tDLLK - 512ck + @//setmem /32 0x021b0018 = 0x00081740 @// MMDC0_MDMISC, RALAT=0x5 (original value) + */ + + ldr r0, [r5, #0x0C] + bic r0, r0, #0xf + orr r0, r0, #0x5 + str r0, [r5, #0x0C] + + ldr r0, [r5, #0x10] + bic r0, r0, #0x7 + orr r0, r0, #0x4 + str r0, [r5, #0x10] + + /* update MISC register: WALAT, RALAT */ + ldr r0, =0x00081740 + str r0, [r5, #0x18] + + /*configure ddr devices to dll on, odt + @//setmem /32 0x021b001c = 0x00428031 + @//setmem /32 0x021b001c = 0x00428039 + */ + ldr r0, =0x00028031 + str r0, [r5, #0x1C] + + ldr r0, =0x00028039 + str r0, [r5, #0x1C] + + /* delay for while */ + ldr r1, =4 +delay7: + ldr r2, =0 +cont7: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont7 + sub r1, r1, #1 + cmp r1, #0 + bgt delay7 + + /* reset dll + @// setmem /32 0x021b001c = 0x09208030 + @// setmem /32 0x021b001c = 0x09208038 + */ + ldr r0, =0x09208030 + str r0, [r5, #0x1C] + + ldr r0, =0x09208038 + str r0, [r5, #0x1C] + + /* delay for while */ + ldr r1, =100 +delay8: + ldr r2, =0 +cont8: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont8 + sub r1, r1, #1 + cmp r1, #0 + bgt delay8 + + /* tcwl=6: + @//setmem /32 0x021b001c = 0x04088032 + @//setmem /32 0x021b001c = 0x0408803a + */ + ldr r0, =0x04088032 + str r0, [r5, #0x1C] + + ldr r0, =0x0408803a + str r0, [r5, #0x1C] + + ldr r0, =0x00428031 + str r0, [r5, #0x1C] + + ldr r0, =0x00428039 + str r0, [r5, #0x1C] + + /* tcl=8 + @// setmem /32 0x021b001c = 0x08408030 + @// setmem /32 0x021b001c = 0x08408038 + */ + ldr r0, =0x08408030 + str r0, [r5, #0x1C] + + ldr r0, =0x08408038 + str r0, [r5, #0x1C] + + /* issue a zq command + @// setmem /32 0x021b001c = 0x04000040 + @// setmem /32 0x021b001c = 0x04000048 + */ + ldr r0, =0x04008040 + str r0, [r5, #0x1C] + + ldr r0, =0x04008048 + str r0, [r5, #0x1C] + + /* ESDCTL ODT enable + @//setmem /32 0x021b0818 = 0x00022225 @// DDR_PHY_P0_MPODTCTRL + @//setmem /32 0x021b4818 = 0x00022225 @// DDR_PHY_P1_MPODTCTRL + */ + ldr r0, =0x00022225 + str r0, [r5, #0x818] + ldr r2, =0x4818 + str r0, [r5, r2] + + /* delay for while */ + ldr r1, =40 +delay15: + ldr r2, =0 +cont15: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont15 + sub r1, r1, #1 + cmp r1, #0 + bgt delay15 + + /* MMDC0_MAPSR adopt power down enable */ + /* setmem /32 0x021b0404 = 0x00011006 */ + ldr r0, [r5, #0x404] + bic r0, r0, #0x01 + str r0, [r5, #0x404] + + /* Enable MMDC power down timer. */ + ldr r0, [r5, #0x4] + orr r0, r0, #0x5500 + str r0, [r5, #0x4] + +update_calibration: + /* Write the new calibration values. */ + ldr r1, [r8] @size of array + add r8, r8, #8 @skip first eight bytes in array +update_calib: + ldr r0, [r8, #0x0] @ offset + ldr r3, [r8, #0x4] @ value + str r3, [r5, r0] + add r8, r8, #8 + sub r1, r1, #1 + cmp r1, #0 + bgt update_calib + + /* Perform a force measurement. */ + ldr r0, =0x800 + str r0, [r5, #0x8B8] + ldr r2, =0x48B8 + str r0, [r5, r2] + + /* clear SBS - unblock DDR accesses */ + ldr r0, [r5, #0x410] + bic r0, r0, #0x100 + str r0, [r5, #0x410] + + mov r0, #0x0 + str r0, [r5, #0x1C] +poll_conreq_clear_2: + ldr r0, [r5, #0x1C] + and r0, r0, #0x4000 + cmp r0, #0x4000 + beq poll_conreq_clear_2 + +done: + + /* Restore registers */ + + ldmfd sp!, {r4,r5,r6, r7, r8, r9, r10, r11} + + mov pc, lr + + .type mx6_do_ddr_freq_change, #object +ENTRY(mx6_do_ddr_freq_change) + .word mx6_ddr_freq_change + .size mx6_ddr_freq_change, . - mx6_ddr_freq_change diff --git a/arch/arm/mach-mx6/mx6_mmdc.c b/arch/arm/mach-mx6/mx6_mmdc.c new file mode 100644 index 000000000000..2b5ce842489e --- /dev/null +++ b/arch/arm/mach-mx6/mx6_mmdc.c @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mx6_mmdc.c + * + * @brief MX6 MMDC specific file. + * + * @ingroup PM + */ +#include <asm/io.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/iram_alloc.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/cpumask.h> +#include <linux/kernel.h> +#include <linux/smp.h> +#include <mach/hardware.h> +#include <mach/clock.h> +#include <asm/mach/map.h> +#include <asm/mach-types.h> +#include <asm/cacheflush.h> +#include <asm/tlb.h> +#include <asm/hardware/gic.h> +#include "crm_regs.h" + + +/* DDR settings */ +unsigned long (*iram_ddr_settings)[2]; +unsigned long (*normal_mmdc_settings)[2]; +unsigned long (*iram_iomux_settings)[2]; +void __iomem *mmdc_base; +void __iomem *iomux_base; +void __iomem *gic_dist_base; +void __iomem *gic_cpu_base; + +void (*mx6_change_ddr_freq)(u32 freq, void *ddr_settings, bool dll_mode, void* iomux_offsets) = NULL; + +extern unsigned int ddr_low_rate; +extern unsigned int ddr_med_rate; +extern unsigned int ddr_normal_rate; +extern int low_bus_freq_mode; +extern int mmdc_med_rate; +extern void __iomem *ccm_base; +extern void mx6_ddr_freq_change(u32 freq, void *ddr_settings, bool dll_mode, void *iomux_offsets); + +static void *ddr_freq_change_iram_base; +static int ddr_settings_size; +static int iomux_settings_size; +static volatile unsigned int cpus_in_wfe; +static volatile bool wait_for_ddr_freq_update; + + +#define MIN_DLL_ON_FREQ 333000000 +#define MAX_DLL_OFF_FREQ 125000000 + +unsigned long ddr3_mmdc_regs_offsets[][2] = { + {0x83c, 0x0}, + {0x840, 0x0}, + {0x483c, 0x0}, + {0x4840, 0x0}, + {0x848, 0x0}, + {0x4848, 0x0}, + {0x850, 0x0}, + {0x4850, 0x0}, +}; + +unsigned long iomux_offsets_mx6q[][2] = { + {0x5A8, 0x0}, + {0x5B0, 0x0}, + {0x524, 0x0}, + {0x51C, 0x0}, + {0x518, 0x0}, + {0x50C, 0x0}, + {0x5B8, 0x0}, + {0x5C0, 0x0}, +}; +unsigned long iomux_offsets_mx6dl[][2] = { + {0x4BC, 0x0}, + {0x4C0, 0x0}, + {0x4C4, 0x0}, + {0x4C8, 0x0}, + {0x4CC, 0x0}, + {0x4D0, 0x0}, + {0x4D4, 0x0}, + {0x4D8, 0x0}, +}; + +unsigned long ddr3_400[][2] = { + {0x83c, 0x42490249}, + {0x840, 0x02470247}, + {0x483c, 0x42570257}, + {0x4840, 0x02400240}, + {0x848, 0x4039363C}, + {0x4848, 0x3A39333F}, + {0x850, 0x38414441}, + {0x4850, 0x472D4833} +}; + +unsigned long *irq_used; + +unsigned long irqs_used_mx6q[] = { + MXC_INT_INTERRUPT_139_NUM, + MX6Q_INT_PERFMON1, + MX6Q_INT_PERFMON2, + MX6Q_INT_PERFMON3, +}; + +unsigned long irqs_used_mx6dl[] = { + MXC_INT_INTERRUPT_139_NUM, + MX6Q_INT_PERFMON1, +}; + +int can_change_ddr_freq(void) +{ + return 1; +} + + +/* Each active core apart from the one changing the DDR frequency will execute + * this function. The rest of the cores have to remain in WFE state until the frequency + * is changed. + */ +irqreturn_t wait_in_wfe_irq(int irq, void *dev_id) +{ + u32 me = smp_processor_id(); + + *((char *)(&cpus_in_wfe) + (u8)me) = 0xff; + + while (wait_for_ddr_freq_update) + wfe(); + + *((char *)(&cpus_in_wfe) + (u8)me) = 0; + return IRQ_HANDLED; +} + +/* Change the DDR frequency. */ +int update_ddr_freq(int ddr_rate) +{ + int i; + unsigned int reg; + bool dll_off = false; + unsigned int online_cpus = 0; + int cpu = 0; + int me; + + if (!can_change_ddr_freq()) + return -1; + + if (low_bus_freq_mode) + dll_off = true; + + iram_ddr_settings[0][0] = ddr_settings_size; + iram_iomux_settings[0][0] = iomux_settings_size; + if (ddr_rate == ddr_med_rate) { + for (i = 0; i < iram_ddr_settings[0][0]; i++) { + iram_ddr_settings[i + 1][0] = + ddr3_400[i][0]; + iram_ddr_settings[i + 1][1] = + ddr3_400[i][1]; + } + } else if (ddr_rate == ddr_normal_rate) { + for (i = 0; i < iram_ddr_settings[0][0]; i++) { + iram_ddr_settings[i + 1][0] = + normal_mmdc_settings[i][0]; + iram_ddr_settings[i + 1][1] = + normal_mmdc_settings[i][1]; + } + } + + /* Ensure that all Cores are in WFE. */ + local_irq_disable(); + + me = smp_processor_id(); + + *((char *)(&cpus_in_wfe) + (u8)me) = 0xff; + wait_for_ddr_freq_update = true; + + for_each_online_cpu(cpu) { + *((char *)(&online_cpus) + (u8)cpu) = 0xff; + if (cpu != me) { + /* Set the interrupt to be pending in the GIC. */ + reg = 1 << (irq_used[cpu] % 32); + writel_relaxed(reg, gic_dist_base + GIC_DIST_PENDING_SET + (irq_used[cpu] / 32) * 4); + udelay(10); + } + } + while (cpus_in_wfe != online_cpus) + udelay(5); + + /* Now we can change the DDR frequency. */ + mx6_change_ddr_freq(ddr_rate, iram_ddr_settings, dll_off, iram_iomux_settings); + + /* DDR frequency change is done . */ + wait_for_ddr_freq_update = false; + + /* Wake up all the cores. */ + sev(); + + *((char *)(&cpus_in_wfe) + (u8)me) = 0; + + local_irq_enable(); + + return 0; +} + +int init_mmdc_settings(void) +{ + unsigned long iram_paddr; + int i, err, cpu; + + mmdc_base = ioremap(MMDC_P0_BASE_ADDR, SZ_32K); + iomux_base = ioremap(MX6Q_IOMUXC_BASE_ADDR, SZ_16K); + gic_dist_base = ioremap(IC_DISTRIBUTOR_BASE_ADDR, SZ_16K); + gic_cpu_base = ioremap(IC_INTERFACES_BASE_ADDR, SZ_16K); + + normal_mmdc_settings = ddr3_mmdc_regs_offsets; + ddr_settings_size = ARRAY_SIZE(ddr3_mmdc_regs_offsets); + + /* Store the original DDR settings at boot. */ + for (i = 0; i < ddr_settings_size; i++) { + normal_mmdc_settings[i][1] = + __raw_readl(mmdc_base + + normal_mmdc_settings[i][0]); + } + /* Store the size of the array in iRAM also, + * increase the size by 8 bytes. + */ + iram_ddr_settings = iram_alloc(ddr_settings_size + 8, &iram_paddr); + if (iram_ddr_settings == NULL) { + printk(KERN_DEBUG + "%s: failed to allocate iRAM memory for ddr settings\n", + __func__); + return ENOMEM; + } + + /* Store the size of the iomux settings in iRAM also, + * increase the size by 8 bytes. + */ + iram_iomux_settings = iram_alloc(iomux_settings_size + 8, &iram_paddr); + if (iram_iomux_settings == NULL) { + printk(KERN_DEBUG + "%s: failed to allocate iRAM memory for iomuxr settings\n", + __func__); + return ENOMEM; + } + + /* Store the IOMUX settings at boot. */ + if (cpu_is_mx6q()) { + iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6q); + for (i = 0; i < iomux_settings_size; i++) { + iomux_offsets_mx6q[i][1] = + __raw_readl(iomux_base + + iomux_offsets_mx6q[i][0]); + iram_iomux_settings[i+1][0] = iomux_offsets_mx6q[i][0]; + iram_iomux_settings[i+1][1] = iomux_offsets_mx6q[i][1]; + } + irq_used = irqs_used_mx6q; + } + if (cpu_is_mx6dl()) { + iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6dl); + for (i = 0; i < iomux_settings_size; i++) { + iomux_offsets_mx6dl[i][1] = + __raw_readl(iomux_base + + iomux_offsets_mx6dl[i][0]); + iram_iomux_settings[i+1][0] = iomux_offsets_mx6dl[i][0]; + iram_iomux_settings[i+1][1] = iomux_offsets_mx6dl[i][1]; + } + irq_used = irqs_used_mx6dl; + } + + /* Allocate IRAM for the DDR freq change code. */ + iram_alloc(SZ_8K, &iram_paddr); + /* Need to remap the area here since we want the memory region + to be executable. */ + ddr_freq_change_iram_base = __arm_ioremap(iram_paddr, + SZ_8K, MT_MEMORY_NONCACHED); + memcpy(ddr_freq_change_iram_base, mx6_ddr_freq_change, SZ_8K); + mx6_change_ddr_freq = (void *)ddr_freq_change_iram_base; + + for_each_online_cpu(cpu) { + /* Set up a reserved interrupt to get all the active cores into a WFE state + * before changing the DDR frequency. + */ + err = request_irq(irq_used[cpu], wait_in_wfe_irq, IRQF_PERCPU, "mmdc_1", + NULL); + if (err) { + printk(KERN_ERR "MMDC: Unable to attach to %ld,err = %d\n", irq_used[cpu], err); + return err; + } + err = irq_set_affinity(irq_used[cpu], cpumask_of(cpu)); + if (err) { + printk(KERN_ERR "MMDC: unable to set irq affinity irq=%ld,\n", irq_used[cpu]); + return err; + } + } + return 0; +} diff --git a/arch/arm/mach-mx6/pcie.c b/arch/arm/mach-mx6/pcie.c index 985e6252a5f9..38e9abcd29cc 100644 --- a/arch/arm/mach-mx6/pcie.c +++ b/arch/arm/mach-mx6/pcie.c @@ -29,6 +29,9 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/gpio.h> +#include <linux/platform_device.h> + +#include <mach/pcie.h> #include <asm/sizes.h> @@ -86,6 +89,75 @@ /* GPR8: iomuxc_gpr8_tx_swing_low(iomuxc_gpr8[31:25]) */ #define iomuxc_gpr8_tx_swing_low (0x7F << 25) +/* Registers of PHY */ +/* Register PHY_STS_R */ +/* PHY Status Register */ +#define PHY_STS_R (PRT_LOG_R_BaseAddress + 0x110) + +/* Register PHY_CTRL_R */ +/* PHY Control Register */ +#define PHY_CTRL_R (PRT_LOG_R_BaseAddress + 0x114) + +#define SSP_CR_SUP_DIG_MPLL_OVRD_IN_LO 0x0011 +/* FIELD: RES_ACK_IN_OVRD [15:15] +// FIELD: RES_ACK_IN [14:14] +// FIELD: RES_REQ_IN_OVRD [13:13] +// FIELD: RES_REQ_IN [12:12] +// FIELD: RTUNE_REQ_OVRD [11:11] +// FIELD: RTUNE_REQ [10:10] +// FIELD: MPLL_MULTIPLIER_OVRD [9:9] +// FIELD: MPLL_MULTIPLIER [8:2] +// FIELD: MPLL_EN_OVRD [1:1] +// FIELD: MPLL_EN [0:0] +*/ + +#define SSP_CR_SUP_DIG_ATEOVRD 0x0010 +/* FIELD: ateovrd_en [2:2] +// FIELD: ref_usb2_en [1:1] +// FIELD: ref_clkdiv2 [0:0] +*/ + +#define SSP_CR_LANE0_DIG_RX_OVRD_IN_LO 0x1005 +/* FIELD: RX_LOS_EN_OVRD [13:13] +// FIELD: RX_LOS_EN [12:12] +// FIELD: RX_TERM_EN_OVRD [11:11] +// FIELD: RX_TERM_EN [10:10] +// FIELD: RX_BIT_SHIFT_OVRD [9:9] +// FIELD: RX_BIT_SHIFT [8:8] +// FIELD: RX_ALIGN_EN_OVRD [7:7] +// FIELD: RX_ALIGN_EN [6:6] +// FIELD: RX_DATA_EN_OVRD [5:5] +// FIELD: RX_DATA_EN [4:4] +// FIELD: RX_PLL_EN_OVRD [3:3] +// FIELD: RX_PLL_EN [2:2] +// FIELD: RX_INVERT_OVRD [1:1] +// FIELD: RX_INVERT [0:0] +*/ + +#define SSP_CR_LANE0_DIG_RX_ASIC_OUT 0x100D +/* FIELD: LOS [2:2] +// FIELD: PLL_STATE [1:1] +// FIELD: VALID [0:0] +*/ + +/* control bus bit definition */ +#define PCIE_CR_CTL_DATA_LOC 0 +#define PCIE_CR_CTL_CAP_ADR_LOC 16 +#define PCIE_CR_CTL_CAP_DAT_LOC 17 +#define PCIE_CR_CTL_WR_LOC 18 +#define PCIE_CR_CTL_RD_LOC 19 +#define PCIE_CR_STAT_DATA_LOC 0 +#define PCIE_CR_STAT_ACK_LOC 16 + +#define PCIE_CAP_STRUC_BaseAddress 0x70 + +/* Register LNK_CAP */ +/* PCIE Link cap */ +#define LNK_CAP (PCIE_CAP_STRUC_BaseAddress + 0xc) +#define LNK_CAP_RegisterSize 32 +#define LNK_CAP_RegisterResetValue 0x011cc12 +#define LNK_CAP_RegisterResetMask 0xffffffff + /* End of Register Definitions */ #define PCIE_DBI_BASE_ADDR (PCIE_ARB_END_ADDR - SZ_16K + 1) @@ -95,9 +167,6 @@ #define PCIE_CONF_FUNC(f) (((f) & 0x7) << 8) #define PCIE_CONF_REG(r) ((r) & ~0x3) -#define MX6_ARM2_PCIE_PWR_EN (IMX_GPIO_NR(8, 0) + 2) -#define MX6_ARM2_PCIE_RESET (IMX_GPIO_NR(8, 8) + 2) - static void __iomem *base; static void __iomem *dbi_base; @@ -125,6 +194,10 @@ struct imx_pcie_port { static struct imx_pcie_port imx_pcie_port[1]; static int num_pcie_ports; +static int pcie_phy_cr_read(int addr, int *data); +static int pcie_phy_cr_write(int addr, int data); +static void change_field(int *in, int start, int end, int val); + /* IMX PCIE GPR configure routines */ static inline void imx_pcie_clrset(u32 mask, u32 val, void __iomem *addr) { @@ -194,13 +267,39 @@ static int __init imx_pcie_setup(int nr, struct pci_sys_data *sys) static int imx_pcie_link_up(void __iomem *dbi_base) { /* Check the pcie link up or link down */ - u32 rc, iterations = 0x100000; + int iterations = 200; + u32 rc, ltssm, rx_valid, temp; do { /* link is debug bit 36 debug 1 start in bit 32 */ - rc = readl(dbi_base + DB_R1) & (0x1 << (36-32)) ; + rc = readl(dbi_base + DB_R1) & (0x1 << (36 - 32)) ; iterations--; - if ((iterations % 0x100000) == 0) + usleep_range(2000, 3000); + + /* From L0, initiate MAC entry to gen2 if EP/RC supports gen2. + * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2). + * If (MAC/LTSSM.state == Recovery.RcvrLock) + * && (PHY/rx_valid==0) then pulse PHY/rx_reset. Transition + * to gen2 is stuck + */ + pcie_phy_cr_read(SSP_CR_LANE0_DIG_RX_ASIC_OUT, &rx_valid); + ltssm = readl(dbi_base + DB_R0) & 0x3F; + if ((ltssm == 0x0D) && ((rx_valid & 0x01) == 0)) { + pr_info("Transition to gen2 is stuck, reset PHY!\n"); + pcie_phy_cr_read(SSP_CR_LANE0_DIG_RX_OVRD_IN_LO, &temp); + change_field(&temp, 3, 3, 0x1); + change_field(&temp, 5, 5, 0x1); + pcie_phy_cr_write(SSP_CR_LANE0_DIG_RX_OVRD_IN_LO, + 0x0028); + usleep_range(2000, 3000); + pcie_phy_cr_read(SSP_CR_LANE0_DIG_RX_OVRD_IN_LO, &temp); + change_field(&temp, 3, 3, 0x0); + change_field(&temp, 5, 5, 0x0); + pcie_phy_cr_write(SSP_CR_LANE0_DIG_RX_OVRD_IN_LO, + 0x0000); + } + + if ((iterations < 0)) pr_info("link up failed, DB_R0:0x%08x, DB_R1:0x%08x!\n" , readl(dbi_base + DB_R0) , readl(dbi_base + DB_R1)); @@ -351,15 +450,151 @@ static struct hw_pci imx_pci __initdata = { .map_irq = imx_pcie_map_irq, }; -static void imx_pcie_enable_controller(void) +/* PHY CR bus acess routines */ +static int pcie_phy_cr_ack_polling(int max_iterations, int exp_val) +{ + u32 temp_rd_data, wait_counter = 0; + + do { + temp_rd_data = readl(dbi_base + PHY_STS_R); + temp_rd_data = (temp_rd_data >> PCIE_CR_STAT_ACK_LOC) & 0x1; + wait_counter++; + } while ((wait_counter < max_iterations) && (temp_rd_data != exp_val)); + + if (temp_rd_data != exp_val) + return 0 ; + return 1 ; +} + +static int pcie_phy_cr_cap_addr(int addr) +{ + u32 temp_wr_data; + + /* write addr */ + temp_wr_data = addr << PCIE_CR_CTL_DATA_LOC ; + writel(temp_wr_data, dbi_base + PHY_CTRL_R); + + /* capture addr */ + temp_wr_data |= (0x1 << PCIE_CR_CTL_CAP_ADR_LOC); + writel(temp_wr_data, dbi_base + PHY_CTRL_R); + + /* wait for ack */ + if (!pcie_phy_cr_ack_polling(100, 1)) + return 0; + + /* deassert cap addr */ + temp_wr_data = addr << PCIE_CR_CTL_DATA_LOC; + writel(temp_wr_data, dbi_base + PHY_CTRL_R); + + /* wait for ack de-assetion */ + if (!pcie_phy_cr_ack_polling(100, 0)) + return 0 ; + + return 1 ; +} + +static int pcie_phy_cr_read(int addr , int *data) +{ + u32 temp_rd_data, temp_wr_data; + + /* write addr */ + /* cap addr */ + if (!pcie_phy_cr_cap_addr(addr)) + return 0; + + /* assert rd signal */ + temp_wr_data = 0x1 << PCIE_CR_CTL_RD_LOC; + writel(temp_wr_data, dbi_base + PHY_CTRL_R); + + /* wait for ack */ + if (!pcie_phy_cr_ack_polling(100, 1)) + return 0; + + /* after got ack return data */ + temp_rd_data = readl(dbi_base + PHY_STS_R); + *data = (temp_rd_data & (0xffff << PCIE_CR_STAT_DATA_LOC)) ; + + /* deassert rd signal */ + temp_wr_data = 0x0; + writel(temp_wr_data, dbi_base + PHY_CTRL_R); + + /* wait for ack de-assetion */ + if (!pcie_phy_cr_ack_polling(100, 0)) + return 0 ; + + return 1 ; + +} + +static int pcie_phy_cr_write(int addr, int data) +{ + u32 temp_wr_data; + + /* write addr */ + /* cap addr */ + if (!pcie_phy_cr_cap_addr(addr)) + return 0 ; + + temp_wr_data = data << PCIE_CR_CTL_DATA_LOC; + writel(temp_wr_data, dbi_base + PHY_CTRL_R); + + /* capture data */ + temp_wr_data |= (0x1 << PCIE_CR_CTL_CAP_DAT_LOC); + writel(temp_wr_data, dbi_base + PHY_CTRL_R); + + /* wait for ack */ + if (!pcie_phy_cr_ack_polling(100, 1)) + return 0 ; + + /* deassert cap data */ + temp_wr_data = data << PCIE_CR_CTL_DATA_LOC; + writel(temp_wr_data, dbi_base + PHY_CTRL_R); + + /* wait for ack de-assetion */ + if (!pcie_phy_cr_ack_polling(100, 0)) + return 0; + + /* assert wr signal */ + temp_wr_data = 0x1 << PCIE_CR_CTL_WR_LOC; + writel(temp_wr_data, dbi_base + PHY_CTRL_R); + + /* wait for ack */ + if (!pcie_phy_cr_ack_polling(100, 1)) + return 0; + + /* deassert wr signal */ + temp_wr_data = data << PCIE_CR_CTL_DATA_LOC; + writel(temp_wr_data, dbi_base + PHY_CTRL_R); + + /* wait for ack de-assetion */ + if (!pcie_phy_cr_ack_polling(100, 0)) + return 0; + + temp_wr_data = 0x0 ; + writel(temp_wr_data, dbi_base + PHY_CTRL_R); + + return 1 ; +} + +static void change_field(int *in, int start, int end, int val) +{ + int mask; + + mask = ((0xFFFFFFFF << start) ^ (0xFFFFFFFF << (end + 1))) & 0xFFFFFFFF; + *in = (*in & ~mask) | (val << start); +} + +static void imx_pcie_enable_controller(struct device *dev) { struct clk *pcie_clk; + struct imx_pcie_platform_data *pdata = dev->platform_data; + + /* Enable PCIE power */ + gpio_request(pdata->pcie_pwr_en, "PCIE POWER_EN"); - /* PCIE PWR_EN: EXP_IO2 of MAX7310_1 */ - gpio_request(MX6_ARM2_PCIE_PWR_EN, "PCIE POWER_EN"); + /* activate PCIE_PWR_EN */ + gpio_direction_output(pdata->pcie_pwr_en, 1); - /* activate PCIE_PWR_EN CTRL_2 */ - gpio_direction_output(MX6_ARM2_PCIE_PWR_EN, 1); imx_pcie_clrset(iomuxc_gpr1_test_powerdown, 0 << 18, IOMUXC_GPR1); /* enable the clks */ @@ -373,23 +608,28 @@ static void imx_pcie_enable_controller(void) } } -static void card_reset(void) +static void card_reset(struct device *dev) { - /* PCIE RESET: EXP_IO2 of MAX7310_2 */ - gpio_request(MX6_ARM2_PCIE_RESET, "PCIE RESET"); + struct imx_pcie_platform_data *pdata = dev->platform_data; + + /* PCIE RESET */ + gpio_request(pdata->pcie_rst, "PCIE RESET"); /* activate PERST_B */ - gpio_direction_output(MX6_ARM2_PCIE_RESET, 0); + gpio_direction_output(pdata->pcie_rst, 0); /* Add one reset to the pcie external device */ msleep(100); /* deactive PERST_B */ - gpio_direction_output(MX6_ARM2_PCIE_RESET, 1); + gpio_direction_output(pdata->pcie_rst, 1); } -static void __init add_pcie_port(void __iomem *base, void __iomem *dbi_base) +static void __init add_pcie_port(void __iomem *base, void __iomem *dbi_base, + struct imx_pcie_platform_data *pdata) { + struct clk *pcie_clk; + if (imx_pcie_link_up(dbi_base)) { struct imx_pcie_port *pp = &imx_pcie_port[num_pcie_ports++]; @@ -401,23 +641,51 @@ static void __init add_pcie_port(void __iomem *base, void __iomem *dbi_base) pp->dbi_base = dbi_base; spin_lock_init(&pp->conf_lock); memset(pp->res, 0, sizeof(pp->res)); - } else + } else { pr_info("IMX PCIe port: link down!\n"); + /* Release the clocks, and disable the power */ + + pcie_clk = clk_get(NULL, "pcie_clk"); + if (IS_ERR(pcie_clk)) + pr_err("no pcie clock.\n"); + + clk_disable(pcie_clk); + clk_put(pcie_clk); + + /* Disable PCIE power */ + gpio_request(pdata->pcie_pwr_en, "PCIE POWER_EN"); + + /* activate PCIE_PWR_EN */ + gpio_direction_output(pdata->pcie_pwr_en, 0); + + imx_pcie_clrset(iomuxc_gpr1_test_powerdown, 1 << 18, + IOMUXC_GPR1); + } } -static int __init imx_pcie_init(void) +static int __devinit imx_pcie_pltfm_probe(struct platform_device *pdev) { + struct resource *mem; + struct device *dev = &pdev->dev; + struct imx_pcie_platform_data *pdata = dev->platform_data; + + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(dev, "no mmio space\n"); + return -EINVAL; + } + base = ioremap_nocache(PCIE_ARB_END_ADDR - SZ_1M + 1, SZ_1M - SZ_16K); if (!base) { pr_err("error with ioremap in function %s\n", __func__); return -EIO; } - dbi_base = ioremap_nocache(PCIE_DBI_BASE_ADDR, SZ_16K); + dbi_base = devm_ioremap(dev, mem->start, resource_size(mem)); if (!dbi_base) { - pr_err("error with ioremap in function %s\n", __func__); - iounmap(base); - return -EIO; + dev_err(dev, "can't map %pR\n", mem); + return -ENOMEM; } /* FIXME the field name should be aligned to RM */ @@ -427,31 +695,103 @@ static int __init imx_pcie_init(void) imx_pcie_clrset(iomuxc_gpr12_device_type, PCI_EXP_TYPE_ROOT_PORT << 12, IOMUXC_GPR12); imx_pcie_clrset(iomuxc_gpr12_los_level, 9 << 4, IOMUXC_GPR12); - imx_pcie_clrset(iomuxc_gpr8_tx_deemph_gen1, 21 << 0, IOMUXC_GPR8); - imx_pcie_clrset(iomuxc_gpr8_tx_deemph_gen2_3p5db, 21 << 6, IOMUXC_GPR8); - imx_pcie_clrset(iomuxc_gpr8_tx_deemph_gen2_6db, 32 << 12, IOMUXC_GPR8); - imx_pcie_clrset(iomuxc_gpr8_tx_swing_full, 115 << 18, IOMUXC_GPR8); - imx_pcie_clrset(iomuxc_gpr8_tx_swing_low, 115 << 25, IOMUXC_GPR8); + + imx_pcie_clrset(iomuxc_gpr8_tx_deemph_gen1, 0 << 0, IOMUXC_GPR8); + imx_pcie_clrset(iomuxc_gpr8_tx_deemph_gen2_3p5db, 0 << 6, IOMUXC_GPR8); + imx_pcie_clrset(iomuxc_gpr8_tx_deemph_gen2_6db, 0 << 12, IOMUXC_GPR8); + imx_pcie_clrset(iomuxc_gpr8_tx_swing_full, 127 << 18, IOMUXC_GPR8); + imx_pcie_clrset(iomuxc_gpr8_tx_swing_low, 127 << 25, IOMUXC_GPR8); /* Enable the pwr, clks and so on */ - imx_pcie_enable_controller(); + imx_pcie_enable_controller(dev); imx_pcie_clrset(iomuxc_gpr1_pcie_ref_clk_en, 1 << 16, IOMUXC_GPR1); /* togle the external card's reset */ - card_reset() ; + card_reset(dev) ; + + usleep_range(3000, 4000); + imx_pcie_regions_setup(dbi_base); + usleep_range(3000, 4000); + + /* + * Force to GEN1 because of PCIE2USB storage stress tests + * would be failed when GEN2 is enabled + */ + writel(((readl(dbi_base + LNK_CAP) & 0xfffffff0) | 0x1), + dbi_base + LNK_CAP); /* start link up */ imx_pcie_clrset(iomuxc_gpr12_app_ltssm_enable, 1 << 10, IOMUXC_GPR12); /* add the pcie port */ - add_pcie_port(base, dbi_base); + add_pcie_port(base, dbi_base, pdata); - usleep_range(3000, 4000); - imx_pcie_regions_setup(dbi_base); - usleep_range(3000, 4000); pci_common_init(&imx_pci); return 0; } -device_initcall(imx_pcie_init); + +static int __devexit imx_pcie_pltfm_remove(struct platform_device *pdev) +{ + struct clk *pcie_clk; + struct device *dev = &pdev->dev; + struct imx_pcie_platform_data *pdata = dev->platform_data; + struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + /* Release clocks, and disable power */ + pcie_clk = clk_get(NULL, "pcie_clk"); + if (IS_ERR(pcie_clk)) + pr_err("no pcie clock.\n"); + + if (pcie_clk) { + clk_disable(pcie_clk); + clk_put(pcie_clk); + } + + /* Disable PCIE power */ + gpio_request(pdata->pcie_pwr_en, "PCIE POWER_EN"); + + /* activate PCIE_PWR_EN */ + gpio_direction_output(pdata->pcie_pwr_en, 0); + + imx_pcie_clrset(iomuxc_gpr1_test_powerdown, 1 << 18, IOMUXC_GPR1); + + iounmap(base); + iounmap(dbi_base); + release_mem_region(iomem->start, resource_size(iomem)); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver imx_pcie_pltfm_driver = { + .driver = { + .name = "imx-pcie", + .owner = THIS_MODULE, + }, + .probe = imx_pcie_pltfm_probe, + .remove = __devexit_p(imx_pcie_pltfm_remove), +}; + +/*****************************************************************************\ + * * + * Driver init/exit * + * * +\*****************************************************************************/ + +static int __init imx_pcie_drv_init(void) +{ + return platform_driver_register(&imx_pcie_pltfm_driver); +} + +static void __exit imx_pcie_drv_exit(void) +{ + platform_driver_unregister(&imx_pcie_pltfm_driver); +} + +module_init(imx_pcie_drv_init); +module_exit(imx_pcie_drv_exit); + +MODULE_DESCRIPTION("i.MX PCIE platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-mx6/system.c b/arch/arm/mach-mx6/system.c index 8475fba89b0d..9c494b20b878 100644 --- a/arch/arm/mach-mx6/system.c +++ b/arch/arm/mach-mx6/system.c @@ -55,8 +55,6 @@ static void __iomem *gpc_base = IO_ADDRESS(GPC_BASE_ADDR); volatile unsigned int num_cpu_idle; volatile unsigned int num_cpu_idle_lock = 0x0; - -extern void (*mx6_wait_in_iram)(void *ccm_base); extern void mx6_wait(void *num_cpu_idle_lock, void *num_cpu_idle); extern bool enable_wait_mode; @@ -167,20 +165,19 @@ extern int tick_broadcast_oneshot_active(void); void arch_idle(void) { if (enable_wait_mode) { - if (num_online_cpus() == num_present_cpus()) { #ifdef CONFIG_LOCAL_TIMERS - int cpu = smp_processor_id(); - if (!tick_broadcast_oneshot_active()) - return; + int cpu = smp_processor_id(); + if (!tick_broadcast_oneshot_active()) + return; - clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu); #endif - mxc_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF); - mx6_wait(&num_cpu_idle_lock, &num_cpu_idle); + *((char *)(&num_cpu_idle_lock) + smp_processor_id()) = 0x0; + mxc_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF); + mx6_wait((void *)&num_cpu_idle_lock, (void *)&num_cpu_idle); #ifdef CONFIG_LOCAL_TIMERS - clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu); #endif - } } else cpu_do_idle(); } diff --git a/arch/arm/plat-mxc/clock.c b/arch/arm/plat-mxc/clock.c index 4b9247904b96..f6e14a3cf2f5 100755 --- a/arch/arm/plat-mxc/clock.c +++ b/arch/arm/plat-mxc/clock.c @@ -67,7 +67,7 @@ static void __clk_disable(struct clk *clk) return; if (!clk->usecount) { - WARN(1, "clock enable/disable mismatch!\n"); + WARN(1, "clock enable/disable mismatch! clk %s\n", clk->name); return; } @@ -111,6 +111,11 @@ int clk_enable(struct clk *clk) if (clk == NULL || IS_ERR(clk)) return -EINVAL; + if (clk->flags & AHB_HIGH_SET_POINT) + lp_high_freq++; + else if (clk->flags & AHB_MED_SET_POINT) + lp_med_freq++; + if ((clk->flags & CPU_FREQ_TRIG_UPDATE) && (clk_get_usecount(clk) == 0)) { if (!(clk->flags & @@ -130,7 +135,6 @@ int clk_enable(struct clk *clk) set_high_bus_freq(1); } } - mutex_lock(&clocks_mutex); ret = __clk_enable(clk); mutex_unlock(&clocks_mutex); @@ -156,6 +160,11 @@ void clk_disable(struct clk *clk) if (clk == NULL || IS_ERR(clk)) return; + if (clk->flags & AHB_HIGH_SET_POINT) + lp_high_freq--; + else if (clk->flags & AHB_MED_SET_POINT) + lp_med_freq--; + mutex_lock(&clocks_mutex); __clk_disable(clk); mutex_unlock(&clocks_mutex); diff --git a/arch/arm/plat-mxc/cpufreq.c b/arch/arm/plat-mxc/cpufreq.c index 0774d52b315d..9dfdd39be28c 100755 --- a/arch/arm/plat-mxc/cpufreq.c +++ b/arch/arm/plat-mxc/cpufreq.c @@ -45,6 +45,11 @@ static u32 pre_suspend_rate; extern struct regulator *cpu_regulator; extern int dvfs_core_is_active; extern struct cpu_op *(*get_cpu_op)(int *op); +extern int low_bus_freq_mode; +extern int high_bus_freq_mode; +extern int set_low_bus_freq(void); +extern int set_high_bus_freq(int high_bus_speed); +extern int low_freq_bus_used(void); int set_cpu_freq(int freq) { @@ -66,6 +71,8 @@ int set_cpu_freq(int freq) /*Set the voltage for the GP domain. */ if (freq > org_cpu_rate) { + if (low_bus_freq_mode) + set_high_bus_freq(0); ret = regulator_set_voltage(cpu_regulator, gp_volt, gp_volt); if (ret < 0) { @@ -88,6 +95,8 @@ int set_cpu_freq(int freq) printk(KERN_DEBUG "COULD NOT SET GP VOLTAGE!!!!\n"); return ret; } + if (low_freq_bus_used() && !low_bus_freq_mode) + set_low_bus_freq(); } return ret; diff --git a/arch/arm/plat-mxc/devices/Kconfig b/arch/arm/plat-mxc/devices/Kconfig index 350845eb64f0..47d19921c6b5 100755 --- a/arch/arm/plat-mxc/devices/Kconfig +++ b/arch/arm/plat-mxc/devices/Kconfig @@ -172,3 +172,6 @@ config IMX_HAVE_PLATFORM_IMX_MIPI_CSI2 config IMX_HAVE_PLATFORM_IMX_VDOA bool + +config IMX_HAVE_PLATFORM_IMX_PCIE + bool diff --git a/arch/arm/plat-mxc/devices/Makefile b/arch/arm/plat-mxc/devices/Makefile index f2741caecfca..be2b0a674825 100755 --- a/arch/arm/plat-mxc/devices/Makefile +++ b/arch/arm/plat-mxc/devices/Makefile @@ -62,3 +62,4 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_ASRC) += platform-imx-asrc.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_MIPI_DSI) += platform-imx-mipi_dsi.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_MIPI_CSI2) += platform-imx-mipi_csi2.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_VDOA) += platform-imx-vdoa.o +obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_PCIE) += platform-imx-pcie.o diff --git a/arch/arm/plat-mxc/devices/platform-imx-dma.c b/arch/arm/plat-mxc/devices/platform-imx-dma.c index 9f99aa481526..65a71ac8b9e9 100755 --- a/arch/arm/plat-mxc/devices/platform-imx-dma.c +++ b/arch/arm/plat-mxc/devices/platform-imx-dma.c @@ -183,13 +183,15 @@ static struct sdma_script_start_addrs addr_imx6q_to1 = { .ap_2_ap_addr = 642, .uart_2_mcu_addr = 817, .mcu_2_app_addr = 747, - .per_2_per_addr = 6474, + .per_2_per_addr = 6331, .uartsh_2_mcu_addr = 1032, .mcu_2_shp_addr = 960, .app_2_mcu_addr = 683, .shp_2_mcu_addr = 891, .spdif_2_mcu_addr = 1100, .mcu_2_spdif_addr = 1134, + .mcu_2_ssish_addr = 6242, + .ssish_2_mcu_addr = 6678, }; #endif diff --git a/arch/arm/plat-mxc/devices/platform-imx-pcie.c b/arch/arm/plat-mxc/devices/platform-imx-pcie.c new file mode 100644 index 000000000000..cf3609365129 --- /dev/null +++ b/arch/arm/plat-mxc/devices/platform-imx-pcie.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <mach/hardware.h> +#include <mach/devices-common.h> + +#define imx_pcie_data_entry_single(soc, _id, _hwid, size) \ + { \ + .id = _id, \ + .iobase = soc ## _PCIE ## _hwid ## _BASE_ADDR, \ + .iosize = size, \ + .irq = soc ## _INT_PCIE ## _hwid, \ + } + +#define imx_pcie_data_entry(soc, _id, _hwid, _size) \ + [_id] = imx_pcie_data_entry_single(soc, _id, _hwid, _size) + +#ifdef CONFIG_SOC_IMX6Q +#define MX6Q_PCIE_BASE_ADDR (PCIE_ARB_END_ADDR - SZ_16K + 1) +#define MX6Q_INT_PCIE MXC_INT_PCIE_3 +const struct imx_pcie_data imx6q_pcie_data __initconst = + imx_pcie_data_entry_single(MX6Q, 0, , SZ_16K); +#endif + +struct platform_device *__init imx_add_pcie( + const struct imx_pcie_data *data, + const struct imx_pcie_platform_data *pdata) +{ + struct resource res[] = { + { + .start = data->iobase, + .end = data->iobase + data->iosize - 1, + .flags = IORESOURCE_MEM, + }, { + .start = data->irq, + .end = data->irq, + .flags = IORESOURCE_IRQ, + }, + }; + + return imx_add_platform_device("imx-pcie", -1, + res, ARRAY_SIZE(res), + pdata, sizeof(*pdata)); +} diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h index 9f87b009f129..6fce8c4b8255 100755 --- a/arch/arm/plat-mxc/include/mach/devices-common.h +++ b/arch/arm/plat-mxc/include/mach/devices-common.h @@ -642,3 +642,15 @@ struct imx_vdoa_data { }; struct platform_device *__init imx_add_vdoa( const struct imx_vdoa_data *data); + +#include <mach/pcie.h> +struct imx_pcie_data { + int id; + resource_size_t iobase; + resource_size_t iosize; + resource_size_t irq; +}; + +struct platform_device *__init imx_add_pcie( + const struct imx_pcie_data *data, + const struct imx_pcie_platform_data *pdata); diff --git a/arch/arm/plat-mxc/include/mach/pcie.h b/arch/arm/plat-mxc/include/mach/pcie.h new file mode 100644 index 000000000000..775f65107978 --- /dev/null +++ b/arch/arm/plat-mxc/include/mach/pcie.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __ASM_ARCH_IMX_PCIE_H +#define __ASM_ARCH_IMX_PCIE_H + +/** + * struct imx_pcie_platform_data - optional platform data for pcie on i.MX + * + * @pcie_pwr_en: used for enable/disable pcie power (-EINVAL if unused) + * @pcie_rst: used for reset pcie ep (-EINVAL if unused) + * @pcie_wake_up: used for wake up (-EINVAL if unused) + * @pcie_dis: used for disable pcie ep (-EINVAL if unused) + */ + +struct imx_pcie_platform_data { + unsigned int pcie_pwr_en; + unsigned int pcie_rst; + unsigned int pcie_wake_up; + unsigned int pcie_dis; +}; +#endif /* __ASM_ARCH_IMX_PCIE_H */ diff --git a/arch/arm/plat-mxc/include/mach/sdma.h b/arch/arm/plat-mxc/include/mach/sdma.h index 913e0432e40e..a145c56b649d 100644 --- a/arch/arm/plat-mxc/include/mach/sdma.h +++ b/arch/arm/plat-mxc/include/mach/sdma.h @@ -43,6 +43,8 @@ struct sdma_script_start_addrs { s32 dptc_dvfs_addr; s32 utra_addr; s32 ram_code_start_addr; + s32 mcu_2_ssish_addr; + s32 ssish_2_mcu_addr; }; /** diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 6f168fe568a8..b6c7211ccd52 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -576,6 +576,9 @@ static void sdma_get_pc(struct sdma_channel *sdmac, emi_2_per = sdma->script_addrs->mcu_2_app_addr; break; case IMX_DMATYPE_SSI_SP: + per_2_emi = sdma->script_addrs->ssish_2_mcu_addr; + emi_2_per = sdma->script_addrs->mcu_2_ssish_addr; + break; case IMX_DMATYPE_MMC: case IMX_DMATYPE_SDHC: case IMX_DMATYPE_CSPI_SP: @@ -1125,6 +1128,7 @@ static int sdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, sdmac->watermark_level = dmaengine_cfg->dst_maxburst; sdmac->word_size = dmaengine_cfg->dst_addr_width; } + sdmac->direction = dmaengine_cfg->direction; return sdma_config_channel(sdmac); default: return -ENOSYS; @@ -1155,7 +1159,7 @@ static void sdma_issue_pending(struct dma_chan *chan) */ } -#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34 +#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 37 static void sdma_add_scripts(struct sdma_engine *sdma, const struct sdma_script_start_addrs *addr) diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 4456b331e1d5..162e6c3eb64d 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -637,7 +637,7 @@ static int __init mxs_dma_probe(struct platform_device *pdev) goto err_ioremap; } - mxs_dma->clk = clk_get(&pdev->dev, NULL); + mxs_dma->clk = clk_get(NULL, "mxs-dma-apbh"); if (IS_ERR(mxs_dma->clk)) { ret = PTR_ERR(mxs_dma->clk); goto err_clk; diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 52798a111e16..4aefb00cc6cf 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -355,6 +355,19 @@ config RADIO_SI470X bool "Silicon Labs Si470x FM Radio Receiver support" depends on VIDEO_V4L2 +config I2C_SI4763 + tristate "Silicon Labs Si4763 AMFM Radio Transmitter support" + depends on I2C && VIDEO_V4L2 + ---help--- + Say Y here if you want support to Si4763 AMFM Radio Transmitter. + This device can transmit audio through AM or FM. + This module is the v4l2 radio + interface for the i2c driver of this device. + + To compile this driver as a module, choose M here: the + module will be called radio-si4763. + + source "drivers/media/radio/si470x/Kconfig" config USB_MR800 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index f484a6e04eb2..07cd9ef0e19a 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o +obj-$(CONFIG_I2C_SI4763) += si4763-i2c.o obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o obj-$(CONFIG_USB_DSBR) += dsbr100.o diff --git a/drivers/media/radio/si4763-i2c.c b/drivers/media/radio/si4763-i2c.c new file mode 100644 index 000000000000..c4990a3eee5d --- /dev/null +++ b/drivers/media/radio/si4763-i2c.c @@ -0,0 +1,933 @@ +/* + * Copyright (C) 2010-2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/regulator/consumer.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-common.h> + +#include "si4763-i2c.h" + +#define DEBUG 1 +/* module parameters */ +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0 - 2)"); + +MODULE_DESCRIPTION("I2C driver for Si4763 FM Radio Transmitter"); + +/* Radio Nr */ +static int radio_nr = -1; + + +/* radio_si4713_fops - file operations interface */ +static const struct v4l2_file_operations radio_si4763_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, +}; + + +#define DEFAULT_RDS_PI 0x00 +#define DEFAULT_RDS_PTY 0x00 +#define DEFAULT_RDS_PS_NAME "" +#define DEFAULT_RDS_RADIO_TEXT DEFAULT_RDS_PS_NAME +#define DEFAULT_RDS_DEVIATION 0x00C8 +#define DEFAULT_RDS_PS_REPEAT_COUNT 0x0003 +#define DEFAULT_LIMITER_RTIME 0x1392 +#define DEFAULT_LIMITER_DEV 0x102CA +#define DEFAULT_PILOT_FREQUENCY 0x4A38 +#define DEFAULT_PILOT_DEVIATION 0x1A5E +#define DEFAULT_ACOMP_ATIME 0x0000 +#define DEFAULT_ACOMP_RTIME 0xF4240L +#define DEFAULT_ACOMP_GAIN 0x0F +#define DEFAULT_ACOMP_THRESHOLD (-0x28) +#define DEFAULT_MUTE 0x01 +#define DEFAULT_POWER_LEVEL 88 +#define DEFAULT_FREQUENCY 8800 +#define DEFAULT_PREEMPHASIS FMPE_EU +#define DEFAULT_TUNE_RNL 0xFF + +#define to_si4763_device(sd) container_of(sd, struct si4763_device, sd) + +/* frequency domain transformation (using times 10 to avoid floats) */ +#define FREQ_RANGE_LOW 8750 +#define FREQ_RANGE_HIGH 10800 + +#define MAX_ARGS 7 + +#define RDS_BLOCK 8 +#define RDS_BLOCK_CLEAR 0x03 +#define RDS_BLOCK_LOAD 0x04 +#define RDS_RADIOTEXT_2A 0x20 +#define RDS_RADIOTEXT_BLK_SIZE 4 +#define RDS_RADIOTEXT_INDEX_MAX 0x0F +#define RDS_CARRIAGE_RETURN 0x0D + +#define rds_ps_nblocks(len) ((len / RDS_BLOCK) + (len % RDS_BLOCK ? 1 : 0)) + +#define get_status_bit(p, b, m) (((p) & (m)) >> (b)) +#define set_bits(p, v, b, m) (((p) & ~(m)) | ((v) << (b))) + +#define ATTACK_TIME_UNIT 500 + +#define POWER_OFF 0x00 +#define POWER_ON 0x01 + +#define msb(x) ((u8)((u16) x >> 8)) +#define lsb(x) ((u8)((u16) x & 0x00FF)) +#define compose_u16(msb, lsb) (((u16)msb << 8) | lsb) +#define compose_u32(msb, lsb) (((u32)msb << 16) | lsb) +#define check_command_failed(status) (!(status & SI4713_CTS) || \ + (status & SI4713_ERR)) +/* mute definition */ +#define set_mute(p) ((p & 1) | ((p & 1) << 1)); +#define get_mute(p) (p & 0x01) + +#ifdef DEBUG +#define DBG_BUFFER(device, message, buffer, size) \ + { \ + int i; \ + char str[(size)*5]; \ + for (i = 0; i < size; i++) \ + sprintf(str + i * 5, " 0x%02x", buffer[i]); \ + v4l2_dbg(2, debug, device, "%s:%s\n", message, str); \ + } +#else +#define DBG_BUFFER(device, message, buffer, size) +#endif + + +/* + * si4713_send_command - sends a command to si4713 and waits its response + * @sdev: si4713_device structure for the device we are communicating + * @command: command id + * @args: command arguments we are sending (up to 7) + * @argn: actual size of @args + * @response: buffer to place the expected response from the device (up to 15) + * @respn: actual size of @response + * @usecs: amount of time to wait before reading the response (in usecs) + */ +static int si4713_send_command(struct si4763_device *sdev, const u8 command, + const u8 args[], const int argn, + u8 response[], const int respn, const int usecs) +{ + struct i2c_client *client = sdev->client; + u8 data1[MAX_ARGS + 1]; + int err; + int i; + if (!client->adapter) + return -ENODEV; + + /* First send the command and its arguments */ + data1[0] = command; + memcpy(data1 + 1, args, argn); + DBG_BUFFER(&sdev->sd, "Parameters", data1, argn + 1); + + err = i2c_master_send(client, data1, argn + 1); + if (err != argn + 1) { + v4l2_err(&sdev->sd, "Error while sending command 0x%02x\n", + command); + return (err > 0) ? -EIO : err; + } + + /* Wait response from interrupt */ +/* + if (!wait_for_completion_timeout(&sdev->work, + usecs_to_jiffies(usecs) + 1)) + v4l2_warn(&sdev->sd, + "(%s) Device took too much time to answer.\n", + __func__); + +*/ + + + /* Then get the response */ + i = 0; + err = i2c_master_recv(client, response, respn); + while (i++ < usecs && ((response[0] & 0x80) != 0x80)) { + err = i2c_master_recv(client, response, respn); + udelay(1); + } + + + if (err != respn) { + v4l2_err(&sdev->sd, + "Error while reading response for command 0x%02x\n", + command); + return (err > 0) ? -EIO : err; + } + + DBG_BUFFER(&sdev->sd, "Response", response, respn); + if (check_command_failed(response[0])) + return -EBUSY; + + return 0; +} + + +/* + * si4713_write_property - modifies a si4713 property + * @sdev: si4713_device structure for the device we are communicating + * @prop: property identification number + * @val: new value for that property + */ +static int si4713_write_property(struct si4763_device *sdev, u16 prop, u16 val) +{ + int rval; + u8 resp[SI4713_SET_PROP_NRESP]; + /* + * .First byte = 0 + * .Second byte = property's MSB + * .Third byte = property's LSB + * .Fourth byte = value's MSB + * .Fifth byte = value's LSB + */ + const u8 args[SI4713_SET_PROP_NARGS] = { + 0x00, + msb(prop), + lsb(prop), + msb(val), + lsb(val), + }; + + rval = si4713_send_command(sdev, SI4713_CMD_SET_PROPERTY, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (rval < 0) + return rval; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: property=0x%02x value=0x%02x status=0x%02x\n", + __func__, prop, val, resp[0]); + + /* + * As there is no command response for SET_PROPERTY, + * wait Tcomp time to finish before proceed, in order + * to have property properly set. + */ + msleep(TIMEOUT_SET_PROPERTY); + + return rval; +} + +static int si4713_enable_digitalout(struct si4763_device *sdev) +{ + int rval; + u8 resp[SI4713_SET_PROP_NRESP]; + /* + * .First byte = 0 + * .Second byte = property's MSB + * .Third byte = property's LSB + * .Fourth byte = value's MSB + * .Fifth byte = value's LSB + */ + const u8 args[SI4713_EN_DIGITALOUT_NARGS] = { + 10, + 10, + 12, + 0, + }; + + rval = si4713_send_command(sdev, SI4713_CMD_EN_DIGITALOUT, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (rval < 0) + return rval; + + + /* + * As there is no command response for SET_PROPERTY, + * wait Tcomp time to finish before proceed, in order + * to have property properly set. + */ + msleep(TIMEOUT_SET_PROPERTY); + + return rval; +} + +static int si4713_get_digitalout(struct si4763_device *sdev, u32 *response) +{ + int rval; + u8 resp[5]; + u16 temp1, temp2; + + /* + * .First byte = 0 + * .Second byte = property's MSB + * .Third byte = property's LSB + * .Fourth byte = value's MSB + * .Fifth byte = value's LSB + */ + const u8 args[SI4713_EN_DIGITALOUT_NARGS] = { + 0, + 0, + 0, + 0, + }; + + rval = si4713_send_command(sdev, SI4713_CMD_EN_DIGITALOUT, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (rval < 0) + return rval; + + temp1 = compose_u16(resp[1], resp[2]); + temp2 = compose_u16(resp[3], resp[4]); + *response = compose_u32(temp1, temp2); + /* + * As there is no command response for SET_PROPERTY, + * wait Tcomp time to finish before proceed, in order + * to have property properly set. + */ + msleep(TIMEOUT_SET_PROPERTY); + + return rval; +} + + +/* + * si4713_powerup - Powers the device up + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_powerup(struct si4763_device *sdev) +{ + int err; + u8 resp[SI4760_PWUP_NRESP]; + const u8 args[SI4760_PWUP_NARGS] = { + SI4760_PWUP_ARG1, + SI4760_PWUP_ARG2, + SI4760_PWUP_ARG3, + SI4760_PWUP_ARG4, + SI4760_PWUP_ARG5, + }; + + + if (sdev->power_state) + return 0; + err = si4713_send_command(sdev, SI4760_CMD_POWER_UP, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + TIMEOUT_POWER_UP); + + if (!err) { + v4l2_dbg(1, debug, &sdev->sd, "Powerup response: 0x%02x\n", + resp[0]); + v4l2_dbg(1, debug, &sdev->sd, "Device in power up mode\n"); + sdev->power_state = POWER_ON; + + } else { + /* if (gpio_is_valid(sdev->gpio_reset)) + gpio_set_value(sdev->gpio_reset, 0); + err = regulator_bulk_disable(ARRAY_SIZE(sdev->supplies), + sdev->supplies); + */ if (err) + v4l2_err(&sdev->sd, + "Failed to disable supplies: %d\n", err); + } + + return err; +} + +/* + * si4713_powerdown - Powers the device down + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_powerdown(struct si4763_device *sdev) +{ + int err; + u8 resp[SI4760_PWDN_NRESP]; + + + err = si4713_send_command(sdev, SI4760_CMD_POWER_DOWN, + NULL, 0, + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (!err) { + v4l2_dbg(1, debug, &sdev->sd, "Power down response: 0x%02x\n", + resp[0]); + v4l2_dbg(1, debug, &sdev->sd, "Device in reset mode\n"); + if (err) + v4l2_err(&sdev->sd, + "Failed to disable supplies: %d\n", err); + sdev->power_state = POWER_OFF; + } + + return err; +} + +/* + * si4713_checkrev - Checks if we are treating a device with the correct rev. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_checkrev(struct si4763_device *sdev) +{ + struct i2c_client *client = sdev->client; + int rval; + u8 resp[SI4760_GETREV_NRESP]; + + mutex_lock(&sdev->mutex); + + rval = si4713_send_command(sdev, SI4760_CMD_GET_REV, + NULL, 0, + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (rval < 0) + goto unlock; + + if (resp[1] == SI4760_PRODUCT_NUMBER) { + v4l2_info(&sdev->sd, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + } else { + v4l2_err(&sdev->sd, "Invalid product number\n"); + rval = -EINVAL; + } + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +/* + * si4713_wait_stc - Waits STC interrupt and clears status bits. Usefull + * for TX_TUNE_POWER, TX_TUNE_FREQ and TX_TUNE_MEAS + * @sdev: si4713_device structure for the device we are communicating + * @usecs: timeout to wait for STC interrupt signal + */ +static int si4713_wait_stc(struct si4763_device *sdev, const int usecs) +{ + int err; + u8 resp[SI4713_GET_STATUS_NRESP]; + + /* Wait response from STC interrupt */ + if (!wait_for_completion_timeout(&sdev->work, + usecs_to_jiffies(usecs) + 1)) + v4l2_warn(&sdev->sd, + "%s: device took too much time to answer (%d usec).\n", + __func__, usecs); + + /* Clear status bits */ + err = si4713_send_command(sdev, SI4713_CMD_GET_INT_STATUS, + NULL, 0, + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (err < 0) + goto exit; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: status bits: 0x%02x\n", __func__, resp[0]); + + if (!(resp[0] & SI4713_STC_INT)) + err = -EIO; + +exit: + return err; +} + +/* + * si4713_tx_tune_freq - Sets the state of the RF carrier and sets the tuning + * frequency between 76 and 108 MHz in 10 kHz units and + * steps of 50 kHz. + * @sdev: si4713_device structure for the device we are communicating + * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) + */ +static int si4713_tx_tune_freq(struct si4763_device *sdev, u16 frequency) +{ + int err; + u8 val[SI4713_TXFREQ_NRESP]; + /* + * .First byte = 0 + * .Second byte = frequency's MSB + * .Third byte = frequency's LSB + */ + const u8 args[SI4713_TXFREQ_NARGS] = { + 0x00, + msb(frequency), + lsb(frequency), + 0x00, + 0x00, + }; + + err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_FREQ, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + return 0; +} + + +/* + * si4713_tx_tune_measure - Enters receive mode and measures the received noise + * level in units of dBuV on the selected frequency. + * The Frequency must be between 76 and 108 MHz in 10 kHz + * units and steps of 50 kHz. The command also sets the + * antenna tuning capacitance. A value of 0 means + * autotuning, and a value of 1 to 191 indicates manual + * override. + * @sdev: si4713_device structure for the device we are communicating + * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) + * @antcap: value of antenna tuning capacitor (0 - 191) + */ +static int si4713_tx_tune_measure(struct si4763_device *sdev, u16 frequency, + u8 antcap) +{ + int err; + u8 val[SI4713_TXMEA_NRESP]; + /* + * .First byte = 0 + * .Second byte = frequency's MSB + * .Third byte = frequency's LSB + * .Fourth byte = antcap + */ + const u8 args[SI4713_TXMEA_NARGS] = { + 0x00, + msb(frequency), + lsb(frequency), + antcap, + }; + + sdev->tune_rnl = DEFAULT_TUNE_RNL; + + if (antcap > SI4713_MAX_ANTCAP) + return -EDOM; + + err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_MEASURE, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: frequency=0x%02x antcap=0x%02x status=0x%02x\n", + __func__, frequency, antcap, val[0]); + + return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE); +} + +static int si4763_fm_rsq_status(struct si4763_device *sdev, + u16 *frequency) +{ + int err; + u8 val[SI4763_FM_RSQ_STATUS_NRESP]; + /* + * .First byte = intack bit + */ + const u8 args[SI4763_FM_RSQ_STATUS_NARGS] = { + 0x00, + }; + + err = si4713_send_command(sdev, SI4763_CMD_FM_RSQ_STATUS, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + *frequency = compose_u16(val[3], val[4]); + return err; +} + + +static int si4713_set_power_state(struct si4763_device *sdev, u8 value) +{ + int rval; + + mutex_lock(&sdev->mutex); + + if (value) + rval = si4713_powerup(sdev); + else + rval = si4713_powerdown(sdev); + + mutex_unlock(&sdev->mutex); + return rval; +} + + +/* + * si4713_initialize - Sets the device up with default configuration. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_initialize(struct si4763_device *sdev) +{ + int rval; + + rval = si4713_set_power_state(sdev, POWER_OFF); + if (rval < 0) + goto exit; + + rval = si4713_set_power_state(sdev, POWER_ON); + if (rval < 0) + goto exit; + + rval = si4713_checkrev(sdev); + if (rval < 0) + goto exit; + rval = si4713_set_power_state(sdev, POWER_OFF); + if (rval < 0) + goto exit; + +exit: + return rval; +} + +static int si4763_setup(struct si4763_device *sdev) +{ + int rval; + u32 temp; + rval = si4713_write_property(sdev, 0x0202, 0xBB80); + if (rval < 0) + goto exit; + + rval = si4713_write_property(sdev, 0x0203, 0x0400); + if (rval < 0) + goto exit; + rval = si4713_tx_tune_freq(sdev, FREQ_RANGE_LOW); + if (rval < 0) + goto exit; + + rval = si4713_enable_digitalout(sdev); + if (rval < 0) + goto exit; + + rval = si4713_get_digitalout(sdev, &temp); + if (rval < 0) + goto exit; + +exit: + return rval; +} + +/* + * Video4Linux Subdev Interface + */ + +/* si4713_ioctl - deal with private ioctls (only rnl for now) */ +long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct si4763_device *sdev = to_si4763_device(sd); + struct si4713_rnl *rnl = arg; + int frequency = 0; + int rval = 0; + + if (!arg) + return -EINVAL; + + mutex_lock(&sdev->mutex); + switch (cmd) { + case SI4713_IOC_MEASURE_RNL: + + if (sdev->power_state) { + /* Set desired measurement frequency */ + rval = si4713_tx_tune_measure(sdev, frequency, 0); + if (rval < 0) + goto unlock; + /* get results from tune status */ + } + rnl->rnl = sdev->tune_rnl; + break; + + default: + /* nothing */ + rval = -ENOIOCTLCMD; + } + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + + +/* si4763_g_frequency - get tuner or modulator radio frequency */ +static int si4763_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + + struct si4763_device *sdev = video_drvdata(file); + int rval = 0; + + f->type = V4L2_TUNER_RADIO; + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + u16 freq; + + rval = si4763_fm_rsq_status(sdev, &freq); + if (rval < 0) + goto unlock; + + sdev->frequency = freq; + } + + f->frequency = sdev->frequency; + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +/* si4713_s_frequency - set tuner or modulator radio frequency */ +static int si4763_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) + +{ + struct si4763_device *sdev = video_drvdata(file); + int rval = 0; + u16 frequency = f->frequency; + + /* Check frequency range */ + if (frequency < FREQ_RANGE_LOW || frequency > FREQ_RANGE_HIGH) + return -EDOM; + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + rval = si4713_tx_tune_freq(sdev, frequency); + if (rval < 0) + goto unlock; + frequency = rval; + rval = 0; + } + sdev->frequency = frequency; + f->frequency = frequency; + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + + +/************************************************************************** + * File Operations Interface + **************************************************************************/ + +/* + * si4763_fops_open - file open + */ +int si4763_fops_open(struct file *file) +{ + struct si4763_device *radio = video_drvdata(file); + int retval = 0; + + if (radio->users != 0) + return -ENODEV; + /* start radio */ + si4713_set_power_state(radio, POWER_ON); + si4763_setup(radio); + radio->users = 1; + + return retval; +} + + +/* + * si470x_fops_release - file release + */ +int si4763_fops_release(struct file *file) +{ + struct si4763_device *radio = video_drvdata(file); + int retval = 0; + + /* safety check */ + if (!radio) + return -ENODEV; + + + /* stop radio */ + si4713_set_power_state(radio, POWER_OFF); + radio->users = 0; + return retval; +} + + +/* + * si4763_fops - file operations interface + * video_ioctl2 is part of the v4l2 implementations. Change this pointer to the + * ioctl function you want to implement, in case you don't want to be part of + * v4l2. + */ +static const struct v4l2_file_operations si4763_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, + .open = si4763_fops_open, + .release = si4763_fops_release, +}; + + +/* + * si4763_ioctl_ops - video device ioctl operations + */ +static const struct v4l2_ioctl_ops si4763_ioctl_ops = { + .vidioc_g_frequency = si4763_g_frequency, + .vidioc_s_frequency = si4763_s_frequency, +}; + + +/* + * si4763_viddev_template - video device interface + */ +struct video_device si4763_viddev_template = { + .fops = &si4763_fops, + .name = "si4763_i2c", + .release = video_device_release, + .ioctl_ops = &si4763_ioctl_ops, +}; + + +/* + * I2C driver interface + */ +/* si4713_probe - probe for the device */ +static int si4713_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct si4763_device *sdev; + int rval; + + sdev = kzalloc(sizeof *sdev, GFP_KERNEL); + if (!sdev) { + dev_err(&client->dev, "Failed to alloc video device.\n"); + rval = -ENOMEM; + goto exit; + } + sdev->client = client; + sdev->users = 0; + /* video device allocation and initialization */ + sdev->videodev = video_device_alloc(); + if (!sdev->videodev) { + rval = -ENOMEM; + goto free_video; + } + + + mutex_init(&sdev->mutex); + memcpy(sdev->videodev, &si4763_viddev_template, + sizeof(si4763_viddev_template)); + video_set_drvdata(sdev->videodev, sdev); +/* + if (client->irq) { + rval = request_irq(client->irq, + si4713_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED, + client->name, sdev); + if (rval < 0) { + v4l2_err(&sdev->sd, "Could not request IRQ\n"); + goto put_reg; + } + v4l2_dbg(1, debug, &sdev->sd, "IRQ requested.\n"); + } else { + v4l2_warn(&sdev->sd, "IRQ not configured. Using timeouts.\n"); + } +*/ + + rval = si4713_initialize(sdev); + if (rval < 0) { + v4l2_err(&sdev->sd, "Failed to probe device information.\n"); + goto free_sdev; + } + + /* register video device */ + rval = video_register_device(sdev->videodev, VFL_TYPE_RADIO, + radio_nr); + if (rval) { + dev_warn(&client->dev, "Could not register video device\n"); + goto free_video; + } + i2c_set_clientdata(client, sdev); + return 0; + +free_video: + video_device_release(sdev->videodev); + +free_sdev: + kfree(sdev); +exit: + return rval; +} + +/* si4713_remove - remove the device */ +static int si4713_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct si4763_device *sdev = to_si4763_device(sd); + + if (sdev->power_state) + si4713_set_power_state(sdev, POWER_DOWN); + + if (client->irq > 0) + free_irq(client->irq, sdev); + + v4l2_device_unregister_subdev(sd); + kfree(sdev); + + return 0; +} + +/* si4713_i2c_driver - i2c driver interface */ +static const struct i2c_device_id si4713_id[] = { + { "si4763_i2c" , 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, si4713_id); + +static struct i2c_driver si4713_i2c_driver = { + .driver = { + .name = "si4763_i2c", + }, + .probe = si4713_probe, + .remove = si4713_remove, + .id_table = si4713_id, +}; + +/* Module Interface */ +static int __init si4713_module_init(void) +{ + return i2c_add_driver(&si4713_i2c_driver); +} + +static void __exit si4713_module_exit(void) +{ + i2c_del_driver(&si4713_i2c_driver); +} + +module_init(si4713_module_init); +module_exit(si4713_module_exit); + +/* Module information */ +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("I2C si4763"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/radio/si4763-i2c.h b/drivers/media/radio/si4763-i2c.h new file mode 100644 index 000000000000..d97eb35863da --- /dev/null +++ b/drivers/media/radio/si4763-i2c.h @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2008-2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +#ifndef SI4763_I2C_H +#define SI4763_I2C_H + +#include <media/v4l2-subdev.h> +#include <media/si4763.h> + +#define SI4760_PRODUCT_NUMBER 0x05 + +/* Command Timeouts */ +#define DEFAULT_TIMEOUT 500 +#define TIMEOUT_SET_PROPERTY 20 +#define TIMEOUT_TX_TUNE_POWER 30000 +#define TIMEOUT_TX_TUNE 110000 +#define TIMEOUT_POWER_UP 200000 + +/* + * Command and its arguments definitions + */ +#define SI4713_PWUP_CTSIEN (1<<7) +#define SI4713_PWUP_GPO2OEN (1<<6) +#define SI4713_PWUP_PATCH (1<<5) +#define SI4713_PWUP_XOSCEN (1<<4) +#define SI4713_PWUP_FUNC_TX 0x02 +#define SI4713_PWUP_FUNC_PATCH 0x0F +#define SI4713_PWUP_OPMOD_ANALOG 0x50 +#define SI4713_PWUP_OPMOD_DIGITAL 0x0F +#define SI4760_PWUP_NARGS 5 +#define SI4760_PWUP_NRESP 1 +#define SI4760_PWDOWN_NARGS 1 + +#define SI4760_CMD_POWER_UP 0x01 + +#define SI4760_PWUP_ARG1 0xF7 +#define SI4760_PWUP_ARG2 0x28 +#define SI4760_PWUP_ARG3 0x07 +#define SI4760_PWUP_ARG4 0x11 +#define SI4760_PWUP_ARG5 0x11 + +#define SI4713_CMD_EN_DIGITALOUT 0x18 +#define SI4713_EN_DIGITALOUT_NARGS 4 + + +#define SI4760_GETREV_NRESP 9 +#define SI4760_CMD_GET_REV 0x02 + +#define SI4760_PWDN_NRESP 1 +#define SI4760_CMD_POWER_DOWN 0x11 + +#define SI4763_CMD_FM_RSQ_STATUS 0x32 +#define SI4763_FM_RSQ_STATUS_NARGS 1 +#define SI4763_FM_RSQ_STATUS_NRESP 5 + +#define SI4713_SET_PROP_NARGS 5 +#define SI4713_SET_PROP_NRESP 1 +#define SI4713_CMD_SET_PROPERTY 0x13 + +#define SI4713_GET_PROP_NARGS 3 +#define SI4713_GET_PROP_NRESP 4 +#define SI4713_CMD_GET_PROPERTY 0x13 + +#define SI4713_GET_STATUS_NRESP 1 +#define SI4713_CMD_GET_INT_STATUS 0x14 + +#define SI4713_CMD_PATCH_ARGS 0x15 +#define SI4713_CMD_PATCH_DATA 0x16 + +#define SI4713_MAX_FREQ 10800 +#define SI4713_MIN_FREQ 7600 +#define SI4713_TXFREQ_NARGS 5 +#define SI4713_TXFREQ_NRESP 1 +#define SI4713_CMD_TX_TUNE_FREQ 0x30 + +#define SI4713_MAX_POWER 120 +#define SI4713_MIN_POWER 88 +#define SI4713_MAX_ANTCAP 191 +#define SI4713_MIN_ANTCAP 0 +#define SI4713_TXPWR_NARGS 4 +#define SI4713_TXPWR_NRESP 1 +#define SI4713_CMD_TX_TUNE_POWER 0x31 + +#define SI4713_TXMEA_NARGS 4 +#define SI4713_TXMEA_NRESP 1 +#define SI4713_CMD_TX_TUNE_MEASURE 0x32 + +#define SI4713_INTACK_MASK 0x01 +#define SI4713_TXSTATUS_NARGS 1 +#define SI4713_TXSTATUS_NRESP 8 +#define SI4713_CMD_TX_TUNE_STATUS 0x33 + +#define SI4713_OVERMOD_BIT (1 << 2) +#define SI4713_IALH_BIT (1 << 1) +#define SI4713_IALL_BIT (1 << 0) +#define SI4713_ASQSTATUS_NARGS 1 +#define SI4713_ASQSTATUS_NRESP 5 +#define SI4713_CMD_TX_ASQ_STATUS 0x34 + +#define SI4713_RDSBUFF_MODE_MASK 0x87 +#define SI4713_RDSBUFF_NARGS 7 +#define SI4713_RDSBUFF_NRESP 6 +#define SI4713_CMD_TX_RDS_BUFF 0x35 + +#define SI4713_RDSPS_PSID_MASK 0x1F +#define SI4713_RDSPS_NARGS 5 +#define SI4713_RDSPS_NRESP 1 +#define SI4713_CMD_TX_RDS_PS 0x36 + +#define SI4713_CMD_GPO_CTL 0x80 +#define SI4713_CMD_GPO_SET 0x81 + +/* + * Bits from status response + */ +#define SI4713_CTS (1<<7) +#define SI4713_ERR (1<<6) +#define SI4713_RDS_INT (1<<2) +#define SI4713_ASQ_INT (1<<1) +#define SI4713_STC_INT (1<<0) + +/* + * Property definitions + */ +#define SI4713_GPO_IEN 0x0001 +#define SI4713_DIG_INPUT_FORMAT 0x0101 +#define SI4713_DIG_INPUT_SAMPLE_RATE 0x0103 +#define SI4713_REFCLK_FREQ 0x0201 +#define SI4713_REFCLK_PRESCALE 0x0202 +#define SI4713_TX_COMPONENT_ENABLE 0x2100 +#define SI4713_TX_AUDIO_DEVIATION 0x2101 +#define SI4713_TX_PILOT_DEVIATION 0x2102 +#define SI4713_TX_RDS_DEVIATION 0x2103 +#define SI4713_TX_LINE_INPUT_LEVEL 0x2104 +#define SI4713_TX_LINE_INPUT_MUTE 0x2105 +#define SI4713_TX_PREEMPHASIS 0x2106 +#define SI4713_TX_PILOT_FREQUENCY 0x2107 +#define SI4713_TX_ACOMP_ENABLE 0x2200 +#define SI4713_TX_ACOMP_THRESHOLD 0x2201 +#define SI4713_TX_ACOMP_ATTACK_TIME 0x2202 +#define SI4713_TX_ACOMP_RELEASE_TIME 0x2203 +#define SI4713_TX_ACOMP_GAIN 0x2204 +#define SI4713_TX_LIMITER_RELEASE_TIME 0x2205 +#define SI4713_TX_ASQ_INTERRUPT_SOURCE 0x2300 +#define SI4713_TX_ASQ_LEVEL_LOW 0x2301 +#define SI4713_TX_ASQ_DURATION_LOW 0x2302 +#define SI4713_TX_ASQ_LEVEL_HIGH 0x2303 +#define SI4713_TX_ASQ_DURATION_HIGH 0x2304 +#define SI4713_TX_RDS_INTERRUPT_SOURCE 0x2C00 +#define SI4713_TX_RDS_PI 0x2C01 +#define SI4713_TX_RDS_PS_MIX 0x2C02 +#define SI4713_TX_RDS_PS_MISC 0x2C03 +#define SI4713_TX_RDS_PS_REPEAT_COUNT 0x2C04 +#define SI4713_TX_RDS_PS_MESSAGE_COUNT 0x2C05 +#define SI4713_TX_RDS_PS_AF 0x2C06 +#define SI4713_TX_RDS_FIFO_SIZE 0x2C07 + +#define PREEMPHASIS_USA 75 +#define PREEMPHASIS_EU 50 +#define PREEMPHASIS_DISABLED 0 +#define FMPE_USA 0x00 +#define FMPE_EU 0x01 +#define FMPE_DISABLED 0x02 + +#define POWER_UP 0x01 +#define POWER_DOWN 0x00 + +struct rds_info { + u32 pi; +#define MAX_RDS_PTY 31 + u32 pty; +#define MAX_RDS_DEVIATION 90000 + u32 deviation; +/* + * PSNAME is known to be defined as 8 character sized (RDS Spec). + * However, there is receivers which scroll PSNAME 8xN sized. + */ +#define MAX_RDS_PS_NAME 96 + u8 ps_name[MAX_RDS_PS_NAME + 1]; +/* + * MAX_RDS_RADIO_TEXT is known to be defined as 32 (2A group) or 64 (2B group) + * character sized (RDS Spec). + * However, there is receivers which scroll them as well. + */ +#define MAX_RDS_RADIO_TEXT 384 + u8 radio_text[MAX_RDS_RADIO_TEXT + 1]; + u32 enabled; +}; + +struct limiter_info { +#define MAX_LIMITER_RELEASE_TIME 102390 + u32 release_time; +#define MAX_LIMITER_DEVIATION 90000 + u32 deviation; + u32 enabled; +}; + +struct pilot_info { +#define MAX_PILOT_DEVIATION 90000 + u32 deviation; +#define MAX_PILOT_FREQUENCY 19000 + u32 frequency; + u32 enabled; +}; + +struct acomp_info { +#define MAX_ACOMP_RELEASE_TIME 1000000 + u32 release_time; +#define MAX_ACOMP_ATTACK_TIME 5000 + u32 attack_time; +#define MAX_ACOMP_THRESHOLD 0 +#define MIN_ACOMP_THRESHOLD (-40) + s32 threshold; +#define MAX_ACOMP_GAIN 20 + u32 gain; + u32 enabled; +}; + +#define SI4763_NUM_SUPPLIES 2 + +/* + * si4763_device - private data + */ +struct si4763_device { + /* v4l2_subdev and i2c reference (v4l2_subdev priv data) */ + struct v4l2_subdev sd; + /* private data structures */ + struct mutex mutex; + struct completion work; + struct rds_info rds_info; + struct limiter_info limiter_info; + struct pilot_info pilot_info; + struct acomp_info acomp_info; + struct regulator_bulk_data supplies[SI4763_NUM_SUPPLIES]; + int gpio_reset; + u32 frequency; + u32 preemphasis; + u32 mute; + u32 power_level; + u32 power_state; + u32 antenna_capacitor; + u32 stereo; + u32 tune_rnl; + u8 users; + struct i2c_client *client; + struct video_device *videodev; +}; +#endif /* ifndef SI4763_I2C_H */ diff --git a/drivers/media/video/mxc/output/mxc_vout.c b/drivers/media/video/mxc/output/mxc_vout.c index 43b53678d5ca..7542ca5f2062 100644 --- a/drivers/media/video/mxc/output/mxc_vout.c +++ b/drivers/media/video/mxc/output/mxc_vout.c @@ -30,6 +30,22 @@ #define MAX_FB_NUM 6 #define FB_BUFS 3 +#define VALID_HEIGHT_1080P (1080) +#define FRAME_HEIGHT_1080P (1088) +#define FRAME_WIDTH_1080P (1920) +#define MAX_INTERLACED_WIDTH (1024) +#define CHECK_TILED_1080P_DISPLAY(vout) \ + (((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) && \ + ((vout)->task.input.width == FRAME_WIDTH_1080P) && \ + ((vout)->task.output.crop.w == FRAME_WIDTH_1080P) && \ + ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \ + ((vout)->task.output.crop.h == VALID_HEIGHT_1080P)) +#define CHECK_TILED_1080P_STREAM(vout) \ + (((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) && \ + ((vout)->task.input.width == FRAME_WIDTH_1080P) && \ + ((vout)->task.input.crop.w == FRAME_WIDTH_1080P) && \ + ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \ + ((vout)->task.input.crop.h == FRAME_HEIGHT_1080P)) struct mxc_vout_fb { char *name; @@ -62,7 +78,14 @@ struct mxc_vout_output { bool fmt_init; bool bypass_pp; + bool is_vdoaipu_task; struct ipu_task task; + struct ipu_task vdoa_task; + struct vdoa_mem { + void *vaddr; + dma_addr_t paddr; + size_t size; + } vdoa_dma; bool timer_stop; struct timer_list timer; @@ -97,7 +120,7 @@ static int video_nr = 16; /* Module parameters */ module_param(video_nr, int, S_IRUGO); MODULE_PARM_DESC(video_nr, "video device numbers"); -module_param(debug, bool, S_IRUGO); +module_param(debug, int, 0600); MODULE_PARM_DESC(debug, "Debug level (0-1)"); const static struct v4l2_fmtdesc mxc_formats[] = { @@ -145,6 +168,14 @@ const static struct v4l2_fmtdesc mxc_formats[] = { .description = "YUV420", .pixelformat = V4L2_PIX_FMT_YUV420, }, + { + .description = "TILED NV12P", + .pixelformat = IPU_PIX_FMT_TILED_NV12, + }, + { + .description = "TILED NV12F", + .pixelformat = IPU_PIX_FMT_TILED_NV12F, + }, }; #define NUM_MXC_VOUT_FORMATS (ARRAY_SIZE(mxc_formats)) @@ -158,6 +189,24 @@ static struct mxc_vout_fb g_fb_setting[MAX_FB_NUM]; static int config_disp_output(struct mxc_vout_output *vout); static void release_disp_output(struct mxc_vout_output *vout); +static unsigned int get_frame_size(struct mxc_vout_output *vout) +{ + unsigned int size; + + if (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) + size = TILED_NV12_FRAME_SIZE(vout->task.input.width, + vout->task.input.height); + else if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) { + size = TILED_NV12_FRAME_SIZE(vout->task.input.width, + vout->task.input.height/2); + size *= 2; + } else + size = vout->task.input.width * vout->task.input.height * + fmt_to_bpp(vout->task.input.format)/8; + + return size; +} + static ipu_channel_t get_ipu_channel(struct fb_info *fbi) { ipu_channel_t ipu_ch = CHAN_NONE; @@ -315,6 +364,9 @@ static bool deinterlace_3_field(struct mxc_vout_output *vout) static bool is_pp_bypass(struct mxc_vout_output *vout) { + if ((IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) || + (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format)) + return false; if ((vout->task.input.width == vout->task.output.width) && (vout->task.input.height == vout->task.output.height) && (vout->task.input.crop.w == vout->task.output.crop.w) && @@ -374,6 +426,8 @@ static int show_buf(struct mxc_vout_output *vout, int idx, struct fb_info *fbi = vout->fbi; struct fb_var_screeninfo var; int ret; + u32 is_1080p; + u32 yres = 0; memcpy(&var, &fbi->var, sizeof(var)); @@ -390,9 +444,17 @@ static int show_buf(struct mxc_vout_output *vout, int idx, ret = fb_pan_display(fbi, &var); console_unlock(); } else { - var.yoffset = idx * fbi->var.yres; console_lock(); + is_1080p = CHECK_TILED_1080P_DISPLAY(vout); + if (is_1080p) { + yres = fbi->var.yres; + fbi->var.yres = FRAME_HEIGHT_1080P; + } + + var.yoffset = idx * fbi->var.yres; ret = fb_pan_display(fbi, &var); + if (is_1080p) + fbi->var.yres = yres; console_unlock(); } @@ -408,6 +470,8 @@ static void disp_work_func(struct work_struct *work) unsigned long flags = 0; struct ipu_pos ipos; int ret = 0; + u32 is_1080p; + u32 ocrop_h = 0; v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work begin one frame\n"); @@ -458,11 +522,31 @@ static void disp_work_func(struct work_struct *work) } vout->task.output.paddr = vout->disp_bufs[vout->frame_count % FB_BUFS]; + + is_1080p = CHECK_TILED_1080P_DISPLAY(vout); + if (is_1080p) { + vout->task.input.crop.h = FRAME_HEIGHT_1080P; + vout->task.output.height = FRAME_HEIGHT_1080P; + ocrop_h = vout->task.output.crop.h; + vout->task.output.crop.h = FRAME_HEIGHT_1080P; + } + if (vout->is_vdoaipu_task) { + vout->vdoa_task.input.paddr = vout->task.input.paddr; + vout->vdoa_task.output.paddr = vout->vdoa_dma.paddr; + ret = ipu_queue_task(&vout->vdoa_task); + if (ret < 0) { + mutex_unlock(&vout->task_lock); + goto err; + } + vout->task.input.paddr = vout->vdoa_task.output.paddr; + } ret = ipu_queue_task(&vout->task); if (ret < 0) { mutex_unlock(&vout->task_lock); goto err; } + if (is_1080p) + vout->task.output.crop.h = ocrop_h; } mutex_unlock(&vout->task_lock); @@ -569,6 +653,7 @@ static int mxc_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) { struct mxc_vout_output *vout = q->priv_data; + unsigned int frame_size; if (!vout) return -EINVAL; @@ -576,8 +661,8 @@ static int mxc_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count, if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type) return -EINVAL; - *size = PAGE_ALIGN(vout->task.input.width * vout->task.input.height * - fmt_to_bpp(vout->task.input.format)/8); + frame_size = get_frame_size(vout); + *size = PAGE_ALIGN(frame_size); return 0; } @@ -758,8 +843,7 @@ static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh, f->fmt.pix.width = vout->task.input.width; f->fmt.pix.height = vout->task.input.height; f->fmt.pix.pixelformat = vout->task.input.format; - f->fmt.pix.sizeimage = vout->task.input.width * vout->task.input.height * - fmt_to_bpp(vout->task.input.format)/8; + f->fmt.pix.sizeimage = get_frame_size(vout); if (f->fmt.pix.priv) { rect = (struct v4l2_rect *)f->fmt.pix.priv; @@ -768,6 +852,10 @@ static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh, rect->width = vout->task.input.crop.w; rect->height = vout->task.input.crop.h; } + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "frame_size:0x%x, pix_fmt:0x%x\n", + f->fmt.pix.sizeimage, + vout->task.input.format); return 0; } @@ -813,33 +901,128 @@ again: return ret; } +static inline int vdoaipu_try_task(struct mxc_vout_output *vout) +{ + int ret; + u32 icrop_h = 0, icrop_w = 0; + int is_1080p_stream; + size_t size; + struct ipu_task *ipu_task = &vout->task; + struct ipu_task *vdoa_task = &vout->vdoa_task; + + is_1080p_stream = CHECK_TILED_1080P_STREAM(vout); + if (is_1080p_stream) + ipu_task->input.crop.h = VALID_HEIGHT_1080P; + + if (ipu_task->input.crop.h % IPU_PIX_FMT_TILED_NV12_MBALIGN) { + icrop_h = ipu_task->input.crop.h; + ipu_task->input.crop.h = ALIGN(ipu_task->input.crop.h, + IPU_PIX_FMT_TILED_NV12_MBALIGN); + } + if (ipu_task->input.crop.w % IPU_PIX_FMT_TILED_NV12_MBALIGN) { + icrop_w = ipu_task->input.crop.w; + ipu_task->input.crop.w = ALIGN(ipu_task->input.crop.w, + IPU_PIX_FMT_TILED_NV12_MBALIGN); + } + + memset(vdoa_task, 0, sizeof(*vdoa_task)); + memcpy(&vdoa_task->input, &ipu_task->input, sizeof(ipu_task->input)); + vdoa_task->output.format = IPU_PIX_FMT_NV12; + vdoa_task->output.width = ipu_task->input.crop.w; + vdoa_task->output.height = ipu_task->input.crop.h; + vdoa_task->output.crop.w = ipu_task->input.crop.w; + vdoa_task->output.crop.h = ipu_task->input.crop.h; + + size = PAGE_ALIGN(ipu_task->input.crop.w * + ipu_task->input.crop.h * + fmt_to_bpp(vdoa_task->output.format)/8); + if (size > vout->vdoa_dma.size) { + if (vout->vdoa_dma.vaddr) { + dma_free_coherent(vout->vbq.dev, vout->vdoa_dma.size, + vout->vdoa_dma.vaddr, vout->vdoa_dma.paddr); + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "free vdoa_dma.size:0x%x, paddr:0x%x\n", + vout->vdoa_dma.size, + vout->vdoa_dma.paddr); + memset(&vout->vdoa_dma, 0, sizeof(vout->vdoa_dma)); + } + vout->vdoa_dma.size = size; + vout->vdoa_dma.vaddr = dma_alloc_coherent(vout->vbq.dev, + vout->vdoa_dma.size, + &vout->vdoa_dma.paddr, + GFP_DMA | GFP_KERNEL); + if (!vout->vdoa_dma.vaddr) + return -ENOMEM; + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "alloc vdoa_dma.size:0x%x, paddr:0x%x\n", + vout->vdoa_dma.size, + vout->vdoa_dma.paddr); + } + ret = ipu_check_task(vdoa_task); + if (ret != IPU_CHECK_OK) + return -EINVAL; + + ipu_task->input.format = vdoa_task->output.format; + if (icrop_h) { + ipu_task->input.height = vdoa_task->output.height; + ipu_task->input.crop.h = icrop_h; + } + if (icrop_w) { + ipu_task->input.width = vdoa_task->output.width; + ipu_task->input.crop.w = icrop_w; + } + ret = ipu_try_task(vout); + + return ret; +} + static int mxc_vout_try_task(struct mxc_vout_output *vout) { int ret = 0; + struct ipu_output *output = &vout->task.output; + struct ipu_input *input = &vout->task.input; - vout->task.input.crop.w -= vout->task.input.crop.w%8; - vout->task.input.crop.h -= vout->task.input.crop.h%8; - + input->crop.w -= input->crop.w%8; + input->crop.h -= input->crop.h%8; /* assume task.output already set by S_CROP */ if (is_pp_bypass(vout)) { v4l2_info(vout->vfd->v4l2_dev, "Bypass IC.\n"); vout->bypass_pp = true; - vout->task.output.format = vout->task.input.format; + output->format = input->format; } else { /* if need CSC, choose IPU-DP or IPU_IC do it */ vout->bypass_pp = false; if (vout->disp_support_csc) { - if (colorspaceofpixel(vout->task.input.format) == YUV_CS) - vout->task.output.format = IPU_PIX_FMT_UYVY; + if (colorspaceofpixel(input->format) == YUV_CS) + output->format = IPU_PIX_FMT_UYVY; else - vout->task.output.format = IPU_PIX_FMT_RGB565; + output->format = IPU_PIX_FMT_RGB565; } else { if (colorspaceofpixel(vout->disp_fmt) == YUV_CS) - vout->task.output.format = IPU_PIX_FMT_UYVY; + output->format = IPU_PIX_FMT_UYVY; + else + output->format = IPU_PIX_FMT_RGB565; + } + + vout->is_vdoaipu_task = 0; + if ((IPU_PIX_FMT_TILED_NV12 == input->format) || + (IPU_PIX_FMT_TILED_NV12F == input->format)) { + /* check resize/rotate/flip, or csc task */ + if ((IPU_ROTATE_NONE != output->rotate) || + (input->crop.w != output->crop.w) || + (input->crop.h != output->crop.h) || + (!vout->disp_support_csc && + (colorspaceofpixel(vout->disp_fmt) == RGB_CS))) + vout->is_vdoaipu_task = 1; else - vout->task.output.format = IPU_PIX_FMT_RGB565; + /* IC bypass */ + output->format = IPU_PIX_FMT_NV12; } - ret = ipu_try_task(vout); + + if (vout->is_vdoaipu_task) + ret = vdoaipu_try_task(vout); + else + ret = ipu_try_task(vout); } return ret; @@ -849,11 +1032,23 @@ static int mxc_vout_try_format(struct mxc_vout_output *vout, struct v4l2_format { int ret = 0; struct v4l2_rect *rect = NULL; + u32 o_height = 0; + u32 ocrop_h = 0; + u32 is_1080p; vout->task.input.width = f->fmt.pix.width; vout->task.input.height = f->fmt.pix.height; vout->task.input.format = f->fmt.pix.pixelformat; + if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) { + if (vout->task.input.width > MAX_INTERLACED_WIDTH) + return -EINVAL; + v4l2_info(vout->vfd->v4l2_dev, + "tiled fmt enable deinterlace.\n"); + vout->task.input.deinterlace.enable = true; + vout->task.input.deinterlace.field_fmt = + IPU_DEINTERLACE_FIELD_TOP; + } switch (f->fmt.pix.field) { /* Images are in progressive format, not interlaced */ case V4L2_FIELD_NONE: @@ -893,6 +1088,15 @@ static int mxc_vout_try_format(struct mxc_vout_output *vout, struct v4l2_format vout->task.input.crop.h = f->fmt.pix.height; } + is_1080p = CHECK_TILED_1080P_DISPLAY(vout); + if (is_1080p) { + vout->task.input.crop.h = FRAME_HEIGHT_1080P; + o_height = vout->task.output.height; + ocrop_h = vout->task.output.crop.h; + vout->task.output.height = FRAME_HEIGHT_1080P; + vout->task.output.crop.h = FRAME_HEIGHT_1080P; + } + ret = mxc_vout_try_task(vout); if (!ret) { if (rect) { @@ -904,6 +1108,11 @@ static int mxc_vout_try_format(struct mxc_vout_output *vout, struct v4l2_format } } + if (is_1080p) { + vout->task.output.height = o_height; + vout->task.output.crop.h = ocrop_h; + } + return ret; } @@ -1311,6 +1520,7 @@ static int config_disp_output(struct mxc_vout_output *vout) struct fb_info *fbi = vout->fbi; struct fb_var_screeninfo var; int i, display_buf_size, fb_num, ret; + u32 is_1080p; memcpy(&var, &fbi->var, sizeof(var)); @@ -1332,7 +1542,11 @@ static int config_disp_output(struct mxc_vout_output *vout) } else { fb_num = FB_BUFS; var.xres_virtual = var.xres; - var.yres_virtual = fb_num * var.yres; + is_1080p = CHECK_TILED_1080P_DISPLAY(vout); + if (is_1080p) + var.yres_virtual = fb_num * FRAME_HEIGHT_1080P; + else + var.yres_virtual = fb_num * var.yres; var.vmode &= ~FB_VMODE_YWRAP; } var.bits_per_pixel = fmt_to_bpp(vout->task.output.format); @@ -1357,7 +1571,11 @@ static int config_disp_output(struct mxc_vout_output *vout) if (ret < 0) return ret; - display_buf_size = fbi->fix.line_length * fbi->var.yres; + is_1080p = CHECK_TILED_1080P_DISPLAY(vout); + if (is_1080p) + display_buf_size = fbi->fix.line_length * FRAME_HEIGHT_1080P; + else + display_buf_size = fbi->fix.line_length * fbi->var.yres; for (i = 0; i < fb_num; i++) vout->disp_bufs[i] = fbi->fix.smem_start + i * display_buf_size; @@ -1515,6 +1733,15 @@ static void mxc_vout_free_output(struct mxc_vout_dev *dev) for (i = 0; i < dev->out_num; i++) { vout = dev->out[i]; vfd = vout->vfd; + if (vout->vdoa_dma.vaddr) { + dma_free_coherent(vout->vbq.dev, vout->vdoa_dma.size, + vout->vdoa_dma.vaddr, vout->vdoa_dma.paddr); + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "free vdoa_dma.size:0x%x, paddr:0x%x\n", + vout->vdoa_dma.size, + vout->vdoa_dma.paddr); + memset(&vout->vdoa_dma, 0, sizeof(vout->vdoa_dma)); + } if (vfd) { if (!video_is_registered(vfd)) video_device_release(vfd); diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c index 18fd1a67ba83..92fdf710a5b1 100644 --- a/drivers/mfd/mxc-hdmi-core.c +++ b/drivers/mfd/mxc-hdmi-core.c @@ -64,6 +64,7 @@ int mxc_hdmi_ipu_id; int mxc_hdmi_disp_id; static struct mxc_edid_cfg hdmi_core_edid_cfg; static int hdmi_core_init; +static unsigned int hdmi_dma_running; u8 hdmi_readb(unsigned int reg) { @@ -243,6 +244,12 @@ static void hdmi_set_clock_regenerator_n(unsigned int value) { u8 val; + if (!hdmi_dma_running) { + hdmi_writeb(value & 0xff, HDMI_AUD_N1); + hdmi_writeb(0, HDMI_AUD_N2); + hdmi_writeb(0, HDMI_AUD_N3); + } + hdmi_writeb(value & 0xff, HDMI_AUD_N1); hdmi_writeb((value >> 8) & 0xff, HDMI_AUD_N2); hdmi_writeb((value >> 16) & 0x0f, HDMI_AUD_N3); @@ -257,6 +264,12 @@ static void hdmi_set_clock_regenerator_cts(unsigned int cts) { u8 val; + if (!hdmi_dma_running) { + hdmi_writeb(cts & 0xff, HDMI_AUD_CTS1); + hdmi_writeb(0, HDMI_AUD_CTS2); + hdmi_writeb(0, HDMI_AUD_CTS3); + } + /* Must be set/cleared first */ val = hdmi_readb(HDMI_AUD_CTS3); val &= ~HDMI_AUD_CTS3_CTS_MANUAL; @@ -433,18 +446,12 @@ static void hdmi_set_clk_regenerator(void) return; } - clk_enable(isfr_clk); - clk_enable(iahb_clk); - pr_debug("%s: samplerate=%d ratio=%d pixelclk=%d N=%d cts=%d\n", __func__, sample_rate, hdmi_ratio, (int)pixel_clk_rate, clk_n, clk_cts); - hdmi_set_clock_regenerator_n(clk_n); hdmi_set_clock_regenerator_cts(clk_cts); - - clk_disable(iahb_clk); - clk_disable(isfr_clk); + hdmi_set_clock_regenerator_n(clk_n); } /* Need to run this before phy is enabled the first time to prevent @@ -464,10 +471,15 @@ void hdmi_clk_regenerator_update_pixel_clock(void) hdmi_set_clk_regenerator(); } +void hdmi_set_dma_mode(unsigned int dma_running) +{ + hdmi_dma_running = dma_running; + hdmi_set_clk_regenerator(); +} + void hdmi_set_sample_rate(unsigned int rate) { sample_rate = rate; - hdmi_set_clk_regenerator(); } void hdmi_set_edid_cfg(struct mxc_edid_cfg *cfg) @@ -511,6 +523,7 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev) #endif hdmi_core_init = 0; + hdmi_dma_running = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index d6f72f8c69a3..beb289133744 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -452,6 +452,8 @@ config MTD_NAND_GPMI_NAND depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28 || SOC_IMX6Q) select MTD_PARTITIONS select MTD_CMDLINE_PARTS + select MTC_CHAR + select MXS_DMA help Enables NAND Flash support for IMX23 or IMX28. The GPMI controller is very powerful, with the help of BCH diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h index 04acf6413b16..e1478d4de587 100644 --- a/drivers/mtd/nand/gpmi-nand/bch-regs.h +++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h @@ -71,6 +71,7 @@ ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ : ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ ) +#define BM_BCH_FLASH0LAYOUT0_GF13_0_GF14_1 0x00000400 #define HW_BCH_FLASH0LAYOUT1 0x00000090 @@ -103,4 +104,5 @@ ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ : ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ ) +#define BM_BCH_FLASH0LAYOUT1_GF13_0_GF14_1 0x00000400 #endif diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 698535773ff4..1c273e4eb2b4 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -22,11 +22,15 @@ #include <linux/delay.h> #include <linux/clk.h> #include <mach/mxs.h> +#include <mach/clock.h> #include "gpmi-nand.h" #include "gpmi-regs.h" #include "bch-regs.h" +#define FEATURE_SIZE 4 /* p1, p2, p3, p4 */ +#define NAND_CMD_SET_FEATURE 0xef + struct timing_threshod timing_default_threshold = { .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >> BP_GPMI_TIMING0_DATA_SETUP), @@ -37,6 +41,281 @@ struct timing_threshod timing_default_threshold = { .max_dll_delay_in_ns = 16, }; +static struct clk *mxs_dma_clk; + +static void setup_ddr_timing_onfi(struct gpmi_nand_data *this) +{ + uint32_t value; + struct resources *resources = &this->resources; + + /* set timing 2 register */ + value = BF_GPMI_TIMING2_DATA_PAUSE(0x6) + | BF_GPMI_TIMING2_CMDADD_PAUSE(0x4) + | BF_GPMI_TIMING2_POSTAMBLE_DELAY(0x2) + | BF_GPMI_TIMING2_PREAMBLE_DELAY(0x4) + | BF_GPMI_TIMING2_CE_DELAY(0x2) + | BF_GPMI_TIMING2_READ_LATENCY(0x2); + + __raw_writel(value, resources->gpmi_regs + HW_GPMI_TIMING2); + + /* set timing 1 register */ + __raw_writel(BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(0x500), + resources->gpmi_regs + HW_GPMI_TIMING1); + + /* Put GPMI in NAND mode, disable device reset, and make certain + IRQRDY polarity is active high. */ + value = BV_GPMI_CTRL1_GPMI_MODE__NAND + | BM_GPMI_CTRL1_GANGED_RDYBUSY + | BF_GPMI_CTRL1_WRN_DLY_SEL(0x3) + | (BV_GPMI_CTRL1_DEV_RESET__DISABLED << 3) + | (BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH << 2); + + __raw_writel(value, resources->gpmi_regs + HW_GPMI_CTRL1_SET); +} + +/* This must be called in the context of enabling necessary clocks */ +static void common_ddr_init(struct resources *resources) +{ + uint32_t value; + + /* [6] enable both write & read DDR DLLs */ + value = BM_GPMI_READ_DDR_DLL_CTRL_REFCLK_ON | + BM_GPMI_READ_DDR_DLL_CTRL_ENABLE | + BF_GPMI_READ_DDR_DLL_CTRL_SLV_UPDATE_INT(0x2) | + BF_GPMI_READ_DDR_DLL_CTRL_SLV_DLY_TARGET(0x7); + + __raw_writel(value, resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL); + + /* [7] reset read */ + __raw_writel(value | BM_GPMI_READ_DDR_DLL_CTRL_RESET, + resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL); + value = value & ~BM_GPMI_READ_DDR_DLL_CTRL_RESET; + __raw_writel(value, resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL); + + value = BM_GPMI_WRITE_DDR_DLL_CTRL_REFCLK_ON | + BM_GPMI_WRITE_DDR_DLL_CTRL_ENABLE | + BF_GPMI_WRITE_DDR_DLL_CTRL_SLV_UPDATE_INT(0x2) | + BF_GPMI_WRITE_DDR_DLL_CTRL_SLV_DLY_TARGET(0x7) , + + __raw_writel(value, resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL); + + /* [8] reset write */ + __raw_writel(value | BM_GPMI_WRITE_DDR_DLL_CTRL_RESET, + resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL); + __raw_writel(value, resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL); + + /* [9] wait for locks for read and write */ + do { + uint32_t read_status, write_status; + uint32_t r_mask, w_mask; + + read_status = __raw_readl(resources->gpmi_regs + + HW_GPMI_READ_DDR_DLL_STS); + write_status = __raw_readl(resources->gpmi_regs + + HW_GPMI_WRITE_DDR_DLL_STS); + + r_mask = (BM_GPMI_READ_DDR_DLL_STS_REF_LOCK | + BM_GPMI_READ_DDR_DLL_STS_SLV_LOCK); + w_mask = (BM_GPMI_WRITE_DDR_DLL_STS_REF_LOCK | + BM_GPMI_WRITE_DDR_DLL_STS_SLV_LOCK); + + if (((read_status & r_mask) == r_mask) + && ((write_status & w_mask) == w_mask)) + break; + } while (1); + + /* [10] force update of read/write */ + value = __raw_readl(resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL); + __raw_writel(value | BM_GPMI_READ_DDR_DLL_CTRL_SLV_FORCE_UPD, + resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL); + __raw_writel(value, resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL); + + value = __raw_readl(resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL); + __raw_writel(value | BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_FORCE_UPD, + resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL); + __raw_writel(value, resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL); + + /* [11] set gate update */ + value = __raw_readl(resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL); + value |= BM_GPMI_READ_DDR_DLL_CTRL_GATE_UPDATE; + __raw_writel(value, resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL); + + value = __raw_readl(resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL); + value |= BM_GPMI_WRITE_DDR_DLL_CTRL_GATE_UPDATE; + __raw_writel(value, resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL); +} + +static int enable_ddr_onfi(struct gpmi_nand_data *this) +{ + struct resources *resources = &this->resources; + struct nand_chip *nand = &this->nand; + struct mtd_info *mtd = &this->mtd; + int saved_chip_number = 0; + uint8_t device_feature[FEATURE_SIZE]; + int mode = 0;/* there is 5 mode available, default is 0 */ + + saved_chip_number = this->current_chip; + nand->select_chip(mtd, 0); + + /* [0] set proper timing */ + __raw_writel(BF_GPMI_TIMING0_ADDRESS_SETUP(0x1) + | BF_GPMI_TIMING0_DATA_HOLD(0x3) + | BF_GPMI_TIMING0_DATA_SETUP(0x3), + resources->gpmi_regs + HW_GPMI_TIMING0); + + /* [1] send SET FEATURE commond to NAND */ + memset(device_feature, 0, sizeof(device_feature)); + device_feature[0] = (0x1 << 4) | (mode & 0x7); + + nand->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + nand->cmdfunc(mtd, NAND_CMD_SET_FEATURE, 1, -1); + nand->write_buf(mtd, device_feature, FEATURE_SIZE); + + /* [2] set clk divider */ + __raw_writel(BM_GPMI_CTRL1_GPMI_CLK_DIV2_EN, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* [3] about the clock, pay attention! */ + nand->select_chip(mtd, saved_chip_number); + { + struct clk *pll1; + pll1 = clk_get(NULL, "pll1_main_clk"); + if (IS_ERR(pll1)) { + printk(KERN_INFO "No PLL1 clock\n"); + return -EINVAL; + } + clk_set_parent(resources->clock, pll1); + clk_set_rate(resources->clock, 20000000); + } + nand->select_chip(mtd, 0); + + /* [4] setup timing */ + setup_ddr_timing_onfi(this); + + /* [5] set to SYNC mode */ + __raw_writel(BM_GPMI_CTRL1_TOGGLE_MODE, + resources->gpmi_regs + HW_GPMI_CTRL1_CLR); + __raw_writel(BM_GPMI_CTRL1_SSYNCMODE | BM_GPMI_CTRL1_GANGED_RDYBUSY, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* common DDR initialization */ + common_ddr_init(resources); + + nand->select_chip(mtd, saved_chip_number); + + printk(KERN_INFO "Micron ONFI NAND enters synchronous mode %d\n", mode); + return 0; +} + +static void setup_ddr_timing_toggle(struct gpmi_nand_data *this) +{ + uint32_t value; + struct resources *resources = &this->resources; + + /* set timing 2 register */ + value = BF_GPMI_TIMING2_DATA_PAUSE(0x6) + | BF_GPMI_TIMING2_CMDADD_PAUSE(0x4) + | BF_GPMI_TIMING2_POSTAMBLE_DELAY(0x3) + | BF_GPMI_TIMING2_PREAMBLE_DELAY(0x2) + | BF_GPMI_TIMING2_CE_DELAY(0x2) + | BF_GPMI_TIMING2_READ_LATENCY(0x2); + + __raw_writel(value, resources->gpmi_regs + HW_GPMI_TIMING2); + + /* set timing 1 register */ + __raw_writel(BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(0x500), + resources->gpmi_regs + HW_GPMI_TIMING1); + + /* Put GPMI in NAND mode, disable device reset, and make certain + IRQRDY polarity is active high. */ + value = BV_GPMI_CTRL1_GPMI_MODE__NAND + | BM_GPMI_CTRL1_GANGED_RDYBUSY + | (BV_GPMI_CTRL1_DEV_RESET__DISABLED << 3) + | (BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH << 2); + + __raw_writel(value, resources->gpmi_regs + HW_GPMI_CTRL1_SET); +} + +static int enable_ddr_toggle(struct gpmi_nand_data *this) +{ + struct resources *resources = &this->resources; + struct nand_chip *nand = &this->nand; + struct mtd_info *mtd = &this->mtd; + int saved_chip_number = this->current_chip; + + nand->select_chip(mtd, 0); + + /* [0] set proper timing */ + __raw_writel(BF_GPMI_TIMING0_ADDRESS_SETUP(0x5) + | BF_GPMI_TIMING0_DATA_HOLD(0xa) + | BF_GPMI_TIMING0_DATA_SETUP(0xa), + resources->gpmi_regs + HW_GPMI_TIMING0); + + /* [2] set clk divider */ + __raw_writel(BM_GPMI_CTRL1_GPMI_CLK_DIV2_EN, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* [3] about the clock, pay attention! */ + nand->select_chip(mtd, saved_chip_number); + { + struct clk *pll1; + unsigned long rate; + + pll1 = clk_get(NULL, "pll1_main_clk"); + if (IS_ERR(pll1)) { + printk(KERN_INFO "No PLL1 clock\n"); + return -EINVAL; + } + + /* toggle nand : 133/66 MHz */ + rate = 33000000; + clk_set_parent(resources->clock, pll1); + clk_set_rate(resources->clock, rate); + } + nand->select_chip(mtd, 0); + + /* [4] setup timing */ + setup_ddr_timing_toggle(this); + + /* [5] set to TOGGLE mode */ + __raw_writel(BM_GPMI_CTRL1_SSYNCMODE, + resources->gpmi_regs + HW_GPMI_CTRL1_CLR); + __raw_writel(BM_GPMI_CTRL1_TOGGLE_MODE | BM_GPMI_CTRL1_GANGED_RDYBUSY, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* common DDR initialization */ + common_ddr_init(resources); + + nand->select_chip(mtd, saved_chip_number); + + printk(KERN_INFO "-- Sumsung TOGGLE NAND is enabled now. --\n"); + return 0; +} + +inline bool is_board_support_ddr(struct gpmi_nand_data *this) +{ + /* Only arm2 lpddr2pop board supports the DDR, */ + /* the common arm2 board does not. */ + return this->pdata->enable_ddr; +} + +inline bool is_ddr_nand(struct gpmi_nand_data *this) +{ + return this->nand.onfi_version; +} + +/* To check if we need to initialize something else*/ +int extra_init(struct gpmi_nand_data *this) +{ + if (is_board_support_ddr(this)) { + if (1) + return enable_ddr_onfi(this); + if (0) + return enable_ddr_toggle(this); + } + return 0; +} + /* * Clear the bit and poll it cleared. This is usually called with * a reset address and mask being either SFTRST(bit 31) or CLKGATE @@ -128,6 +407,12 @@ int gpmi_init(struct gpmi_nand_data *this) struct resources *r = &this->resources; int ret; + mxs_dma_clk = clk_get(NULL, "mxs-dma-apbh"); + if (IS_ERR(mxs_dma_clk)) { + pr_err("can not get the dma clock\n"); + ret = -ENOENT; + goto err_out; + } ret = clk_prepare_enable(r->clock); if (ret) goto err_out; @@ -224,11 +509,15 @@ int bch_set_geometry(struct gpmi_nand_data *this) writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) + | (is_ddr_nand(this) && is_board_support_ddr(this) ? \ + BM_BCH_FLASH0LAYOUT0_GF13_0_GF14_1 : 0) | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this), r->bch_regs + HW_BCH_FLASH0LAYOUT0); writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) + | (is_ddr_nand(this) && is_board_support_ddr(this) ? \ + BM_BCH_FLASH0LAYOUT1_GF13_0_GF14_1 : 0) | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this), r->bch_regs + HW_BCH_FLASH0LAYOUT1); @@ -719,6 +1008,13 @@ void gpmi_begin(struct gpmi_nand_data *this) pr_err("We failed in enable the clk\n"); goto err_out; } + if (!clk_get_usecount(mxs_dma_clk)) { + ret = clk_prepare_enable(mxs_dma_clk); + if (ret) { + pr_err("We failed in enable the dma clk\n"); + goto err_out; + } + } /* set ready/busy timeout */ writel(0x500 << BP_GPMI_TIMING1_BUSY_TIMEOUT, @@ -784,6 +1080,8 @@ void gpmi_end(struct gpmi_nand_data *this) { struct resources *r = &this->resources; clk_disable_unprepare(r->clock); + if (clk_get_usecount(mxs_dma_clk)) + clk_disable_unprepare(mxs_dma_clk); } /* Clears a BCH interrupt. */ @@ -951,11 +1249,22 @@ int gpmi_send_page(struct gpmi_nand_data *this, uint32_t address; uint32_t ecc_command; uint32_t buffer_mask; + uint32_t busw; + uint32_t page_size; struct dma_async_tx_descriptor *desc; struct dma_chan *channel = get_dma_chan(this); int chip = this->current_chip; u32 pio[6]; + /* DDR use the 16-bit for DATA transmission! */ + if (is_board_support_ddr(this) && is_ddr_nand(this)) { + busw = BV_GPMI_CTRL0_WORD_LENGTH__16_BIT; + page_size = geo->page_size >> 1; + } else { + busw = BM_GPMI_CTRL0_WORD_LENGTH; + page_size = geo->page_size; + } + /* A DMA descriptor that does an ECC page read. */ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; @@ -964,7 +1273,7 @@ int gpmi_send_page(struct gpmi_nand_data *this, BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH + | busw | BF_GPMI_CTRL0_CS(chip, this) | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) | BF_GPMI_CTRL0_ADDRESS(address) @@ -973,7 +1282,7 @@ int gpmi_send_page(struct gpmi_nand_data *this, pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); - pio[3] = geo->page_size; + pio[3] = page_size; pio[4] = payload; pio[5] = auxiliary; @@ -997,18 +1306,29 @@ int gpmi_read_page(struct gpmi_nand_data *this, uint32_t address; uint32_t ecc_command; uint32_t buffer_mask; + uint32_t page_size; + uint32_t busw; struct dma_async_tx_descriptor *desc; struct dma_chan *channel = get_dma_chan(this); int chip = this->current_chip; unsigned long flags; u32 pio[6]; + /* DDR use the 16-bit for DATA transmission! */ + if (is_board_support_ddr(this) && is_ddr_nand(this)) { + busw = BV_GPMI_CTRL0_WORD_LENGTH__16_BIT; + page_size = geo->page_size >> 1; + } else { + busw = BM_GPMI_CTRL0_WORD_LENGTH; + page_size = geo->page_size; + } + /* [1] Wait for the chip to report ready. */ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH + | busw | BF_GPMI_CTRL0_CS(chip, this) | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) | BF_GPMI_CTRL0_ADDRESS(address) @@ -1030,7 +1350,7 @@ int gpmi_read_page(struct gpmi_nand_data *this, | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH + | busw | BF_GPMI_CTRL0_CS(chip, this) | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) | BF_GPMI_CTRL0_ADDRESS(address) @@ -1040,7 +1360,7 @@ int gpmi_read_page(struct gpmi_nand_data *this, pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); - pio[3] = geo->page_size; + pio[3] = page_size; pio[4] = payload; pio[5] = auxiliary; @@ -1061,11 +1381,11 @@ int gpmi_read_page(struct gpmi_nand_data *this, address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH + | busw | BF_GPMI_CTRL0_CS(chip, this) | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); + | BF_GPMI_CTRL0_XFER_COUNT(page_size); pio[1] = 0; pio[2] = 0; /* set GPMI_HW_GPMI_ECCCTRL, disable the BCH */ desc = channel->device->device_prep_slave_sg(channel, diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index ea3ce22ff9d5..f25b7c27b101 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -41,7 +41,30 @@ static struct nand_ecclayout gpmi_hw_ecclayout = { .eccpos = { 0, }, .oobfree = { {.offset = 0, .length = 0} } }; +#if 0 +void gpmi_show_regs(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + u32 reg, bch; + int i; + int n; + + n = 0xc0 / 0x10 + 1; + pr_info("-------------- Show GPMI registers ----------\n"); + for (i = 0; i <= n; i++) { + reg = __raw_readl(r->gpmi_regs + i * 0x10); + pr_info("offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); + } + pr_info("-------------- Show GPMI registers end ----------\n"); + pr_info("-------------- Show BCH registers ----------\n"); + for (i = 0; i <= n; i++) { + reg = __raw_readl(r->bch_regs + i * 0x10); + pr_info("offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); + } + pr_info("-------------- Show BCH registers end ----------\n"); +} +#endif static irqreturn_t bch_irq(int irq, void *cookie) { struct gpmi_nand_data *this = cookie; @@ -99,9 +122,14 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this) /* The default for the length of Galois Field. */ geo->gf_len = 13; + if (is_ddr_nand(this) && is_board_support_ddr(this)) + geo->gf_len = 14; /* The default for chunk size. There is no oobsize greater then 512. */ geo->ecc_chunk_size = 512; + if (is_ddr_nand(this) && is_board_support_ddr(this)) + geo->ecc_chunk_size = 1024; + while (geo->ecc_chunk_size < mtd->oobsize) geo->ecc_chunk_size *= 2; /* keep C >= O */ @@ -115,6 +143,10 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this) } geo->page_size = mtd->writesize + mtd->oobsize; + if (is_ddr_nand(this) && is_board_support_ddr(this)) + geo->page_size = mtd->writesize + geo->metadata_size + + (geo->ecc_strength * geo->gf_len * 8 + / geo->ecc_chunk_count); geo->payload_size = mtd->writesize; /* @@ -1001,6 +1033,19 @@ exit_auxiliary: } } +#define MAX_PAGESIZE 8192 +static uint8_t verify_buf[MAX_PAGESIZE]; + +static int gpmi_verify_buf(struct mtd_info *mtd, const uint8_t * buf, int len) +{ + struct nand_chip *nand = mtd->priv; + + gpmi_ecc_read_page(mtd, nand, verify_buf, len); + if (memcmp(buf, verify_buf, len)) + return -EFAULT; + return 0; +} + /* * There are several places in this driver where we have to handle the OOB and * block marks. This is the function where things are the most complicated, so @@ -1401,6 +1446,25 @@ static int __devinit nand_boot_init(struct gpmi_nand_data *this) return 0; } +#if 0 +static void show_nfc_geometry(struct bch_geometry *geo) +{ + pr_info("---------------------------------------\n"); + pr_info(" NFC Geometry (used by BCH)\n"); + pr_info("---------------------------------------\n"); + pr_info("ECC Strength : %u\n", geo->ecc_strength); + pr_info("Page Size in Bytes : %u\n", geo->page_size); + pr_info("Metadata Size in Bytes : %u\n", geo->metadata_size); + pr_info("ECC Chunk Size in Bytes: %u\n", geo->ecc_chunk_size); + pr_info("ECC Chunk Count : %u\n", geo->ecc_chunk_count); + pr_info("Payload Size in Bytes : %u\n", geo->payload_size); + pr_info("Auxiliary Size in Bytes: %u\n", geo->auxiliary_size); + pr_info("Auxiliary Status Offset: %u\n", geo->auxiliary_status_offset); + pr_info("Block Mark Byte Offset : %u\n", geo->block_mark_byte_offset); + pr_info("Block Mark Bit Offset : %u\n", geo->block_mark_bit_offset); +} +#endif + static int __devinit gpmi_set_geometry(struct gpmi_nand_data *this) { int ret; @@ -1434,6 +1498,11 @@ static int gpmi_pre_bbt_scan(struct gpmi_nand_data *this) if (ret) return ret; + /* extra init */ + ret = extra_init(this); + if (ret != 0) + return ret; + /* NAND boot init, depends on the gpmi_set_geometry(). */ return nand_boot_init(this); } @@ -1482,6 +1551,7 @@ static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this) chip->read_byte = gpmi_read_byte; chip->read_buf = gpmi_read_buf; chip->write_buf = gpmi_write_buf; + chip->verify_buf = gpmi_verify_buf; chip->ecc.read_page = gpmi_ecc_read_page; chip->ecc.write_page = gpmi_ecc_write_page; chip->ecc.read_oob = gpmi_ecc_read_oob; diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index 06a072bf85a1..229b2c5e1b12 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -235,6 +235,8 @@ struct timing_threshod { }; /* Common Services */ +extern bool is_ddr_nand(struct gpmi_nand_data *); +extern bool is_board_support_ddr(struct gpmi_nand_data *); extern int common_nfc_set_geometry(struct gpmi_nand_data *); extern struct dma_chan *get_dma_chan(struct gpmi_nand_data *); extern void prepare_data_dma(struct gpmi_nand_data *, @@ -245,6 +247,7 @@ extern int start_dma_with_bch_irq(struct gpmi_nand_data *, struct dma_async_tx_descriptor *); /* GPMI-NAND helper function library */ +extern int extra_init(struct gpmi_nand_data *); extern int gpmi_init(struct gpmi_nand_data *); extern void gpmi_clear_bch(struct gpmi_nand_data *); extern void gpmi_dump_info(struct gpmi_nand_data *); diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h index 83431240e2f2..a281716dc61a 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h @@ -1,7 +1,7 @@ /* * Freescale GPMI NAND Flash Driver * - * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright 2008-2012 Freescale Semiconductor, Inc. * Copyright 2008 Embedded Alley Solutions, Inc. * * This program is free software; you can redistribute it and/or modify @@ -108,32 +108,50 @@ #define HW_GPMI_CTRL1_CLR 0x00000068 #define HW_GPMI_CTRL1_TOG 0x0000006c -#define BM_GPMI_CTRL1_BCH_MODE (1 << 18) - -#define BP_GPMI_CTRL1_DLL_ENABLE 17 -#define BM_GPMI_CTRL1_DLL_ENABLE (1 << BP_GPMI_CTRL1_DLL_ENABLE) - -#define BP_GPMI_CTRL1_HALF_PERIOD 16 -#define BM_GPMI_CTRL1_HALF_PERIOD (1 << BP_GPMI_CTRL1_HALF_PERIOD) - -#define BP_GPMI_CTRL1_RDN_DELAY 12 -#define BM_GPMI_CTRL1_RDN_DELAY (0xf << BP_GPMI_CTRL1_RDN_DELAY) -#define BF_GPMI_CTRL1_RDN_DELAY(v) \ - (((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY) - -#define BM_GPMI_CTRL1_DEV_RESET (1 << 3) -#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0 -#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1 - -#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2) -#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0 -#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1 - -#define BM_GPMI_CTRL1_CAMERA_MODE (1 << 1) -#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0 -#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1 - -#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0) +#define BM_GPMI_CTRL1_DEV_CLK_STOP 0x80000000 +#define BM_GPMI_CTRL1_SSYNC_CLK_STOP 0x40000000 +#define BM_GPMI_CTRL1_WRITE_CLK_STOP 0x20000000 +#define BM_GPMI_CTRL1_TOGGLE_MODE 0x10000000 +#define BM_GPMI_CTRL1_GPMI_CLK_DIV2_EN 0x08000000 +#define BM_GPMI_CTRL1_UPDATE_CS 0x04000000 +#define BM_GPMI_CTRL1_SSYNCMODE 0x02000000 +#define BV_GPMI_CTRL1_SSYNCMODE__ASYNC 0x0 +#define BV_GPMI_CTRL1_SSYNCMODE__SSYNC 0x1 +#define BM_GPMI_CTRL1_DECOUPLE_CS 0x01000000 +#define BP_GPMI_CTRL1_WRN_DLY_SEL 22 +#define BM_GPMI_CTRL1_WRN_DLY_SEL 0x00C00000 +#define BF_GPMI_CTRL1_WRN_DLY_SEL(v) \ + (((v) << 22) & BM_GPMI_CTRL1_WRN_DLY_SEL) +#define BM_GPMI_CTRL1_RSVD1 0x00200000 +#define BM_GPMI_CTRL1_TIMEOUT_IRQ_EN 0x00100000 +#define BM_GPMI_CTRL1_GANGED_RDYBUSY 0x00080000 +#define BM_GPMI_CTRL1_BCH_MODE 0x00040000 +#define BM_GPMI_CTRL1_DLL_ENABLE 0x00020000 +#define BP_GPMI_CTRL1_HALF_PERIOD 16 +#define BM_GPMI_CTRL1_HALF_PERIOD 0x00010000 +#define BP_GPMI_CTRL1_RDN_DELAY 12 +#define BM_GPMI_CTRL1_RDN_DELAY 0x0000F000 +#define BF_GPMI_CTRL1_RDN_DELAY(v) \ + (((v) << 12) & BM_GPMI_CTRL1_RDN_DELAY) +#define BM_GPMI_CTRL1_DMA2ECC_MODE 0x00000800 +#define BM_GPMI_CTRL1_DEV_IRQ 0x00000400 +#define BM_GPMI_CTRL1_TIMEOUT_IRQ 0x00000200 +#define BM_GPMI_CTRL1_BURST_EN 0x00000100 +#define BM_GPMI_CTRL1_ABORT_WAIT_REQUEST 0x00000080 +#define BP_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL 4 +#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL 0x00000070 +#define BF_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL(v) \ + (((v) << 4) & BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL) +#define BM_GPMI_CTRL1_DEV_RESET 0x00000008 +#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0 +#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1 +#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY 0x00000004 +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0 +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1 +#define BM_GPMI_CTRL1_CAMERA_MODE 0x00000002 +#define BM_GPMI_CTRL1_GPMI_MODE 0x00000001 +#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0 +#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1 #define HW_GPMI_TIMING0 0x00000070 @@ -154,8 +172,45 @@ #define HW_GPMI_TIMING1 0x00000080 #define BP_GPMI_TIMING1_BUSY_TIMEOUT 16 +#define BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 0xFFFF0000 +#define BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(v) \ + (((v) << 16) & BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT) #define HW_GPMI_TIMING2 0x00000090 + +#define BP_GPMI_TIMING2_RSVD1 27 +#define BM_GPMI_TIMING2_RSVD1 0xF8000000 +#define BF_GPMI_TIMING2_RSVD1(v) \ + (((v) << 27) & BM_GPMI_TIMING2_RSVD1) +#define BP_GPMI_TIMING2_READ_LATENCY 24 +#define BM_GPMI_TIMING2_READ_LATENCY 0x07000000 +#define BF_GPMI_TIMING2_READ_LATENCY(v) \ + (((v) << 24) & BM_GPMI_TIMING2_READ_LATENCY) +#define BP_GPMI_TIMING2_RSVD0 21 +#define BM_GPMI_TIMING2_RSVD0 0x00E00000 +#define BF_GPMI_TIMING2_RSVD0(v) \ + (((v) << 21) & BM_GPMI_TIMING2_RSVD0) +#define BP_GPMI_TIMING2_CE_DELAY 16 +#define BM_GPMI_TIMING2_CE_DELAY 0x001F0000 +#define BF_GPMI_TIMING2_CE_DELAY(v) \ + (((v) << 16) & BM_GPMI_TIMING2_CE_DELAY) +#define BP_GPMI_TIMING2_PREAMBLE_DELAY 12 +#define BM_GPMI_TIMING2_PREAMBLE_DELAY 0x0000F000 +#define BF_GPMI_TIMING2_PREAMBLE_DELAY(v) \ + (((v) << 12) & BM_GPMI_TIMING2_PREAMBLE_DELAY) +#define BP_GPMI_TIMING2_POSTAMBLE_DELAY 8 +#define BM_GPMI_TIMING2_POSTAMBLE_DELAY 0x00000F00 +#define BF_GPMI_TIMING2_POSTAMBLE_DELAY(v) \ + (((v) << 8) & BM_GPMI_TIMING2_POSTAMBLE_DELAY) +#define BP_GPMI_TIMING2_CMDADD_PAUSE 4 +#define BM_GPMI_TIMING2_CMDADD_PAUSE 0x000000F0 +#define BF_GPMI_TIMING2_CMDADD_PAUSE(v) \ + (((v) << 4) & BM_GPMI_TIMING2_CMDADD_PAUSE) +#define BP_GPMI_TIMING2_DATA_PAUSE 0 +#define BM_GPMI_TIMING2_DATA_PAUSE 0x0000000F +#define BF_GPMI_TIMING2_DATA_PAUSE(v) \ + (((v) << 0) & BM_GPMI_TIMING2_DATA_PAUSE) + #define HW_GPMI_DATA 0x000000a0 /* MX28 uses this to detect READY. */ @@ -169,4 +224,104 @@ #define HW_GPMI_DEBUG 0x000000c0 #define MX23_BP_GPMI_DEBUG_READY0 28 #define MX23_BM_GPMI_DEBUG_READY0 (1 << MX23_BP_GPMI_DEBUG_READY0) + +#define HW_GPMI_READ_DDR_DLL_CTRL (0x00000100) + +#define BP_GPMI_READ_DDR_DLL_CTRL_REF_UPDATE_INT 28 +#define BM_GPMI_READ_DDR_DLL_CTRL_REF_UPDATE_INT 0xF0000000 +#define BF_GPMI_READ_DDR_DLL_CTRL_REF_UPDATE_INT(v) \ + (((v) << 28) & BM_GPMI_READ_DDR_DLL_CTRL_REF_UPDATE_INT) +#define BP_GPMI_READ_DDR_DLL_CTRL_SLV_UPDATE_INT 20 +#define BM_GPMI_READ_DDR_DLL_CTRL_SLV_UPDATE_INT 0x0FF00000 +#define BF_GPMI_READ_DDR_DLL_CTRL_SLV_UPDATE_INT(v) \ + (((v) << 20) & BM_GPMI_READ_DDR_DLL_CTRL_SLV_UPDATE_INT) +#define BP_GPMI_READ_DDR_DLL_CTRL_RSVD1 18 +#define BM_GPMI_READ_DDR_DLL_CTRL_RSVD1 0x000C0000 +#define BF_GPMI_READ_DDR_DLL_CTRL_RSVD1(v) \ + (((v) << 18) & BM_GPMI_READ_DDR_DLL_CTRL_RSVD1) +#define BP_GPMI_READ_DDR_DLL_CTRL_SLV_OVERRIDE_VAL 10 +#define BM_GPMI_READ_DDR_DLL_CTRL_SLV_OVERRIDE_VAL 0x0003FC00 +#define BF_GPMI_READ_DDR_DLL_CTRL_SLV_OVERRIDE_VAL(v) \ + (((v) << 10) & BM_GPMI_READ_DDR_DLL_CTRL_SLV_OVERRIDE_VAL) +#define BM_GPMI_READ_DDR_DLL_CTRL_SLV_OVERRIDE 0x00000200 +#define BM_GPMI_READ_DDR_DLL_CTRL_REFCLK_ON 0x00000100 +#define BM_GPMI_READ_DDR_DLL_CTRL_GATE_UPDATE 0x00000080 +#define BP_GPMI_READ_DDR_DLL_CTRL_SLV_DLY_TARGET 3 +#define BM_GPMI_READ_DDR_DLL_CTRL_SLV_DLY_TARGET 0x00000078 +#define BF_GPMI_READ_DDR_DLL_CTRL_SLV_DLY_TARGET(v) \ + (((v) << 3) & BM_GPMI_READ_DDR_DLL_CTRL_SLV_DLY_TARGET) +#define BM_GPMI_READ_DDR_DLL_CTRL_SLV_FORCE_UPD 0x00000004 +#define BM_GPMI_READ_DDR_DLL_CTRL_RESET 0x00000002 +#define BM_GPMI_READ_DDR_DLL_CTRL_ENABLE 0x00000001 + +#define HW_GPMI_WRITE_DDR_DLL_CTRL (0x00000110) + +#define BP_GPMI_WRITE_DDR_DLL_CTRL_REF_UPDATE_INT 28 +#define BM_GPMI_WRITE_DDR_DLL_CTRL_REF_UPDATE_INT 0xF0000000 +#define BF_GPMI_WRITE_DDR_DLL_CTRL_REF_UPDATE_INT(v) \ + (((v) << 28) & BM_GPMI_WRITE_DDR_DLL_CTRL_REF_UPDATE_INT) +#define BP_GPMI_WRITE_DDR_DLL_CTRL_SLV_UPDATE_INT 20 +#define BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_UPDATE_INT 0x0FF00000 +#define BF_GPMI_WRITE_DDR_DLL_CTRL_SLV_UPDATE_INT(v) \ + (((v) << 20) & BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_UPDATE_INT) +#define BP_GPMI_WRITE_DDR_DLL_CTRL_RSVD1 18 +#define BM_GPMI_WRITE_DDR_DLL_CTRL_RSVD1 0x000C0000 +#define BF_GPMI_WRITE_DDR_DLL_CTRL_RSVD1(v) \ + (((v) << 18) & BM_GPMI_WRITE_DDR_DLL_CTRL_RSVD1) +#define BP_GPMI_WRITE_DDR_DLL_CTRL_SLV_OVERRIDE_VAL 10 +#define BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_OVERRIDE_VAL 0x0003FC00 +#define BF_GPMI_WRITE_DDR_DLL_CTRL_SLV_OVERRIDE_VAL(v) \ + (((v) << 10) & BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_OVERRIDE_VAL) +#define BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_OVERRIDE 0x00000200 +#define BM_GPMI_WRITE_DDR_DLL_CTRL_REFCLK_ON 0x00000100 +#define BM_GPMI_WRITE_DDR_DLL_CTRL_GATE_UPDATE 0x00000080 +#define BP_GPMI_WRITE_DDR_DLL_CTRL_SLV_DLY_TARGET 3 +#define BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_DLY_TARGET 0x00000078 +#define BF_GPMI_WRITE_DDR_DLL_CTRL_SLV_DLY_TARGET(v) \ + (((v) << 3) & BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_DLY_TARGET) +#define BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_FORCE_UPD 0x00000004 +#define BM_GPMI_WRITE_DDR_DLL_CTRL_RESET 0x00000002 +#define BM_GPMI_WRITE_DDR_DLL_CTRL_ENABLE 0x00000001 + +#define HW_GPMI_READ_DDR_DLL_STS (0x00000120) + +#define BP_GPMI_READ_DDR_DLL_STS_RSVD1 25 +#define BM_GPMI_READ_DDR_DLL_STS_RSVD1 0xFE000000 +#define BF_GPMI_READ_DDR_DLL_STS_RSVD1(v) \ + (((v) << 25) & BM_GPMI_READ_DDR_DLL_STS_RSVD1) +#define BP_GPMI_READ_DDR_DLL_STS_REF_SEL 17 +#define BM_GPMI_READ_DDR_DLL_STS_REF_SEL 0x01FE0000 +#define BF_GPMI_READ_DDR_DLL_STS_REF_SEL(v) \ + (((v) << 17) & BM_GPMI_READ_DDR_DLL_STS_REF_SEL) +#define BM_GPMI_READ_DDR_DLL_STS_REF_LOCK 0x00010000 +#define BP_GPMI_READ_DDR_DLL_STS_RSVD0 9 +#define BM_GPMI_READ_DDR_DLL_STS_RSVD0 0x0000FE00 +#define BF_GPMI_READ_DDR_DLL_STS_RSVD0(v) \ + (((v) << 9) & BM_GPMI_READ_DDR_DLL_STS_RSVD0) +#define BP_GPMI_READ_DDR_DLL_STS_SLV_SEL 1 +#define BM_GPMI_READ_DDR_DLL_STS_SLV_SEL 0x000001FE +#define BF_GPMI_READ_DDR_DLL_STS_SLV_SEL(v) \ + (((v) << 1) & BM_GPMI_READ_DDR_DLL_STS_SLV_SEL) +#define BM_GPMI_READ_DDR_DLL_STS_SLV_LOCK 0x00000001 + +#define HW_GPMI_WRITE_DDR_DLL_STS (0x00000130) + +#define BP_GPMI_WRITE_DDR_DLL_STS_RSVD1 25 +#define BM_GPMI_WRITE_DDR_DLL_STS_RSVD1 0xFE000000 +#define BF_GPMI_WRITE_DDR_DLL_STS_RSVD1(v) \ + (((v) << 25) & BM_GPMI_WRITE_DDR_DLL_STS_RSVD1) +#define BP_GPMI_WRITE_DDR_DLL_STS_REF_SEL 17 +#define BM_GPMI_WRITE_DDR_DLL_STS_REF_SEL 0x01FE0000 +#define BF_GPMI_WRITE_DDR_DLL_STS_REF_SEL(v) \ + (((v) << 17) & BM_GPMI_WRITE_DDR_DLL_STS_REF_SEL) +#define BM_GPMI_WRITE_DDR_DLL_STS_REF_LOCK 0x00010000 +#define BP_GPMI_WRITE_DDR_DLL_STS_RSVD0 9 +#define BM_GPMI_WRITE_DDR_DLL_STS_RSVD0 0x0000FE00 +#define BF_GPMI_WRITE_DDR_DLL_STS_RSVD0(v) \ + (((v) << 9) & BM_GPMI_WRITE_DDR_DLL_STS_RSVD0) +#define BP_GPMI_WRITE_DDR_DLL_STS_SLV_SEL 1 +#define BM_GPMI_WRITE_DDR_DLL_STS_SLV_SEL 0x000001FE +#define BF_GPMI_WRITE_DDR_DLL_STS_SLV_SEL(v) \ + (((v) << 1) & BM_GPMI_WRITE_DDR_DLL_STS_SLV_SEL) +#define BM_GPMI_WRITE_DDR_DLL_STS_SLV_LOCK 0x00000001 #endif diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c index c7d37df019ed..46a9645fcf25 100644 --- a/drivers/mxc/ipu3/ipu_device.c +++ b/drivers/mxc/ipu3/ipu_device.c @@ -371,13 +371,10 @@ static bool deinterlace_3_field(struct ipu_task_entry *t) static u32 tiled_filed_size(struct ipu_task_entry *t) { - u32 y_size; u32 field_size; /* note: page_align is required by VPU hw ouput buffer */ - y_size = t->input.width * t->input.height/2; - field_size = ALIGN(y_size, SZ_4K) + ALIGN(y_size/2, SZ_4K); - + field_size = TILED_NV12_FRAME_SIZE(t->input.width, t->input.height/2); return field_size; } diff --git a/drivers/mxc/ipu3/vdoa.c b/drivers/mxc/ipu3/vdoa.c index 1a5266d7d7a8..2d3aaef16622 100644 --- a/drivers/mxc/ipu3/vdoa.c +++ b/drivers/mxc/ipu3/vdoa.c @@ -308,6 +308,7 @@ int vdoa_start(vdoa_handle_t handle, int timeout_ms) CHECK_NULL_PTR(vdoa); CHECK_STATE(VDOA_GET_OBUF, return -EINVAL); + vdoa->state = VDOA_START; init_completion(&vdoa->comp); vdoa_write_register(vdoa, VDOAIST, VDOAIEIST_TRANSFER_ERR | VDOAIEIST_TRANSFER_END); @@ -318,7 +319,6 @@ int vdoa_start(vdoa_handle_t handle, int timeout_ms) vdoa_write_register(vdoa, VDOASRR, VDOASRR_START_XFER); dump_registers(vdoa); - vdoa->state = VDOA_START; ret = wait_for_completion_timeout(&vdoa->comp, msecs_to_jiffies(timeout_ms)); diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index 2051e8691a64..41470e954938 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -609,6 +609,9 @@ static int snvs_rtc_probe(struct platform_device *pdev) tv.tv_nsec = 0; tv.tv_sec = rtc_read_lp_counter(ioaddr + SNVS_LPSRTCMR); + /* Remove can_wakeup flag to add common power wakeup interface */ + pdev->dev.power.can_wakeup = 0; + /* By default, devices should wakeup if they can */ /* So snvs is set as "should wakeup" as it can */ device_init_wakeup(&pdev->dev, 1); diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index eba760095956..be58e17dc526 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2004-2007, 2012 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright (C) 2008 Juergen Beisert * * This program is free software; you can redistribute it and/or @@ -652,6 +652,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); struct spi_imx_config config; + clk_enable(spi_imx->clk); config.bpw = t ? t->bits_per_word : spi->bits_per_word; config.speed_hz = t ? t->speed_hz : spi->max_speed_hz; config.mode = spi->mode; @@ -678,7 +679,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, BUG(); spi_imx->devtype_data.config(spi_imx, &config); - + clk_disable(spi_imx->clk); return 0; } @@ -687,6 +688,7 @@ static int spi_imx_transfer(struct spi_device *spi, { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); + clk_enable(spi_imx->clk); spi_imx->tx_buf = transfer->tx_buf; spi_imx->rx_buf = transfer->rx_buf; spi_imx->count = transfer->len; @@ -699,6 +701,7 @@ static int spi_imx_transfer(struct spi_device *spi, spi_imx->devtype_data.intctrl(spi_imx, MXC_INT_TE); wait_for_completion(&spi_imx->xfer_done); + clk_disable(spi_imx->clk); return transfer->len; } @@ -863,12 +866,12 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) spi_imx->devtype_data.reset(spi_imx); spi_imx->devtype_data.intctrl(spi_imx, 0); - ret = spi_bitbang_start(&spi_imx->bitbang); if (ret) { dev_err(&pdev->dev, "bitbang start failed with %d\n", ret); goto out_clk_put; } + clk_disable(spi_imx->clk); dev_info(&pdev->dev, "probed\n"); @@ -902,7 +905,7 @@ static int __devexit spi_imx_remove(struct platform_device *pdev) int i; spi_bitbang_stop(&spi_imx->bitbang); - + clk_enable(spi_imx->clk); writel(0, spi_imx->base + MXC_CSPICTRL); clk_disable(spi_imx->clk); clk_put(spi_imx->clk); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index f8cd9bd076ce..d65c589cef60 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -881,7 +881,7 @@ static int imx_uart_dma_init(struct imx_port *sport) goto err; } - slave_config.direction = DMA_FROM_DEVICE; + slave_config.direction = DMA_DEV_TO_MEM; slave_config.src_addr = sport->port.mapbase + URXD0; slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; slave_config.src_maxburst = RXTL; /* fix me */ @@ -908,7 +908,7 @@ static int imx_uart_dma_init(struct imx_port *sport) goto err; } - slave_config.direction = DMA_TO_DEVICE; + slave_config.direction = DMA_MEM_TO_DEV; slave_config.dst_addr = sport->port.mapbase + URTX0; slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; slave_config.dst_maxburst = TXTL; /* fix me */ diff --git a/drivers/usb/gadget/arcotg_udc.c b/drivers/usb/gadget/arcotg_udc.c index 5a1aab94b405..ea81802a3ca6 100755 --- a/drivers/usb/gadget/arcotg_udc.c +++ b/drivers/usb/gadget/arcotg_udc.c @@ -3458,7 +3458,11 @@ end: dr_clk_gate(false); } - --udc_controller->suspended; + + if (!(--udc_controller->suspended) && !udc_controller->stopped) { + dr_clk_gate(true); + dr_phy_low_power_mode(udc_controller, false); + } enable_irq(udc_controller->irq); mutex_unlock(&udc_resume_mutex); printk(KERN_DEBUG "USB Gadget resume ends\n"); diff --git a/drivers/usb/gadget/arcotg_udc.h b/drivers/usb/gadget/arcotg_udc.h index 00c8e8a8cdd7..8f4b88ebc283 100755 --- a/drivers/usb/gadget/arcotg_udc.h +++ b/drivers/usb/gadget/arcotg_udc.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2009-2012 Freescale Semiconductor, Inc. All Rights Reserved. * * 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 @@ -46,11 +46,7 @@ #define NEED_IRAM(ep) ((g_iram_size) && \ ((ep)->desc->bmAttributes == USB_ENDPOINT_XFER_BULK)) -#ifdef CONFIG_ARCH_MX5 #define POSTPONE_FREE_LAST_DTD -#else -#undef POSTPONE_FREE_LAST_DTD -#endif /* ### define USB registers here */ diff --git a/drivers/video/mxc/mxc_epdc_fb.c b/drivers/video/mxc/mxc_epdc_fb.c index 5bc048d216be..da77373e03a6 100644 --- a/drivers/video/mxc/mxc_epdc_fb.c +++ b/drivers/video/mxc/mxc_epdc_fb.c @@ -4726,9 +4726,15 @@ out_copybuffer: fb_data->phys_addr_copybuf); out_upd_buffers: for (i = 0; i < fb_data->max_num_buffers; i++) - dma_free_writecombine(&pdev->dev, fb_data->max_pix_size, - fb_data->virt_addr_updbuf[i], - fb_data->phys_addr_updbuf[i]); + if (fb_data->virt_addr_updbuf[i] != NULL) + dma_free_writecombine(&pdev->dev, + fb_data->max_pix_size, + fb_data->virt_addr_updbuf[i], + fb_data->phys_addr_updbuf[i]); + if (fb_data->virt_addr_updbuf != NULL) + kfree(fb_data->virt_addr_updbuf); + if (fb_data->phys_addr_updbuf != NULL) + kfree(fb_data->phys_addr_updbuf); out_upd_lists: list_for_each_entry_safe(plist, temp_list, &fb_data->upd_buf_free_list, list) { @@ -4753,6 +4759,7 @@ static int mxc_epdc_fb_remove(struct platform_device *pdev) { struct update_data_list *plist, *temp_list; struct mxc_epdc_fb_data *fb_data = platform_get_drvdata(pdev); + int i; mxc_epdc_fb_blank(FB_BLANK_POWERDOWN, &fb_data->info); @@ -4766,6 +4773,16 @@ static int mxc_epdc_fb_remove(struct platform_device *pdev) unregister_framebuffer(&fb_data->info); free_irq(fb_data->epdc_irq, fb_data); + for (i = 0; i < fb_data->max_num_buffers; i++) + if (fb_data->virt_addr_updbuf[i] != NULL) + dma_free_writecombine(&pdev->dev, fb_data->max_pix_size, + fb_data->virt_addr_updbuf[i], + fb_data->phys_addr_updbuf[i]); + if (fb_data->virt_addr_updbuf != NULL) + kfree(fb_data->virt_addr_updbuf); + if (fb_data->phys_addr_updbuf != NULL) + kfree(fb_data->phys_addr_updbuf); + dma_free_writecombine(&pdev->dev, fb_data->working_buffer_size, fb_data->working_buffer_virt, fb_data->working_buffer_phys); @@ -4780,9 +4797,6 @@ static int mxc_epdc_fb_remove(struct platform_device *pdev) list_for_each_entry_safe(plist, temp_list, &fb_data->upd_buf_free_list, list) { list_del(&plist->list); - dma_free_writecombine(&pdev->dev, fb_data->max_pix_size, - plist->virt_addr, - plist->phys_addr); kfree(plist); } #ifdef CONFIG_FB_MXC_EINK_AUTO_UPDATE_MODE diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c index 19d15df0d901..64a92316f974 100644 --- a/drivers/video/mxc_hdmi.c +++ b/drivers/video/mxc_hdmi.c @@ -570,6 +570,10 @@ static void hdmi_video_packetize(struct mxc_hdmi *hdmi) } else return; + if (!hdmi->edid_cfg.vsd_dc_48bit && !hdmi->edid_cfg.vsd_dc_36bit && + !hdmi->edid_cfg.vsd_dc_30bit && !hdmi->edid_cfg.vsd_dc_y444) + color_depth = 0; + /* set the packetizer registers */ val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) & HDMI_VP_PR_CD_COLOR_DEPTH_MASK) | diff --git a/firmware/imx/sdma/sdma-imx6q-to1.bin.ihex b/firmware/imx/sdma/sdma-imx6q-to1.bin.ihex index f9ef5944601f..b96992873ace 100644 --- a/firmware/imx/sdma/sdma-imx6q-to1.bin.ihex +++ b/firmware/imx/sdma/sdma-imx6q-to1.bin.ihex @@ -1,99 +1,100 @@ :1000000053444D4101000000010000001C000000AD -:1000100023000000A8000000780500008202000014 +:1000100025000000B000000078050000820200000A :10002000FFFFFFFF00000000FFFFFFFFFFFFFFFFDC :10003000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0 -:10004000FFFFFFFFB61800006A1A00004B04000013 -:10005000EB0200004A190000FF180000080400002D -:1000600095040000C0030000C105000070050000F9 -:1000700009040000AB020000E30400007B03000061 +:10004000FFFFFFFFFFFFFFFF6A1A0000FFFFFFFF38 +:10005000EB020000BB180000FFFFFFFF08040000D8 +:10006000FFFFFFFFC0030000FFFFFFFFFFFFFFFFD9 +:10007000FFFFFFFFAB020000FFFFFFFF7B0300005D :10008000FFFFFFFFFFFFFFFF4C0400006E040000B6 :10009000FFFFFFFF00180000FFFFFFFFFFFFFFFF54 -:1000A0000000000000180000E3C1DB57E35FE357E6 -:1000B000F352016A8F00D500017D8D00A005EB5D34 -:1000C0007804037D79042C7D367C79041F7CEE5600 -:1000D000000F6006057D0965437E0A62417E209817 -:1000E0000A623E7E09653C7E12051205AD0260077C -:1000F000037DFB55D36D2B98FB55041DD36DC86A4A -:100100002F7F011F03200048E47C5398FB55D76DD7 -:10011000150005780962C86A0962C86AD76D5298E5 -:10012000FB55D76D1500150005780A62C86A0A628A -:10013000C86AD76D5298FB55D76D1500150015008C -:1001400005780B62C86A0B62C86AD76D097CDF6DDF -:10015000077F0000EB55004D077DFAC1E357069875 -:100160000700CC680C6813C20AC20398D9C1E3C166 -:10017000DB57E35FE357F352216A8F00D500017D1F -:100180008D00A005EB5DFB567804037D79042A7D84 -:10019000317C7904207C700B1103EB53000F60035A -:1001A000057D0965377E0A62357E86980A62327E51 -:1001B0000965307E12051205AD026007027C065A01 -:1001C0008E98265A277F011F03200048E87C700B79 -:1001D00011031353AF98150004780962065A096297 -:1001E000265AAE981500150004780A62065A0A626B -:1001F000265AAE9815001500150004780B62065AB1 -:100200000B62265A077C0000EB55004D067DFAC1B3 -:10021000E357699807000C6813C20AC26698700B0E -:10022000110313536C07017CD9C1FB5E8A066B076F -:10023000017CD9C1F35EDB59D3588F0110010F390E -:100240008B003CC12B7DC05AC85B4EC1277C880304 -:100250008906E35CFF0D1105FF1DBC053E07004D3F -:10026000187D700811007E07097D7D07027D2852E8 -:10027000E698F852DB54BC02CC02097C7C07027D74 -:100280002852EF98F852D354BC02CC02097D0004E6 -:10029000DD988B00C052C85359C1D67D0002CD985D -:1002A000FF08BF007F07157D8804D500017D8D0004 -:1002B000A005EB5D8F0212021202FF3ADA05027C02 -:1002C0003E071899A402DD02027D3E0718995E07D9 -:1002D0001899EB559805EB5DF352FB546A07267DA0 -:1002E0006C07017D55996B07577C6907047D68078A -:1002F000027D010E2F999358D600017D8E009355F3 -:10030000A005935DA00602780255045D1D7C004E99 -:10031000087C6907037D0255177E3C99045D147FB4 -:10032000890693500048017D2799A0991500067809 -:100330000255045D4F070255245D2F07017CA099EB -:1003400017006F07017C012093559D000700A7D976 -:10035000F598D36C6907047D6807027D010E6499E6 -:100360009358D600017D8E009355A005935DA0069D -:1003700002780255C86D0F7C004E087C6907037D2A -:100380000255097E7199C86D067F89069350004811 -:10039000017D5C99A0999A99C36A6907047D6807F1 -:1003A000027D010E87999358D600017D8E009355EA -:1003B000A005935DA0060278C865045D0F7C004E21 -:1003C000087C6907037DC865097E9499045D067FF2 -:1003D000890693500048017D7F99A09993559D000F -:1003E0000700FF6CA7D9F5980000E354EB55004DCA -:1003F000017CF598DD98E354EB55FF0A1102FF1AD2 -:100400007F07027CA005B4999D008C05BA05A00564 -:100410001002BA04AD0454040600E3C1DB57FB52DA -:10042000C36AF352056A8F00D500017D8D00A005D7 -:10043000EB5D7804037D79042B7D1E7C7904337C8D -:10044000EE56000FFB556007027DC36DD599041D64 -:10045000C36DC8623B7E6006027D10021202096A0B -:10046000357F1202096A327F1202096A2F7F011F4B -:1004700003200048E77C099AFB55C76D150015005D -:1004800015000578C8620B6AC8620B6AC76D089AC6 -:10049000FB55C76D150015000578C8620A6AC86269 -:1004A0000A6AC76D089AFB55C76D15000578C862C2 -:1004B000096AC862096AC76D097C286A077F00005B -:1004C000EB55004D057DFAC1DB57BF9977C2540447 -:1004D0000AC2BA99D9C1E3C1DB57F352056A8F004A -:1004E000D500017D8D00A005FB567804037D7904BD -:1004F000297D1F7C79042E7CE35D700D1105ED557F -:10050000000F6007027D0652329A2652337E600544 -:10051000027D10021202096A2D7F1202096A2A7FE7 -:100520001202096A277F011F03200048EA7CE35575 -:100530005D9A150015001500047806520B6A2652C4 -:100540000B6A5C9A15001500047806520A6A265256 -:100550000A6A5C9A150004780652096A2652096AEA -:10056000097C286A077F0000DB57004D057DFAC132 -:10057000DB571B9A77C254040AC2189AE3C1DB57AF -:10058000F352056AFB568E02941AC36AC862690266 -:10059000247D941EC36ED36EC8624802C86A942636 -:1005A000981EC36ED36EC8624C02C86A9826C36E8A -:1005B000981EC36EC8629826C36E6002097CC8622A -:1005C0006E02247D096A1E7F0125004D257D849AD7 -:1005D000286A187F04627AC2B89AE36E8F00D80541 -:1005E000017D8D00A005C8626E02107D096A0A7F38 -:1005F0000120F97C286A067F0000004D0D7DFAC1BC -:10060000DB576E9A070004620C6AB59A286AFA7F73 -:1006100004627AC258045404286AF47F0AC26B9AAE +:1000A000000000000018000062180000161A00008E +:1000B000E3C1DB57E35FE357F352016A8F00D500DA +:1000C000017D8D00A005EB5D7804037D79042C7D16 +:1000D000367C79041F7CEE56000F6006057D0965AD +:1000E000437E0A62417E20980A623E7E09653C7E1C +:1000F00012051205AD026007037DFB55D36D2B98E9 +:10010000FB55041DD36DC86A2F7F011F03200048D3 +:10011000E47C5398FB55D76D150005780962C86AD1 +:100120000962C86AD76D5298FB55D76D1500150046 +:1001300005780A62C86A0A62C86AD76D5298FB5588 +:10014000D76D15001500150005780B62C86A0B62A3 +:10015000C86AD76D097CDF6D077F0000EB55004D45 +:10016000077DFAC1E35706980700CC680C6813C2F4 +:100170000AC20398D9C1E3C1DB57E35FE357F352E7 +:10018000216A8F00D500017D8D00A005EB5DFB5637 +:100190007804037D79042A7D317C7904207C700BFE +:1001A0001103EB53000F6003057D0965377E0A627A +:1001B000357E86980A62327E0965307E1205120508 +:1001C000AD026007027C065A8E98265A277F011FCF +:1001D00003200048E87C700B11031353AF981500FF +:1001E00004780962065A0962265AAE98150015006D +:1001F00004780A62065A0A62265AAE98150015005B +:10020000150004780B62065A0B62265A077C000020 +:10021000EB55004D067DFAC1E357699807000C685D +:1002200013C20AC26698700B110313536C07017C4A +:10023000D9C1FB5E8A066B07017CD9C1F35EDB592D +:10024000D3588F0110010F398B003CC12B7DC05A50 +:10025000C85B4EC1277C88038906E35CFF0D11054E +:10026000FF1DBC053E07004D187D700811007E077C +:10027000097D7D07027D2852E698F852DB54BC02C6 +:10028000CC02097C7C07027D2852EF98F852D354A7 +:10029000BC02CC02097D0004DD988B00C052C8531B +:1002A00059C1D67D0002CD98FF08BF007F07157D9C +:1002B0008804D500017D8D00A005EB5D8F02120240 +:1002C0001202FF3ADA05027C3E071899A402DD0209 +:1002D000027D3E0718995E071899EB559805EB5D6E +:1002E000F352FB546A07267D6C07017D55996B0715 +:1002F000577C6907047D6807027D010E2F9993588A +:10030000D600017D8E009355A005935DA00602786E +:100310000255045D1D7C004E087C6907037D025573 +:10032000177E3C99045D147F890693500048017D37 +:100330002799A099150006780255045D4F070255CC +:10034000245D2F07017CA09917006F07017C012015 +:1003500093559D000700A7D9F598D36C6907047DD4 +:100360006807027D010E64999358D600017D8E00C6 +:100370009355A005935DA00602780255C86D0F7CC9 +:10038000004E087C6907037D0255097E7199C86D8E +:10039000067F890693500048017D5C99A0999A993F +:1003A000C36A6907047D6807027D010E8799935827 +:1003B000D600017D8E009355A005935DA0060278BE +:1003C000C865045D0F7C004E087C6907037DC86525 +:1003D000097E9499045D067F890693500048017D4B +:1003E0007F99A09993559D000700FF6CA7D9F598B8 +:1003F0000000E354EB55004D017CF598DD98E35483 +:10040000EB55FF0A1102FF1A7F07027CA005B49981 +:100410009D008C05BA05A0051002BA04AD04540471 +:100420000600E3C1DB57FB52C36AF352056A8F0033 +:10043000D500017D8D00A005EB5D7804037D790476 +:100440002B7D1E7C7904337CEE56000FFB55600734 +:10045000027DC36DD599041DC36DC8623B7E6006E5 +:10046000027D10021202096A357F1202096A327F88 +:100470001202096A2F7F011F03200048E77C099AB6 +:10048000FB55C76D1500150015000578C8620B6A8D +:10049000C8620B6AC76D089AFB55C76D1500150039 +:1004A0000578C8620A6AC8620A6AC76D089AFB556D +:1004B000C76D15000578C862096AC862096AC76D08 +:1004C000097C286A077F0000EB55004D057DFAC1C5 +:1004D000DB57BF9977C254040AC2BA99D9C1E3C1A4 +:1004E000DB57F352056A8F00D500017D8D00A00512 +:1004F000FB567804037D7904297D1F7C79042E7CCA +:10050000E35D700D1105ED55000F6007027D065289 +:10051000329A2652337E6005027D10021202096A69 +:100520002D7F1202096A2A7F1202096A277F011FA2 +:1005300003200048EA7CE3555D9A1500150015007C +:10054000047806520B6A26520B6A5C9A1500150055 +:10055000047806520A6A26520A6A5C9A15000478E0 +:100560000652096A2652096A097C286A077F000038 +:10057000DB57004D057DFAC1DB571B9A77C2540447 +:100580000AC2189AE3C1DB57F352056AFB568E0282 +:10059000941AC36AC8626902247D941EC36ED36E26 +:1005A000C8624802C86A9426981EC36ED36EC86299 +:1005B0004C02C86A9826C36E981EC36EC8629826FD +:1005C000C36E6002097CC8626E02247D096A1E7FC8 +:1005D0000125004D257D849A286A187F04627AC21D +:1005E000B89AE36E8F00D805017D8D00A005C86222 +:1005F0006E02107D096A0A7F0120F97C286A067F55 +:100600000000004D0D7DFAC1DB576E9A07000462B1 +:100610000C6AB59A286AFA7F04627AC258045404B4 +:08062000286AF47F0AC26B9AFC :00000001FF diff --git a/include/linux/ipu.h b/include/linux/ipu.h index 973cfe9b45a5..e0c9e90c2804 100644 --- a/include/linux/ipu.h +++ b/include/linux/ipu.h @@ -141,6 +141,8 @@ typedef enum { #define IPU_PIX_FMT_YUV422P fourcc('4', '2', '2', 'P') /*!< 16 YUV 4:2:2 */ /*! @} */ #define IPU_PIX_FMT_TILED_NV12_MBALIGN (16) +#define TILED_NV12_FRAME_SIZE(w, h) \ + (ALIGN((w) * (h), SZ_4K) + ALIGN((w) * (h) / 2, SZ_4K)) /* IPU device */ typedef enum { RGB_CS, diff --git a/include/linux/mfd/mxc-hdmi-core.h b/include/linux/mfd/mxc-hdmi-core.h index 0b241838f1b9..8681053d231f 100644 --- a/include/linux/mfd/mxc-hdmi-core.h +++ b/include/linux/mfd/mxc-hdmi-core.h @@ -37,6 +37,7 @@ void hdmi_irq_enable(int irq); unsigned int hdmi_irq_disable(int irq); void hdmi_set_sample_rate(unsigned int rate); +void hdmi_set_dma_mode(unsigned int dma_running); void hdmi_init_clk_regenerator(void); void hdmi_clk_regenerator_update_pixel_clock(void); diff --git a/include/linux/mtd/gpmi-nand.h b/include/linux/mtd/gpmi-nand.h index 6659446591ec..e4b52f281df5 100644 --- a/include/linux/mtd/gpmi-nand.h +++ b/include/linux/mtd/gpmi-nand.h @@ -66,5 +66,6 @@ struct gpmi_nand_platform_data { struct mtd_partition *partitions; unsigned partition_count; unsigned int enable_bbt:1; + unsigned int enable_ddr:1; }; #endif diff --git a/include/media/radio-si4763.h b/include/media/radio-si4763.h new file mode 100644 index 000000000000..e7abac285c14 --- /dev/null +++ b/include/media/radio-si4763.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2008-2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +#ifndef RADIO_SI4763_H +#define RADIO_SI4763_H + +#include <linux/i2c.h> + +#define SI4763_NAME "radio-si4763" + +/* + * Platform dependent definition + */ +struct radio_si4763_platform_data { + int i2c_bus; + struct i2c_board_info *subdev_board_info; +}; + +#endif /* ifndef RADIO_SI4763_H*/ diff --git a/include/media/si4763.h b/include/media/si4763.h new file mode 100644 index 000000000000..494542e15c36 --- /dev/null +++ b/include/media/si4763.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008-2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef SI4713_H +#define SI4713_H + +/* The SI4713 I2C sensor chip has a fixed slave address of 0xc6 or 0x22. */ +#define SI4713_I2C_ADDR_BUSEN_HIGH 0x63 +#define SI4713_I2C_ADDR_BUSEN_LOW 0x11 + +/* + * Platform dependent definition + */ +struct si4713_platform_data { + /* Set power state, zero is off, non-zero is on. */ + int (*set_power)(int power); +}; + +/* + * Structure to query for Received Noise Level (RNL). + */ +struct si4713_rnl { + __u32 index; /* modulator index */ + __u32 frequency; /* frequency to peform rnl measurement */ + __s32 rnl; /* result of measurement in dBuV */ + __u32 reserved[4]; /* drivers and apps must init this to 0 */ +}; + +/* + * This is the ioctl number to query for rnl. Users must pass a + * struct si4713_rnl pointer specifying desired frequency in 'frequency' field + * following driver capabilities (i.e V4L2_TUNER_CAP_LOW). + * Driver must return measured value in the same struture, filling 'rnl' field. + */ +#define SI4713_IOC_MEASURE_RNL _IOWR('V', BASE_VIDIOC_PRIVATE + 0, \ + struct si4713_rnl) + +#endif /* ifndef SI4713_H*/ diff --git a/kernel/futex.c b/kernel/futex.c index e250f004c6e3..5e3a534754d7 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -59,6 +59,7 @@ #include <linux/magic.h> #include <linux/pid.h> #include <linux/nsproxy.h> +#include <linux/ptrace.h> #include <asm/futex.h> @@ -2443,40 +2444,29 @@ SYSCALL_DEFINE3(get_robust_list, int, pid, { struct robust_list_head __user *head; unsigned long ret; - const struct cred *cred = current_cred(), *pcred; + struct task_struct *p; if (!futex_cmpxchg_enabled) return -ENOSYS; + rcu_read_lock(); + + ret = -ESRCH; if (!pid) - head = current->robust_list; + p = current; else { - struct task_struct *p; - - ret = -ESRCH; - rcu_read_lock(); p = find_task_by_vpid(pid); if (!p) goto err_unlock; - ret = -EPERM; - pcred = __task_cred(p); - /* If victim is in different user_ns, then uids are not - comparable, so we must have CAP_SYS_PTRACE */ - if (cred->user->user_ns != pcred->user->user_ns) { - if (!ns_capable(pcred->user->user_ns, CAP_SYS_PTRACE)) - goto err_unlock; - goto ok; - } - /* If victim is in same user_ns, then uids are comparable */ - if (cred->euid != pcred->euid && - cred->euid != pcred->uid && - !ns_capable(pcred->user->user_ns, CAP_SYS_PTRACE)) - goto err_unlock; -ok: - head = p->robust_list; - rcu_read_unlock(); } + ret = -EPERM; + if (!ptrace_may_access(p, PTRACE_MODE_READ)) + goto err_unlock; + + head = p->robust_list; + rcu_read_unlock(); + if (put_user(sizeof(*head), len_ptr)) return -EFAULT; return put_user(head, head_ptr); @@ -2628,7 +2618,7 @@ void exit_robust_list(struct task_struct *curr) long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout, u32 __user *uaddr2, u32 val2, u32 val3) { - int ret = -ENOSYS, cmd = op & FUTEX_CMD_MASK; + int cmd = op & FUTEX_CMD_MASK; unsigned int flags = 0; if (!(op & FUTEX_PRIVATE_FLAG)) @@ -2641,49 +2631,44 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout, } switch (cmd) { + case FUTEX_LOCK_PI: + case FUTEX_UNLOCK_PI: + case FUTEX_TRYLOCK_PI: + case FUTEX_WAIT_REQUEUE_PI: + case FUTEX_CMP_REQUEUE_PI: + if (!futex_cmpxchg_enabled) + return -ENOSYS; + } + + switch (cmd) { case FUTEX_WAIT: val3 = FUTEX_BITSET_MATCH_ANY; case FUTEX_WAIT_BITSET: - ret = futex_wait(uaddr, flags, val, timeout, val3); - break; + return futex_wait(uaddr, flags, val, timeout, val3); case FUTEX_WAKE: val3 = FUTEX_BITSET_MATCH_ANY; case FUTEX_WAKE_BITSET: - ret = futex_wake(uaddr, flags, val, val3); - break; + return futex_wake(uaddr, flags, val, val3); case FUTEX_REQUEUE: - ret = futex_requeue(uaddr, flags, uaddr2, val, val2, NULL, 0); - break; + return futex_requeue(uaddr, flags, uaddr2, val, val2, NULL, 0); case FUTEX_CMP_REQUEUE: - ret = futex_requeue(uaddr, flags, uaddr2, val, val2, &val3, 0); - break; + return futex_requeue(uaddr, flags, uaddr2, val, val2, &val3, 0); case FUTEX_WAKE_OP: - ret = futex_wake_op(uaddr, flags, uaddr2, val, val2, val3); - break; + return futex_wake_op(uaddr, flags, uaddr2, val, val2, val3); case FUTEX_LOCK_PI: - if (futex_cmpxchg_enabled) - ret = futex_lock_pi(uaddr, flags, val, timeout, 0); - break; + return futex_lock_pi(uaddr, flags, val, timeout, 0); case FUTEX_UNLOCK_PI: - if (futex_cmpxchg_enabled) - ret = futex_unlock_pi(uaddr, flags); - break; + return futex_unlock_pi(uaddr, flags); case FUTEX_TRYLOCK_PI: - if (futex_cmpxchg_enabled) - ret = futex_lock_pi(uaddr, flags, 0, timeout, 1); - break; + return futex_lock_pi(uaddr, flags, 0, timeout, 1); case FUTEX_WAIT_REQUEUE_PI: val3 = FUTEX_BITSET_MATCH_ANY; - ret = futex_wait_requeue_pi(uaddr, flags, val, timeout, val3, - uaddr2); - break; + return futex_wait_requeue_pi(uaddr, flags, val, timeout, val3, + uaddr2); case FUTEX_CMP_REQUEUE_PI: - ret = futex_requeue(uaddr, flags, uaddr2, val, val2, &val3, 1); - break; - default: - ret = -ENOSYS; + return futex_requeue(uaddr, flags, uaddr2, val, val2, &val3, 1); } - return ret; + return -ENOSYS; } diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c index 5f9e689dc8f0..a9642d528630 100644 --- a/kernel/futex_compat.c +++ b/kernel/futex_compat.c @@ -10,6 +10,7 @@ #include <linux/compat.h> #include <linux/nsproxy.h> #include <linux/futex.h> +#include <linux/ptrace.h> #include <asm/uaccess.h> @@ -136,40 +137,29 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, { struct compat_robust_list_head __user *head; unsigned long ret; - const struct cred *cred = current_cred(), *pcred; + struct task_struct *p; if (!futex_cmpxchg_enabled) return -ENOSYS; + rcu_read_lock(); + + ret = -ESRCH; if (!pid) - head = current->compat_robust_list; + p = current; else { - struct task_struct *p; - - ret = -ESRCH; - rcu_read_lock(); p = find_task_by_vpid(pid); if (!p) goto err_unlock; - ret = -EPERM; - pcred = __task_cred(p); - /* If victim is in different user_ns, then uids are not - comparable, so we must have CAP_SYS_PTRACE */ - if (cred->user->user_ns != pcred->user->user_ns) { - if (!ns_capable(pcred->user->user_ns, CAP_SYS_PTRACE)) - goto err_unlock; - goto ok; - } - /* If victim is in same user_ns, then uids are comparable */ - if (cred->euid != pcred->euid && - cred->euid != pcred->uid && - !ns_capable(pcred->user->user_ns, CAP_SYS_PTRACE)) - goto err_unlock; -ok: - head = p->compat_robust_list; - rcu_read_unlock(); } + ret = -EPERM; + if (!ptrace_may_access(p, PTRACE_MODE_READ)) + goto err_unlock; + + head = p->compat_robust_list; + rcu_read_unlock(); + if (put_user(sizeof(*head), len_ptr)) return -EFAULT; return put_user(ptr_to_compat(head), head_ptr); diff --git a/sound/soc/codecs/cs42888.c b/sound/soc/codecs/cs42888.c index 86feec2676df..63d244a8d00a 100644 --- a/sound/soc/codecs/cs42888.c +++ b/sound/soc/codecs/cs42888.c @@ -478,11 +478,17 @@ static const struct snd_soc_dapm_route audio_map[] = { { "AOUT4R", NULL, "PWR" }, /* Capture */ - { "ADC1", NULL, "AIN1L" }, - { "ADC1", NULL, "AIN1R" }, + { "PWR", NULL, "AIN1L" }, + { "PWR", NULL, "AIN1R" }, - { "ADC2", NULL, "AIN2L" }, - { "ADC2", NULL, "AIN2R" }, + { "PWR", NULL, "AIN2L" }, + { "PWR", NULL, "AIN2R" }, + + { "ADC1", NULL, "PWR" }, + { "ADC1", NULL, "PWR" }, + + { "ADC2", NULL, "PWR" }, + { "ADC2", NULL, "PWR" }, }; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b8be06b3344a..824a6fa8f487 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3520,7 +3520,8 @@ static int wm8962_mute(struct snd_soc_dai *dai, int mute) WM8962_DAC_MUTE, val); } -#define WM8962_RATES SNDRV_PCM_RATE_8000_96000 +#define WM8962_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) #define WM8962_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/imx/imx-esai.c b/sound/soc/imx/imx-esai.c index f6361716c20c..5f95ecce842e 100644 --- a/sound/soc/imx/imx-esai.c +++ b/sound/soc/imx/imx-esai.c @@ -284,12 +284,10 @@ static int imx_esai_startup(struct snd_pcm_substream *substream, writel(ESAI_GPIO_ESAI, esai->base + ESAI_PCRC); } - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) local_esai->imx_esai_txrx_state |= IMX_DAI_ESAI_TX; - } else { + else local_esai->imx_esai_txrx_state |= IMX_DAI_ESAI_RX; - writel(ESAI_RCR_RPR, esai->base + ESAI_RCR); - } ESAI_DUMP(); return 0; @@ -375,6 +373,14 @@ static int imx_esai_hw_rx_params(struct snd_pcm_substream *substream, rfcr |= ESAI_WORD_LEN_16; rcr |= ESAI_RCR_RSHFD_MSB | ESAI_RCR_RSWS_STL32_WDL16; break; + case SNDRV_PCM_FORMAT_S20_3LE: + rfcr |= ESAI_WORD_LEN_20; + rcr |= ESAI_RCR_RSHFD_MSB | ESAI_RCR_RSWS_STL32_WDL20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + rfcr |= ESAI_WORD_LEN_24; + rcr |= ESAI_RCR_RSHFD_MSB | ESAI_RCR_RSWS_STL32_WDL24; + break; } channels = params_channels(params); @@ -438,7 +444,6 @@ static int imx_esai_trigger(struct snd_pcm_substream *substream, int cmd, { struct imx_esai *esai = snd_soc_dai_get_drvdata(cpu_dai); u32 reg, tfcr = 0, rfcr = 0; - u32 temp; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { tfcr = readl(esai->base + ESAI_TFCR); @@ -457,12 +462,8 @@ static int imx_esai_trigger(struct snd_pcm_substream *substream, int cmd, reg |= ESAI_TCR_TE(substream->runtime->channels); writel(reg, esai->base + ESAI_TCR); } else { - temp = readl(esai->base + ESAI_TCR); - temp &= ~ESAI_TCR_TPR; - writel(temp, esai->base + ESAI_TCR); rfcr |= ESAI_RFCR_RFEN; writel(rfcr, esai->base + ESAI_RFCR); - reg &= ~ESAI_RCR_RPR; reg |= ESAI_RCR_RE(substream->runtime->channels); writel(reg, esai->base + ESAI_RCR); } @@ -473,7 +474,6 @@ static int imx_esai_trigger(struct snd_pcm_substream *substream, int cmd, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { reg &= ~ESAI_TCR_TE(substream->runtime->channels); writel(reg, esai->base + ESAI_TCR); - writel(reg, esai->base + ESAI_TCR); tfcr |= ESAI_TFCR_TFR; tfcr &= ~ESAI_TFCR_TFEN; writel(tfcr, esai->base + ESAI_TFCR); @@ -482,8 +482,6 @@ static int imx_esai_trigger(struct snd_pcm_substream *substream, int cmd, } else { reg &= ~ESAI_RCR_RE(substream->runtime->channels); writel(reg, esai->base + ESAI_RCR); - reg |= ESAI_RCR_RPR; - writel(reg, esai->base + ESAI_RCR); rfcr |= ESAI_RFCR_RFR; rfcr &= ~ESAI_RFCR_RFEN; writel(rfcr, esai->base + ESAI_RFCR); diff --git a/sound/soc/imx/imx-hdmi-dma.c b/sound/soc/imx/imx-hdmi-dma.c index c8e88ff79e3b..1bd36ca1ea57 100644 --- a/sound/soc/imx/imx-hdmi-dma.c +++ b/sound/soc/imx/imx-hdmi-dma.c @@ -35,6 +35,11 @@ #include <mach/mxc_hdmi.h> #include "imx-hdmi.h" +#define HDMI_DMA_BURST_UNSPECIFIED_LEGNTH 0 +#define HDMI_DMA_BURST_INCR4 1 +#define HDMI_DMA_BURST_INCR8 2 +#define HDMI_DMA_BURST_INCR16 3 + struct imx_hdmi_dma_runtime_data { struct snd_pcm_substream *tx_substream; @@ -315,16 +320,16 @@ static void hdmi_dma_set_incr_type(int incr_type) HDMI_AHB_DMA_CONF0_INCR_TYPE_MASK); switch (incr_type) { - case 1: + case HDMI_DMA_BURST_UNSPECIFIED_LEGNTH: break; - case 4: + case HDMI_DMA_BURST_INCR4: value |= HDMI_AHB_DMA_CONF0_BURST_MODE; break; - case 8: + case HDMI_DMA_BURST_INCR8: value |= HDMI_AHB_DMA_CONF0_BURST_MODE | HDMI_AHB_DMA_CONF0_INCR8; break; - case 16: + case HDMI_DMA_BURST_INCR16: value |= HDMI_AHB_DMA_CONF0_BURST_MODE | HDMI_AHB_DMA_CONF0_INCR16; break; @@ -359,11 +364,27 @@ static void hdmi_dma_enable_channels(int channels) } } +static void hdmi_dma_set_thrsld(void) +{ + int rev = hdmi_readb(HDMI_REVISION_ID); + + switch (rev) { + case 0x0a: + hdmi_writeb(126, HDMI_AHB_DMA_THRSLD); + break; + default: + hdmi_writeb(64, HDMI_AHB_DMA_THRSLD); + break; + } + + pr_debug("HDMI_AHB_DMA_THRSLD 0x%02x\n", hdmi_readb(HDMI_AHB_DMA_THRSLD)); +} + static void hdmi_dma_configure_dma(int channels) { hdmi_dma_enable_hlock(1); - hdmi_dma_set_incr_type(4); - hdmi_writeb(64, HDMI_AHB_DMA_THRSLD); + hdmi_dma_set_incr_type(HDMI_DMA_BURST_UNSPECIFIED_LEGNTH); + hdmi_dma_set_thrsld(); hdmi_dma_enable_channels(channels); } @@ -548,6 +569,7 @@ static int hdmi_dma_trigger(struct snd_pcm_substream *substream, int cmd) hdmi_dma_irq_mask(0); hdmi_dma_priv->tx_active = true; hdmi_dma_start(); + hdmi_set_dma_mode(1); break; case SNDRV_PCM_TRIGGER_STOP: @@ -555,6 +577,7 @@ static int hdmi_dma_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_PAUSE_PUSH: hdmi_dma_priv->tx_active = false; hdmi_dma_stop(); + hdmi_set_dma_mode(0); hdmi_dma_irq_mask(1); break; diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c index be2717587c59..a37c98b925ac 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/imx/imx-ssi.c @@ -157,7 +157,13 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) if (ssi->flags & IMX_SSI_SYN) scr |= SSI_SCR_SYN; + /* Dual-FIFO support */ + strcr |= SSI_STCR_TFEN1; + scr |= SSI_SCR_TCH_EN; + writel(strcr, ssi->base + SSI_STCR); + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBS_CFS) + strcr &= ~(SSI_STCR_TFDIR | SSI_STCR_TXDIR); writel(strcr, ssi->base + SSI_SRCR); writel(scr, ssi->base + SSI_SCR); diff --git a/sound/soc/imx/imx-wm8962.c b/sound/soc/imx/imx-wm8962.c index 2885d3a47889..74950ad79861 100644 --- a/sound/soc/imx/imx-wm8962.c +++ b/sound/soc/imx/imx-wm8962.c @@ -326,9 +326,9 @@ static int imx_wm8962_init(struct snd_soc_pcm_runtime *rtd) /* if amic is inserted, disable DMIC */ if (priv->amic_status != plat->mic_active_low) - snd_soc_dapm_nc_pin(&gcodec->dapm, "DMIC"); + snd_soc_dapm_nc_pin(&codec->dapm, "DMIC"); else - snd_soc_dapm_enable_pin(&gcodec->dapm, "DMIC"); + snd_soc_dapm_enable_pin(&codec->dapm, "DMIC"); } return 0; |