From 664a57ecb026dc47f9d8b002e6dcb557e877e4d1 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 3 May 2013 15:31:53 +0100 Subject: dmaengine: ste_dma40: Assign memcpy channels in the driver The channels reserved for memcpy are the same for all currently supported platforms. With this in mind, we can ease the platform data passing requirement by moving these assignments out from platform code and place them directly into the driver. Acked-by: Vinod Koul Acked-by: Arnd Bergmann Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- include/linux/platform_data/dma-ste-dma40.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index 4b781014b0a0..a8087843a99b 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -141,8 +141,6 @@ struct stedma40_chan_cfg { * @dev_len: length of dev_tx and dev_rx * @dev_tx: mapping between destination event line and io address * @dev_rx: mapping between source event line and io address - * @memcpy: list of memcpy event lines - * @memcpy_len: length of memcpy * @memcpy_conf_phy: default configuration of physical channel memcpy * @memcpy_conf_log: default configuration of logical channel memcpy * @disabled_channels: A vector, ending with -1, that marks physical channels @@ -162,8 +160,6 @@ struct stedma40_platform_data { u32 dev_len; const dma_addr_t *dev_tx; const dma_addr_t *dev_rx; - int *memcpy; - u32 memcpy_len; struct stedma40_chan_cfg *memcpy_conf_phy; struct stedma40_chan_cfg *memcpy_conf_log; int disabled_channels[STEDMA40_MAX_PHYS]; -- cgit v1.3-18-gd494 From 29027a1e1121a1c9c5e726cf09dc2e9789a282f3 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 3 May 2013 15:31:54 +0100 Subject: dmaengine: ste_dma40: Move default memcpy configs into the driver There are only two default memcpy configurations used for the DMA40 driver; one for physical memcpy and one for logical memcpy. Instead of invariably passing the same configurations though platform data, we're moving them into the driver instead. Acked-by: Vinod Koul Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- arch/arm/mach-ux500/devices-db8500.c | 28 ------------------------- drivers/dma/ste_dma40.c | 32 +++++++++++++++++++++++++++-- include/linux/platform_data/dma-ste-dma40.h | 4 ---- 3 files changed, 30 insertions(+), 34 deletions(-) (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-ux500/devices-db8500.c b/arch/arm/mach-ux500/devices-db8500.c index 159855fae55b..a30977b374ba 100644 --- a/arch/arm/mach-ux500/devices-db8500.c +++ b/arch/arm/mach-ux500/devices-db8500.c @@ -42,32 +42,6 @@ static struct resource dma40_resources[] = { } }; -/* Default configuration for physcial memcpy */ -struct stedma40_chan_cfg dma40_memcpy_conf_phy = { - .mode = STEDMA40_MODE_PHYSICAL, - .dir = STEDMA40_MEM_TO_MEM, - - .src_info.data_width = STEDMA40_BYTE_WIDTH, - .src_info.psize = STEDMA40_PSIZE_PHY_1, - .src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, - - .dst_info.data_width = STEDMA40_BYTE_WIDTH, - .dst_info.psize = STEDMA40_PSIZE_PHY_1, - .dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, -}; -/* Default configuration for logical memcpy */ -struct stedma40_chan_cfg dma40_memcpy_conf_log = { - .dir = STEDMA40_MEM_TO_MEM, - - .src_info.data_width = STEDMA40_BYTE_WIDTH, - .src_info.psize = STEDMA40_PSIZE_LOG_1, - .src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, - - .dst_info.data_width = STEDMA40_BYTE_WIDTH, - .dst_info.psize = STEDMA40_PSIZE_LOG_1, - .dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, -}; - /* * Mapping between destination event lines and physical device address. * The event line is tied to a device and therefore the address is constant. @@ -150,8 +124,6 @@ static struct stedma40_platform_data dma40_plat_data = { .dev_len = DB8500_DMA_NR_DEV, .dev_rx = dma40_rx_map, .dev_tx = dma40_tx_map, - .memcpy_conf_phy = &dma40_memcpy_conf_phy, - .memcpy_conf_log = &dma40_memcpy_conf_log, .disabled_channels = {-1}, }; diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index cd7b4808d08c..c47139ae8fa8 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -72,6 +72,34 @@ static int dma40_memcpy_channels[] = { DB8500_DMA_MEMCPY_EV_5, }; +/* Default configuration for physcial memcpy */ +struct stedma40_chan_cfg dma40_memcpy_conf_phy = { + .mode = STEDMA40_MODE_PHYSICAL, + .dir = STEDMA40_MEM_TO_MEM, + + .src_info.data_width = STEDMA40_BYTE_WIDTH, + .src_info.psize = STEDMA40_PSIZE_PHY_1, + .src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, + + .dst_info.data_width = STEDMA40_BYTE_WIDTH, + .dst_info.psize = STEDMA40_PSIZE_PHY_1, + .dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, +}; + +/* Default configuration for logical memcpy */ +struct stedma40_chan_cfg dma40_memcpy_conf_log = { + .mode = STEDMA40_MODE_LOGICAL, + .dir = STEDMA40_MEM_TO_MEM, + + .src_info.data_width = STEDMA40_BYTE_WIDTH, + .src_info.psize = STEDMA40_PSIZE_LOG_1, + .src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, + + .dst_info.data_width = STEDMA40_BYTE_WIDTH, + .dst_info.psize = STEDMA40_PSIZE_LOG_1, + .dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, +}; + /** * enum 40_command - The different commands and/or statuses. * @@ -2029,13 +2057,13 @@ static int d40_config_memcpy(struct d40_chan *d40c) dma_cap_mask_t cap = d40c->chan.device->cap_mask; if (dma_has_cap(DMA_MEMCPY, cap) && !dma_has_cap(DMA_SLAVE, cap)) { - d40c->dma_cfg = *d40c->base->plat_data->memcpy_conf_log; + d40c->dma_cfg = dma40_memcpy_conf_log; d40c->dma_cfg.src_dev_type = STEDMA40_DEV_SRC_MEMORY; d40c->dma_cfg.dst_dev_type = dma40_memcpy_channels[d40c->chan.chan_id]; } else if (dma_has_cap(DMA_MEMCPY, cap) && dma_has_cap(DMA_SLAVE, cap)) { - d40c->dma_cfg = *d40c->base->plat_data->memcpy_conf_phy; + d40c->dma_cfg = dma40_memcpy_conf_phy; } else { chan_err(d40c, "No memcpy\n"); return -EINVAL; diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index a8087843a99b..869c571c8c08 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -141,8 +141,6 @@ struct stedma40_chan_cfg { * @dev_len: length of dev_tx and dev_rx * @dev_tx: mapping between destination event line and io address * @dev_rx: mapping between source event line and io address - * @memcpy_conf_phy: default configuration of physical channel memcpy - * @memcpy_conf_log: default configuration of logical channel memcpy * @disabled_channels: A vector, ending with -1, that marks physical channels * that are for different reasons not available for the driver. * @soft_lli_chans: A vector, that marks physical channels will use LLI by SW @@ -160,8 +158,6 @@ struct stedma40_platform_data { u32 dev_len; const dma_addr_t *dev_tx; const dma_addr_t *dev_rx; - struct stedma40_chan_cfg *memcpy_conf_phy; - struct stedma40_chan_cfg *memcpy_conf_log; int disabled_channels[STEDMA40_MAX_PHYS]; int *soft_lli_chans; int num_of_soft_lli_chans; -- cgit v1.3-18-gd494 From 26955c07dcf3c36b6427e52fec0f725300ca079e Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 3 May 2013 15:31:56 +0100 Subject: dmaengine: ste_dma40: Amalgamate DMA source and destination channel numbers Devices which utilise DMA use the same device numbers for transmitting and receiving. In this patch we encode the source and destination information into one single attribute. We can subsequently exploit the direction attribute to see which of the transfer directions are being described. This also lessens the burden on platform data. Cc: Dan Williams Cc: Per Forlin Cc: Rabin Vincent Acked-by: Vinod Koul Acked-by: Arnd Bergmann Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- arch/arm/mach-ux500/board-mop500-audio.c | 18 +-- arch/arm/mach-ux500/board-mop500-sdi.c | 24 ++-- arch/arm/mach-ux500/board-mop500.c | 33 ++--- arch/arm/mach-ux500/cpu-db8500.c | 32 ++--- arch/arm/mach-ux500/devices-db8500.c | 120 ++++++++--------- arch/arm/mach-ux500/ste-dma40-db8500.h | 193 ++++++++++------------------ arch/arm/mach-ux500/usb.c | 10 +- drivers/dma/ste_dma40.c | 93 +++++--------- drivers/dma/ste_dma40_ll.c | 4 +- include/linux/platform_data/dma-ste-dma40.h | 6 +- 10 files changed, 207 insertions(+), 326 deletions(-) (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-ux500/board-mop500-audio.c b/arch/arm/mach-ux500/board-mop500-audio.c index aba9e5692958..5a968fa8b90c 100644 --- a/arch/arm/mach-ux500/board-mop500-audio.c +++ b/arch/arm/mach-ux500/board-mop500-audio.c @@ -23,8 +23,7 @@ static struct stedma40_chan_cfg msp0_dma_rx = { .high_priority = true, .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV31_MSP0_RX_SLIM0_CH0_RX, - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .dev_type = DB8500_DMA_DEV31_MSP0_SLIM0_CH0, .src_info.psize = STEDMA40_PSIZE_LOG_4, .dst_info.psize = STEDMA40_PSIZE_LOG_4, @@ -36,8 +35,7 @@ static struct stedma40_chan_cfg msp0_dma_tx = { .high_priority = true, .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_DST_MEMORY, - .dst_dev_type = DB8500_DMA_DEV31_MSP0_TX_SLIM0_CH0_TX, + .dev_type = DB8500_DMA_DEV31_MSP0_SLIM0_CH0, .src_info.psize = STEDMA40_PSIZE_LOG_4, .dst_info.psize = STEDMA40_PSIZE_LOG_4, @@ -55,8 +53,7 @@ static struct stedma40_chan_cfg msp1_dma_rx = { .high_priority = true, .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV30_MSP3_RX, - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .dev_type = DB8500_DMA_DEV30_MSP3, .src_info.psize = STEDMA40_PSIZE_LOG_4, .dst_info.psize = STEDMA40_PSIZE_LOG_4, @@ -68,8 +65,7 @@ static struct stedma40_chan_cfg msp1_dma_tx = { .high_priority = true, .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_DST_MEMORY, - .dst_dev_type = DB8500_DMA_DEV30_MSP1_TX, + .dev_type = DB8500_DMA_DEV30_MSP1, .src_info.psize = STEDMA40_PSIZE_LOG_4, .dst_info.psize = STEDMA40_PSIZE_LOG_4, @@ -87,8 +83,7 @@ static struct stedma40_chan_cfg msp2_dma_rx = { .high_priority = true, .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV14_MSP2_RX, - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .dev_type = DB8500_DMA_DEV14_MSP2, /* MSP2 DMA doesn't work with PSIZE == 4 on DB8500v2 */ .src_info.psize = STEDMA40_PSIZE_LOG_1, @@ -101,8 +96,7 @@ static struct stedma40_chan_cfg msp2_dma_tx = { .high_priority = true, .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_DST_MEMORY, - .dst_dev_type = DB8500_DMA_DEV14_MSP2_TX, + .dev_type = DB8500_DMA_DEV14_MSP2, .src_info.psize = STEDMA40_PSIZE_LOG_4, .dst_info.psize = STEDMA40_PSIZE_LOG_4, diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 0ef38775a0c1..4e30b6dc9ac5 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -35,8 +35,7 @@ struct stedma40_chan_cfg mop500_sdi0_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV29_SD_MM0_RX, - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .dev_type = DB8500_DMA_DEV29_SD_MM0, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, }; @@ -44,8 +43,7 @@ struct stedma40_chan_cfg mop500_sdi0_dma_cfg_rx = { static struct stedma40_chan_cfg mop500_sdi0_dma_cfg_tx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_SRC_MEMORY, - .dst_dev_type = DB8500_DMA_DEV29_SD_MM0_TX, + .dev_type = DB8500_DMA_DEV29_SD_MM0, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, }; @@ -88,8 +86,7 @@ void mop500_sdi_tc35892_init(struct device *parent) static struct stedma40_chan_cfg sdi1_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV32_SD_MM1_RX, - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .dev_type = DB8500_DMA_DEV32_SD_MM1, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, }; @@ -97,8 +94,7 @@ static struct stedma40_chan_cfg sdi1_dma_cfg_rx = { static struct stedma40_chan_cfg sdi1_dma_cfg_tx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_SRC_MEMORY, - .dst_dev_type = DB8500_DMA_DEV32_SD_MM1_TX, + .dev_type = DB8500_DMA_DEV32_SD_MM1, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, }; @@ -125,8 +121,7 @@ struct mmci_platform_data mop500_sdi1_data = { struct stedma40_chan_cfg mop500_sdi2_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV28_SD_MM2_RX, - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .dev_type = DB8500_DMA_DEV28_SD_MM2, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, }; @@ -134,8 +129,7 @@ struct stedma40_chan_cfg mop500_sdi2_dma_cfg_rx = { static struct stedma40_chan_cfg mop500_sdi2_dma_cfg_tx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_SRC_MEMORY, - .dst_dev_type = DB8500_DMA_DEV28_SD_MM2_TX, + .dev_type = DB8500_DMA_DEV28_SD_MM2, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, }; @@ -163,8 +157,7 @@ struct mmci_platform_data mop500_sdi2_data = { struct stedma40_chan_cfg mop500_sdi4_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV42_SD_MM4_RX, - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .dev_type = DB8500_DMA_DEV42_SD_MM4, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, }; @@ -172,8 +165,7 @@ struct stedma40_chan_cfg mop500_sdi4_dma_cfg_rx = { static struct stedma40_chan_cfg mop500_sdi4_dma_cfg_tx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_SRC_MEMORY, - .dst_dev_type = DB8500_DMA_DEV42_SD_MM4_TX, + .dev_type = DB8500_DMA_DEV42_SD_MM4, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, }; diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 3cd555ac6d0a..871e61517fb2 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -425,8 +425,7 @@ void mop500_snowball_ethernet_clock_enable(void) static struct cryp_platform_data u8500_cryp1_platform_data = { .mem_to_engine = { .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_SRC_MEMORY, - .dst_dev_type = DB8500_DMA_DEV48_CAC1_TX, + .dev_type = DB8500_DMA_DEV48_CAC1, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, .mode = STEDMA40_MODE_LOGICAL, @@ -435,8 +434,7 @@ static struct cryp_platform_data u8500_cryp1_platform_data = { }, .engine_to_mem = { .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV48_CAC1_RX, - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .dev_type = DB8500_DMA_DEV48_CAC1, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, .mode = STEDMA40_MODE_LOGICAL, @@ -447,8 +445,7 @@ static struct cryp_platform_data u8500_cryp1_platform_data = { static struct stedma40_chan_cfg u8500_hash_dma_cfg_tx = { .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_SRC_MEMORY, - .dst_dev_type = DB8500_DMA_DEV50_HAC1_TX, + .dev_type = DB8500_DMA_DEV50_HAC1_TX, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, .mode = STEDMA40_MODE_LOGICAL, @@ -471,8 +468,7 @@ static struct platform_device *mop500_platform_devs[] __initdata = { static struct stedma40_chan_cfg ssp0_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV8_SSP0_RX, - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .dev_type = DB8500_DMA_DEV8_SSP0, .src_info.data_width = STEDMA40_BYTE_WIDTH, .dst_info.data_width = STEDMA40_BYTE_WIDTH, }; @@ -480,8 +476,7 @@ static struct stedma40_chan_cfg ssp0_dma_cfg_rx = { static struct stedma40_chan_cfg ssp0_dma_cfg_tx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_SRC_MEMORY, - .dst_dev_type = DB8500_DMA_DEV8_SSP0_TX, + .dev_type = DB8500_DMA_DEV8_SSP0, .src_info.data_width = STEDMA40_BYTE_WIDTH, .dst_info.data_width = STEDMA40_BYTE_WIDTH, }; @@ -512,8 +507,7 @@ static void __init mop500_spi_init(struct device *parent) static struct stedma40_chan_cfg uart0_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV13_UART0_RX, - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .dev_type = DB8500_DMA_DEV13_UART0, .src_info.data_width = STEDMA40_BYTE_WIDTH, .dst_info.data_width = STEDMA40_BYTE_WIDTH, }; @@ -521,8 +515,7 @@ static struct stedma40_chan_cfg uart0_dma_cfg_rx = { static struct stedma40_chan_cfg uart0_dma_cfg_tx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_SRC_MEMORY, - .dst_dev_type = DB8500_DMA_DEV13_UART0_TX, + .dev_type = DB8500_DMA_DEV13_UART0, .src_info.data_width = STEDMA40_BYTE_WIDTH, .dst_info.data_width = STEDMA40_BYTE_WIDTH, }; @@ -530,8 +523,7 @@ static struct stedma40_chan_cfg uart0_dma_cfg_tx = { static struct stedma40_chan_cfg uart1_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV12_UART1_RX, - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .dev_type = DB8500_DMA_DEV12_UART1, .src_info.data_width = STEDMA40_BYTE_WIDTH, .dst_info.data_width = STEDMA40_BYTE_WIDTH, }; @@ -539,8 +531,7 @@ static struct stedma40_chan_cfg uart1_dma_cfg_rx = { static struct stedma40_chan_cfg uart1_dma_cfg_tx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_SRC_MEMORY, - .dst_dev_type = DB8500_DMA_DEV12_UART1_TX, + .dev_type = DB8500_DMA_DEV12_UART1, .src_info.data_width = STEDMA40_BYTE_WIDTH, .dst_info.data_width = STEDMA40_BYTE_WIDTH, }; @@ -548,8 +539,7 @@ static struct stedma40_chan_cfg uart1_dma_cfg_tx = { static struct stedma40_chan_cfg uart2_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV11_UART2_RX, - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .dev_type = DB8500_DMA_DEV11_UART2, .src_info.data_width = STEDMA40_BYTE_WIDTH, .dst_info.data_width = STEDMA40_BYTE_WIDTH, }; @@ -557,8 +547,7 @@ static struct stedma40_chan_cfg uart2_dma_cfg_rx = { static struct stedma40_chan_cfg uart2_dma_cfg_tx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_MEM_TO_PERIPH, - .src_dev_type = STEDMA40_DEV_SRC_MEMORY, - .dst_dev_type = DB8500_DMA_DEV11_UART2_TX, + .dev_type = DB8500_DMA_DEV11_UART2, .src_info.data_width = STEDMA40_BYTE_WIDTH, .dst_info.data_width = STEDMA40_BYTE_WIDTH, }; diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index e90b5ab23b6d..67d68e05f3a7 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -163,25 +163,25 @@ static void __init db8500_add_gpios(struct device *parent) } static int usb_db8500_rx_dma_cfg[] = { - DB8500_DMA_DEV38_USB_OTG_IEP_1_9, - DB8500_DMA_DEV37_USB_OTG_IEP_2_10, - DB8500_DMA_DEV36_USB_OTG_IEP_3_11, - DB8500_DMA_DEV19_USB_OTG_IEP_4_12, - DB8500_DMA_DEV18_USB_OTG_IEP_5_13, - DB8500_DMA_DEV17_USB_OTG_IEP_6_14, - DB8500_DMA_DEV16_USB_OTG_IEP_7_15, - DB8500_DMA_DEV39_USB_OTG_IEP_8 + DB8500_DMA_DEV38_USB_OTG_IEP_AND_OEP_1_9, + DB8500_DMA_DEV37_USB_OTG_IEP_AND_OEP_2_10, + DB8500_DMA_DEV36_USB_OTG_IEP_AND_OEP_3_11, + DB8500_DMA_DEV19_USB_OTG_IEP_AND_OEP_4_12, + DB8500_DMA_DEV18_USB_OTG_IEP_AND_OEP_5_13, + DB8500_DMA_DEV17_USB_OTG_IEP_AND_OEP_6_14, + DB8500_DMA_DEV16_USB_OTG_IEP_AND_OEP_7_15, + DB8500_DMA_DEV39_USB_OTG_IEP_AND_OEP_8 }; static int usb_db8500_tx_dma_cfg[] = { - DB8500_DMA_DEV38_USB_OTG_OEP_1_9, - DB8500_DMA_DEV37_USB_OTG_OEP_2_10, - DB8500_DMA_DEV36_USB_OTG_OEP_3_11, - DB8500_DMA_DEV19_USB_OTG_OEP_4_12, - DB8500_DMA_DEV18_USB_OTG_OEP_5_13, - DB8500_DMA_DEV17_USB_OTG_OEP_6_14, - DB8500_DMA_DEV16_USB_OTG_OEP_7_15, - DB8500_DMA_DEV39_USB_OTG_OEP_8 + DB8500_DMA_DEV38_USB_OTG_IEP_AND_OEP_1_9, + DB8500_DMA_DEV37_USB_OTG_IEP_AND_OEP_2_10, + DB8500_DMA_DEV36_USB_OTG_IEP_AND_OEP_3_11, + DB8500_DMA_DEV19_USB_OTG_IEP_AND_OEP_4_12, + DB8500_DMA_DEV18_USB_OTG_IEP_AND_OEP_5_13, + DB8500_DMA_DEV17_USB_OTG_IEP_AND_OEP_6_14, + DB8500_DMA_DEV16_USB_OTG_IEP_AND_OEP_7_15, + DB8500_DMA_DEV39_USB_OTG_IEP_AND_OEP_8 }; static const char *db8500_read_soc_id(void) diff --git a/arch/arm/mach-ux500/devices-db8500.c b/arch/arm/mach-ux500/devices-db8500.c index a30977b374ba..7989c564e47a 100644 --- a/arch/arm/mach-ux500/devices-db8500.c +++ b/arch/arm/mach-ux500/devices-db8500.c @@ -50,74 +50,74 @@ static struct resource dma40_resources[] = { */ static const dma_addr_t dma40_tx_map[DB8500_DMA_NR_DEV] = { /* MUSB - these will be runtime-reconfigured */ - [DB8500_DMA_DEV39_USB_OTG_OEP_8] = -1, - [DB8500_DMA_DEV16_USB_OTG_OEP_7_15] = -1, - [DB8500_DMA_DEV17_USB_OTG_OEP_6_14] = -1, - [DB8500_DMA_DEV18_USB_OTG_OEP_5_13] = -1, - [DB8500_DMA_DEV19_USB_OTG_OEP_4_12] = -1, - [DB8500_DMA_DEV36_USB_OTG_OEP_3_11] = -1, - [DB8500_DMA_DEV37_USB_OTG_OEP_2_10] = -1, - [DB8500_DMA_DEV38_USB_OTG_OEP_1_9] = -1, + [DB8500_DMA_DEV39_USB_OTG_IEP_AND_OEP_8] = -1, + [DB8500_DMA_DEV16_USB_OTG_IEP_AND_OEP_7_15] = -1, + [DB8500_DMA_DEV17_USB_OTG_IEP_AND_OEP_6_14] = -1, + [DB8500_DMA_DEV18_USB_OTG_IEP_AND_OEP_5_13] = -1, + [DB8500_DMA_DEV19_USB_OTG_IEP_AND_OEP_4_12] = -1, + [DB8500_DMA_DEV36_USB_OTG_IEP_AND_OEP_3_11] = -1, + [DB8500_DMA_DEV37_USB_OTG_IEP_AND_OEP_2_10] = -1, + [DB8500_DMA_DEV38_USB_OTG_IEP_AND_OEP_1_9] = -1, /* PrimeCells - run-time configured */ - [DB8500_DMA_DEV0_SPI0_TX] = -1, - [DB8500_DMA_DEV1_SD_MMC0_TX] = -1, - [DB8500_DMA_DEV2_SD_MMC1_TX] = -1, - [DB8500_DMA_DEV3_SD_MMC2_TX] = -1, - [DB8500_DMA_DEV8_SSP0_TX] = -1, - [DB8500_DMA_DEV9_SSP1_TX] = -1, - [DB8500_DMA_DEV11_UART2_TX] = -1, - [DB8500_DMA_DEV12_UART1_TX] = -1, - [DB8500_DMA_DEV13_UART0_TX] = -1, - [DB8500_DMA_DEV28_SD_MM2_TX] = -1, - [DB8500_DMA_DEV29_SD_MM0_TX] = -1, - [DB8500_DMA_DEV32_SD_MM1_TX] = -1, - [DB8500_DMA_DEV33_SPI2_TX] = -1, - [DB8500_DMA_DEV35_SPI1_TX] = -1, - [DB8500_DMA_DEV40_SPI3_TX] = -1, - [DB8500_DMA_DEV41_SD_MM3_TX] = -1, - [DB8500_DMA_DEV42_SD_MM4_TX] = -1, - [DB8500_DMA_DEV43_SD_MM5_TX] = -1, - [DB8500_DMA_DEV14_MSP2_TX] = U8500_MSP2_BASE + MSP_TX_RX_REG_OFFSET, - [DB8500_DMA_DEV30_MSP1_TX] = U8500_MSP1_BASE + MSP_TX_RX_REG_OFFSET, - [DB8500_DMA_DEV31_MSP0_TX_SLIM0_CH0_TX] = U8500_MSP0_BASE + MSP_TX_RX_REG_OFFSET, - [DB8500_DMA_DEV48_CAC1_TX] = U8500_CRYP1_BASE + CRYP1_TX_REG_OFFSET, + [DB8500_DMA_DEV0_SPI0] = -1, + [DB8500_DMA_DEV1_SD_MMC0] = -1, + [DB8500_DMA_DEV2_SD_MMC1] = -1, + [DB8500_DMA_DEV3_SD_MMC2] = -1, + [DB8500_DMA_DEV8_SSP0] = -1, + [DB8500_DMA_DEV9_SSP1] = -1, + [DB8500_DMA_DEV11_UART2] = -1, + [DB8500_DMA_DEV12_UART1] = -1, + [DB8500_DMA_DEV13_UART0] = -1, + [DB8500_DMA_DEV28_SD_MM2] = -1, + [DB8500_DMA_DEV29_SD_MM0] = -1, + [DB8500_DMA_DEV32_SD_MM1] = -1, + [DB8500_DMA_DEV33_SPI2] = -1, + [DB8500_DMA_DEV35_SPI1] = -1, + [DB8500_DMA_DEV40_SPI3] = -1, + [DB8500_DMA_DEV41_SD_MM3] = -1, + [DB8500_DMA_DEV42_SD_MM4] = -1, + [DB8500_DMA_DEV43_SD_MM5] = -1, + [DB8500_DMA_DEV14_MSP2] = U8500_MSP2_BASE + MSP_TX_RX_REG_OFFSET, + [DB8500_DMA_DEV30_MSP1] = U8500_MSP1_BASE + MSP_TX_RX_REG_OFFSET, + [DB8500_DMA_DEV31_MSP0_SLIM0_CH0] = U8500_MSP0_BASE + MSP_TX_RX_REG_OFFSET, + [DB8500_DMA_DEV48_CAC1] = U8500_CRYP1_BASE + CRYP1_TX_REG_OFFSET, [DB8500_DMA_DEV50_HAC1_TX] = U8500_HASH1_BASE + HASH1_TX_REG_OFFSET, }; /* Mapping between source event lines and physical device address */ static const dma_addr_t dma40_rx_map[DB8500_DMA_NR_DEV] = { /* MUSB - these will be runtime-reconfigured */ - [DB8500_DMA_DEV39_USB_OTG_IEP_8] = -1, - [DB8500_DMA_DEV16_USB_OTG_IEP_7_15] = -1, - [DB8500_DMA_DEV17_USB_OTG_IEP_6_14] = -1, - [DB8500_DMA_DEV18_USB_OTG_IEP_5_13] = -1, - [DB8500_DMA_DEV19_USB_OTG_IEP_4_12] = -1, - [DB8500_DMA_DEV36_USB_OTG_IEP_3_11] = -1, - [DB8500_DMA_DEV37_USB_OTG_IEP_2_10] = -1, - [DB8500_DMA_DEV38_USB_OTG_IEP_1_9] = -1, + [DB8500_DMA_DEV39_USB_OTG_IEP_AND_OEP_8] = -1, + [DB8500_DMA_DEV16_USB_OTG_IEP_AND_OEP_7_15] = -1, + [DB8500_DMA_DEV17_USB_OTG_IEP_AND_OEP_6_14] = -1, + [DB8500_DMA_DEV18_USB_OTG_IEP_AND_OEP_5_13] = -1, + [DB8500_DMA_DEV19_USB_OTG_IEP_AND_OEP_4_12] = -1, + [DB8500_DMA_DEV36_USB_OTG_IEP_AND_OEP_3_11] = -1, + [DB8500_DMA_DEV37_USB_OTG_IEP_AND_OEP_2_10] = -1, + [DB8500_DMA_DEV38_USB_OTG_IEP_AND_OEP_1_9] = -1, /* PrimeCells */ - [DB8500_DMA_DEV0_SPI0_RX] = -1, - [DB8500_DMA_DEV1_SD_MMC0_RX] = -1, - [DB8500_DMA_DEV2_SD_MMC1_RX] = -1, - [DB8500_DMA_DEV3_SD_MMC2_RX] = -1, - [DB8500_DMA_DEV8_SSP0_RX] = -1, - [DB8500_DMA_DEV9_SSP1_RX] = -1, - [DB8500_DMA_DEV11_UART2_RX] = -1, - [DB8500_DMA_DEV12_UART1_RX] = -1, - [DB8500_DMA_DEV13_UART0_RX] = -1, - [DB8500_DMA_DEV28_SD_MM2_RX] = -1, - [DB8500_DMA_DEV29_SD_MM0_RX] = -1, - [DB8500_DMA_DEV32_SD_MM1_RX] = -1, - [DB8500_DMA_DEV33_SPI2_RX] = -1, - [DB8500_DMA_DEV35_SPI1_RX] = -1, - [DB8500_DMA_DEV40_SPI3_RX] = -1, - [DB8500_DMA_DEV41_SD_MM3_RX] = -1, - [DB8500_DMA_DEV42_SD_MM4_RX] = -1, - [DB8500_DMA_DEV43_SD_MM5_RX] = -1, - [DB8500_DMA_DEV14_MSP2_RX] = U8500_MSP2_BASE + MSP_TX_RX_REG_OFFSET, - [DB8500_DMA_DEV30_MSP3_RX] = U8500_MSP3_BASE + MSP_TX_RX_REG_OFFSET, - [DB8500_DMA_DEV31_MSP0_RX_SLIM0_CH0_RX] = U8500_MSP0_BASE + MSP_TX_RX_REG_OFFSET, - [DB8500_DMA_DEV48_CAC1_RX] = U8500_CRYP1_BASE + CRYP1_RX_REG_OFFSET, + [DB8500_DMA_DEV0_SPI0] = -1, + [DB8500_DMA_DEV1_SD_MMC0] = -1, + [DB8500_DMA_DEV2_SD_MMC1] = -1, + [DB8500_DMA_DEV3_SD_MMC2] = -1, + [DB8500_DMA_DEV8_SSP0] = -1, + [DB8500_DMA_DEV9_SSP1] = -1, + [DB8500_DMA_DEV11_UART2] = -1, + [DB8500_DMA_DEV12_UART1] = -1, + [DB8500_DMA_DEV13_UART0] = -1, + [DB8500_DMA_DEV28_SD_MM2] = -1, + [DB8500_DMA_DEV29_SD_MM0] = -1, + [DB8500_DMA_DEV32_SD_MM1] = -1, + [DB8500_DMA_DEV33_SPI2] = -1, + [DB8500_DMA_DEV35_SPI1] = -1, + [DB8500_DMA_DEV40_SPI3] = -1, + [DB8500_DMA_DEV41_SD_MM3] = -1, + [DB8500_DMA_DEV42_SD_MM4] = -1, + [DB8500_DMA_DEV43_SD_MM5] = -1, + [DB8500_DMA_DEV14_MSP2] = U8500_MSP2_BASE + MSP_TX_RX_REG_OFFSET, + [DB8500_DMA_DEV30_MSP3] = U8500_MSP3_BASE + MSP_TX_RX_REG_OFFSET, + [DB8500_DMA_DEV31_MSP0_SLIM0_CH0] = U8500_MSP0_BASE + MSP_TX_RX_REG_OFFSET, + [DB8500_DMA_DEV48_CAC1] = U8500_CRYP1_BASE + CRYP1_RX_REG_OFFSET, }; static struct stedma40_platform_data dma40_plat_data = { diff --git a/arch/arm/mach-ux500/ste-dma40-db8500.h b/arch/arm/mach-ux500/ste-dma40-db8500.h index a616419bea76..0296ae5b0fd9 100644 --- a/arch/arm/mach-ux500/ste-dma40-db8500.h +++ b/arch/arm/mach-ux500/ste-dma40-db8500.h @@ -12,133 +12,74 @@ #define DB8500_DMA_NR_DEV 64 -enum dma_src_dev_type { - DB8500_DMA_DEV0_SPI0_RX = 0, - DB8500_DMA_DEV1_SD_MMC0_RX = 1, - DB8500_DMA_DEV2_SD_MMC1_RX = 2, - DB8500_DMA_DEV3_SD_MMC2_RX = 3, - DB8500_DMA_DEV4_I2C1_RX = 4, - DB8500_DMA_DEV5_I2C3_RX = 5, - DB8500_DMA_DEV6_I2C2_RX = 6, - DB8500_DMA_DEV7_I2C4_RX = 7, /* Only on V1 and later */ - DB8500_DMA_DEV8_SSP0_RX = 8, - DB8500_DMA_DEV9_SSP1_RX = 9, - DB8500_DMA_DEV10_MCDE_RX = 10, - DB8500_DMA_DEV11_UART2_RX = 11, - DB8500_DMA_DEV12_UART1_RX = 12, - DB8500_DMA_DEV13_UART0_RX = 13, - DB8500_DMA_DEV14_MSP2_RX = 14, - DB8500_DMA_DEV15_I2C0_RX = 15, - DB8500_DMA_DEV16_USB_OTG_IEP_7_15 = 16, - DB8500_DMA_DEV17_USB_OTG_IEP_6_14 = 17, - DB8500_DMA_DEV18_USB_OTG_IEP_5_13 = 18, - DB8500_DMA_DEV19_USB_OTG_IEP_4_12 = 19, - DB8500_DMA_DEV20_SLIM0_CH0_RX_HSI_RX_CH0 = 20, - DB8500_DMA_DEV21_SLIM0_CH1_RX_HSI_RX_CH1 = 21, - DB8500_DMA_DEV22_SLIM0_CH2_RX_HSI_RX_CH2 = 22, - DB8500_DMA_DEV23_SLIM0_CH3_RX_HSI_RX_CH3 = 23, - DB8500_DMA_DEV24_SRC_SXA0_RX_TX = 24, - DB8500_DMA_DEV25_SRC_SXA1_RX_TX = 25, - DB8500_DMA_DEV26_SRC_SXA2_RX_TX = 26, - DB8500_DMA_DEV27_SRC_SXA3_RX_TX = 27, - DB8500_DMA_DEV28_SD_MM2_RX = 28, - DB8500_DMA_DEV29_SD_MM0_RX = 29, - DB8500_DMA_DEV30_MSP1_RX = 30, +/* + * Unless otherwise specified, all channels numbers are used for + * TX & RX, and can be used for either source or destination + * channels. + */ +enum dma_dev_type { + DB8500_DMA_DEV0_SPI0 = 0, + DB8500_DMA_DEV1_SD_MMC0 = 1, + DB8500_DMA_DEV2_SD_MMC1 = 2, + DB8500_DMA_DEV3_SD_MMC2 = 3, + DB8500_DMA_DEV4_I2C1 = 4, + DB8500_DMA_DEV5_I2C3 = 5, + DB8500_DMA_DEV6_I2C2 = 6, + DB8500_DMA_DEV7_I2C4 = 7, /* Only on V1 and later */ + DB8500_DMA_DEV8_SSP0 = 8, + DB8500_DMA_DEV9_SSP1 = 9, + DB8500_DMA_DEV10_MCDE_RX = 10, /* RX only */ + DB8500_DMA_DEV11_UART2 = 11, + DB8500_DMA_DEV12_UART1 = 12, + DB8500_DMA_DEV13_UART0 = 13, + DB8500_DMA_DEV14_MSP2 = 14, + DB8500_DMA_DEV15_I2C0 = 15, + DB8500_DMA_DEV16_USB_OTG_IEP_AND_OEP_7_15 = 16, + DB8500_DMA_DEV17_USB_OTG_IEP_AND_OEP_6_14 = 17, + DB8500_DMA_DEV18_USB_OTG_IEP_AND_OEP_5_13 = 18, + DB8500_DMA_DEV19_USB_OTG_IEP_AND_OEP_4_12 = 19, + DB8500_DMA_DEV20_SLIM0_CH0_HSI_CH0 = 20, + DB8500_DMA_DEV21_SLIM0_CH1_HSI_CH1 = 21, + DB8500_DMA_DEV22_SLIM0_CH2_HSI_CH2 = 22, + DB8500_DMA_DEV23_SLIM0_CH3_HSI_CH3 = 23, + DB8500_DMA_DEV24_SXA0 = 24, + DB8500_DMA_DEV25_SXA1 = 25, + DB8500_DMA_DEV26_SXA2 = 26, + DB8500_DMA_DEV27_SXA3 = 27, + DB8500_DMA_DEV28_SD_MM2 = 28, + DB8500_DMA_DEV29_SD_MM0 = 29, + DB8500_DMA_DEV30_MSP1 = 30, /* On DB8500v2, MSP3 RX replaces MSP1 RX */ - DB8500_DMA_DEV30_MSP3_RX = 30, - DB8500_DMA_DEV31_MSP0_RX_SLIM0_CH0_RX = 31, - DB8500_DMA_DEV32_SD_MM1_RX = 32, - DB8500_DMA_DEV33_SPI2_RX = 33, - DB8500_DMA_DEV34_I2C3_RX2 = 34, - DB8500_DMA_DEV35_SPI1_RX = 35, - DB8500_DMA_DEV36_USB_OTG_IEP_3_11 = 36, - DB8500_DMA_DEV37_USB_OTG_IEP_2_10 = 37, - DB8500_DMA_DEV38_USB_OTG_IEP_1_9 = 38, - DB8500_DMA_DEV39_USB_OTG_IEP_8 = 39, - DB8500_DMA_DEV40_SPI3_RX = 40, - DB8500_DMA_DEV41_SD_MM3_RX = 41, - DB8500_DMA_DEV42_SD_MM4_RX = 42, - DB8500_DMA_DEV43_SD_MM5_RX = 43, - DB8500_DMA_DEV44_SRC_SXA4_RX_TX = 44, - DB8500_DMA_DEV45_SRC_SXA5_RX_TX = 45, - DB8500_DMA_DEV46_SLIM0_CH8_RX_SRC_SXA6_RX_TX = 46, - DB8500_DMA_DEV47_SLIM0_CH9_RX_SRC_SXA7_RX_TX = 47, - DB8500_DMA_DEV48_CAC1_RX = 48, - /* 49, 50 and 51 are not used */ - DB8500_DMA_DEV52_SLIM0_CH4_RX_HSI_RX_CH4 = 52, - DB8500_DMA_DEV53_SLIM0_CH5_RX_HSI_RX_CH5 = 53, - DB8500_DMA_DEV54_SLIM0_CH6_RX_HSI_RX_CH6 = 54, - DB8500_DMA_DEV55_SLIM0_CH7_RX_HSI_RX_CH7 = 55, - /* 56, 57, 58, 59 and 60 are not used */ - DB8500_DMA_DEV61_CAC0_RX = 61, - /* 62 and 63 are not used */ -}; - -enum dma_dest_dev_type { - DB8500_DMA_DEV0_SPI0_TX = 0, - DB8500_DMA_DEV1_SD_MMC0_TX = 1, - DB8500_DMA_DEV2_SD_MMC1_TX = 2, - DB8500_DMA_DEV3_SD_MMC2_TX = 3, - DB8500_DMA_DEV4_I2C1_TX = 4, - DB8500_DMA_DEV5_I2C3_TX = 5, - DB8500_DMA_DEV6_I2C2_TX = 6, - DB8500_DMA_DEV7_I2C4_TX = 7, /* Only on V1 and later */ - DB8500_DMA_DEV8_SSP0_TX = 8, - DB8500_DMA_DEV9_SSP1_TX = 9, - /* 10 is not used*/ - DB8500_DMA_DEV11_UART2_TX = 11, - DB8500_DMA_DEV12_UART1_TX = 12, - DB8500_DMA_DEV13_UART0_TX = 13, - DB8500_DMA_DEV14_MSP2_TX = 14, - DB8500_DMA_DEV15_I2C0_TX = 15, - DB8500_DMA_DEV16_USB_OTG_OEP_7_15 = 16, - DB8500_DMA_DEV17_USB_OTG_OEP_6_14 = 17, - DB8500_DMA_DEV18_USB_OTG_OEP_5_13 = 18, - DB8500_DMA_DEV19_USB_OTG_OEP_4_12 = 19, - DB8500_DMA_DEV20_SLIM0_CH0_TX_HSI_TX_CH0 = 20, - DB8500_DMA_DEV21_SLIM0_CH1_TX_HSI_TX_CH1 = 21, - DB8500_DMA_DEV22_SLIM0_CH2_TX_HSI_TX_CH2 = 22, - DB8500_DMA_DEV23_SLIM0_CH3_TX_HSI_TX_CH3 = 23, - DB8500_DMA_DEV24_DST_SXA0_RX_TX = 24, - DB8500_DMA_DEV25_DST_SXA1_RX_TX = 25, - DB8500_DMA_DEV26_DST_SXA2_RX_TX = 26, - DB8500_DMA_DEV27_DST_SXA3_RX_TX = 27, - DB8500_DMA_DEV28_SD_MM2_TX = 28, - DB8500_DMA_DEV29_SD_MM0_TX = 29, - DB8500_DMA_DEV30_MSP1_TX = 30, - DB8500_DMA_DEV31_MSP0_TX_SLIM0_CH0_TX = 31, - DB8500_DMA_DEV32_SD_MM1_TX = 32, - DB8500_DMA_DEV33_SPI2_TX = 33, - DB8500_DMA_DEV34_I2C3_TX2 = 34, - DB8500_DMA_DEV35_SPI1_TX = 35, - DB8500_DMA_DEV36_USB_OTG_OEP_3_11 = 36, - DB8500_DMA_DEV37_USB_OTG_OEP_2_10 = 37, - DB8500_DMA_DEV38_USB_OTG_OEP_1_9 = 38, - DB8500_DMA_DEV39_USB_OTG_OEP_8 = 39, - DB8500_DMA_DEV40_SPI3_TX = 40, - DB8500_DMA_DEV41_SD_MM3_TX = 41, - DB8500_DMA_DEV42_SD_MM4_TX = 42, - DB8500_DMA_DEV43_SD_MM5_TX = 43, - DB8500_DMA_DEV44_DST_SXA4_RX_TX = 44, - DB8500_DMA_DEV45_DST_SXA5_RX_TX = 45, - DB8500_DMA_DEV46_SLIM0_CH8_TX_DST_SXA6_RX_TX = 46, - DB8500_DMA_DEV47_SLIM0_CH9_TX_DST_SXA7_RX_TX = 47, - DB8500_DMA_DEV48_CAC1_TX = 48, - DB8500_DMA_DEV49_CAC1_TX_HAC1_TX = 49, - DB8500_DMA_DEV50_HAC1_TX = 50, - DB8500_DMA_MEMCPY_TX_0 = 51, - DB8500_DMA_DEV52_SLIM1_CH4_TX_HSI_TX_CH4 = 52, - DB8500_DMA_DEV53_SLIM1_CH5_TX_HSI_TX_CH5 = 53, - DB8500_DMA_DEV54_SLIM1_CH6_TX_HSI_TX_CH6 = 54, - DB8500_DMA_DEV55_SLIM1_CH7_TX_HSI_TX_CH7 = 55, - DB8500_DMA_MEMCPY_TX_1 = 56, - DB8500_DMA_MEMCPY_TX_2 = 57, - DB8500_DMA_MEMCPY_TX_3 = 58, - DB8500_DMA_MEMCPY_TX_4 = 59, - DB8500_DMA_MEMCPY_TX_5 = 60, - DB8500_DMA_DEV61_CAC0_TX = 61, - DB8500_DMA_DEV62_CAC0_TX_HAC0_TX = 62, - DB8500_DMA_DEV63_HAC0_TX = 63, + DB8500_DMA_DEV30_MSP3 = 30, + DB8500_DMA_DEV31_MSP0_SLIM0_CH0 = 31, + DB8500_DMA_DEV32_SD_MM1 = 32, + DB8500_DMA_DEV33_SPI2 = 33, + DB8500_DMA_DEV34_I2C3_RX2_TX2 = 34, + DB8500_DMA_DEV35_SPI1 = 35, + DB8500_DMA_DEV36_USB_OTG_IEP_AND_OEP_3_11 = 36, + DB8500_DMA_DEV37_USB_OTG_IEP_AND_OEP_2_10 = 37, + DB8500_DMA_DEV38_USB_OTG_IEP_AND_OEP_1_9 = 38, + DB8500_DMA_DEV39_USB_OTG_IEP_AND_OEP_8 = 39, + DB8500_DMA_DEV40_SPI3 = 40, + DB8500_DMA_DEV41_SD_MM3 = 41, + DB8500_DMA_DEV42_SD_MM4 = 42, + DB8500_DMA_DEV43_SD_MM5 = 43, + DB8500_DMA_DEV44_SXA4 = 44, + DB8500_DMA_DEV45_SXA5 = 45, + DB8500_DMA_DEV46_SLIM0_CH8_SRC_SXA6 = 46, + DB8500_DMA_DEV47_SLIM0_CH9_SRC_SXA7 = 47, + DB8500_DMA_DEV48_CAC1 = 48, + DB8500_DMA_DEV49_CAC1_TX_HAC1_TX = 49, /* TX only */ + DB8500_DMA_DEV50_HAC1_TX = 50, /* TX only */ + DB8500_DMA_MEMCPY_TX_0 = 51, /* TX only */ + DB8500_DMA_DEV52_SLIM0_CH4_HSI_CH4 = 52, + DB8500_DMA_DEV53_SLIM0_CH5_HSI_CH5 = 53, + DB8500_DMA_DEV54_SLIM0_CH6_HSI_CH6 = 54, + DB8500_DMA_DEV55_SLIM0_CH7_HSI_CH7 = 55, + /* 56 -> 60 are channels reserved for memcpy only */ + DB8500_DMA_DEV61_CAC0 = 61, + DB8500_DMA_DEV62_CAC0_TX_HAC0_TX = 62, /* TX only */ + DB8500_DMA_DEV63_HAC0_TX = 63, /* TX only */ }; #endif diff --git a/arch/arm/mach-ux500/usb.c b/arch/arm/mach-ux500/usb.c index 2dfc72f7cd8a..45af3031dfef 100644 --- a/arch/arm/mach-ux500/usb.c +++ b/arch/arm/mach-ux500/usb.c @@ -15,7 +15,6 @@ #define MUSB_DMA40_RX_CH { \ .mode = STEDMA40_MODE_LOGICAL, \ .dir = STEDMA40_PERIPH_TO_MEM, \ - .dst_dev_type = STEDMA40_DEV_DST_MEMORY, \ .src_info.data_width = STEDMA40_WORD_WIDTH, \ .dst_info.data_width = STEDMA40_WORD_WIDTH, \ .src_info.psize = STEDMA40_PSIZE_LOG_16, \ @@ -25,7 +24,6 @@ #define MUSB_DMA40_TX_CH { \ .mode = STEDMA40_MODE_LOGICAL, \ .dir = STEDMA40_MEM_TO_PERIPH, \ - .src_dev_type = STEDMA40_DEV_SRC_MEMORY, \ .src_info.data_width = STEDMA40_WORD_WIDTH, \ .dst_info.data_width = STEDMA40_WORD_WIDTH, \ .src_info.psize = STEDMA40_PSIZE_LOG_16, \ @@ -125,20 +123,20 @@ struct platform_device ux500_musb_device = { .resource = usb_resources, }; -static inline void ux500_usb_dma_update_rx_ch_config(int *src_dev_type) +static inline void ux500_usb_dma_update_rx_ch_config(int *dev_type) { u32 idx; for (idx = 0; idx < UX500_MUSB_DMA_NUM_RX_CHANNELS; idx++) - musb_dma_rx_ch[idx].src_dev_type = src_dev_type[idx]; + musb_dma_rx_ch[idx].dev_type = dev_type[idx]; } -static inline void ux500_usb_dma_update_tx_ch_config(int *dst_dev_type) +static inline void ux500_usb_dma_update_tx_ch_config(int *dev_type) { u32 idx; for (idx = 0; idx < UX500_MUSB_DMA_NUM_TX_CHANNELS; idx++) - musb_dma_tx_ch[idx].dst_dev_type = dst_dev_type[idx]; + musb_dma_tx_ch[idx].dev_type = dev_type[idx]; } void ux500_add_usb(struct device *parent, resource_size_t base, int irq, diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index d481cb8521d9..63495f6a36f9 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1302,21 +1302,17 @@ static void __d40_config_set_event(struct d40_chan *d40c, static void d40_config_set_event(struct d40_chan *d40c, enum d40_events event_type) { + u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dev_type); + /* Enable event line connected to device (or memcpy) */ if ((d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) || - (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH)) { - u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type); - + (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH)) __d40_config_set_event(d40c, event_type, event, D40_CHAN_REG_SSLNK); - } - - if (d40c->dma_cfg.dir != STEDMA40_PERIPH_TO_MEM) { - u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type); + if (d40c->dma_cfg.dir != STEDMA40_PERIPH_TO_MEM) __d40_config_set_event(d40c, event_type, event, D40_CHAN_REG_SDLNK); - } } static u32 d40_chan_has_events(struct d40_chan *d40c) @@ -1758,8 +1754,6 @@ static int d40_validate_conf(struct d40_chan *d40c, struct stedma40_chan_cfg *conf) { int res = 0; - u32 dst_event_group = D40_TYPE_TO_GROUP(conf->dst_dev_type); - u32 src_event_group = D40_TYPE_TO_GROUP(conf->src_dev_type); bool is_log = conf->mode == STEDMA40_MODE_LOGICAL; if (!conf->dir) { @@ -1767,44 +1761,26 @@ static int d40_validate_conf(struct d40_chan *d40c, res = -EINVAL; } - if (conf->dst_dev_type != STEDMA40_DEV_DST_MEMORY && - d40c->base->plat_data->dev_tx[conf->dst_dev_type] == 0 && - d40c->runtime_addr == 0) { - - chan_err(d40c, "Invalid TX channel address (%d)\n", - conf->dst_dev_type); - res = -EINVAL; - } - - if (conf->src_dev_type != STEDMA40_DEV_SRC_MEMORY && - d40c->base->plat_data->dev_rx[conf->src_dev_type] == 0 && - d40c->runtime_addr == 0) { - chan_err(d40c, "Invalid RX channel address (%d)\n", - conf->src_dev_type); + if ((is_log && conf->dev_type > d40c->base->num_log_chans) || + (!is_log && conf->dev_type > d40c->base->num_phy_chans) || + (conf->dev_type < 0)) { + chan_err(d40c, "Invalid device type (%d)\n", conf->dev_type); res = -EINVAL; } if (conf->dir == STEDMA40_MEM_TO_PERIPH && - conf->dst_dev_type == STEDMA40_DEV_DST_MEMORY) { - chan_err(d40c, "Invalid dst\n"); + d40c->base->plat_data->dev_tx[conf->dev_type] == 0 && + d40c->runtime_addr == 0) { + chan_err(d40c, "Invalid TX channel address (%d)\n", + conf->dev_type); res = -EINVAL; } if (conf->dir == STEDMA40_PERIPH_TO_MEM && - conf->src_dev_type == STEDMA40_DEV_SRC_MEMORY) { - chan_err(d40c, "Invalid src\n"); - res = -EINVAL; - } - - if (conf->src_dev_type == STEDMA40_DEV_SRC_MEMORY && - conf->dst_dev_type == STEDMA40_DEV_DST_MEMORY && is_log) { - chan_err(d40c, "No event line\n"); - res = -EINVAL; - } - - if (conf->dir == STEDMA40_PERIPH_TO_PERIPH && - (src_event_group != dst_event_group)) { - chan_err(d40c, "Invalid event group\n"); + d40c->base->plat_data->dev_rx[conf->dev_type] == 0 && + d40c->runtime_addr == 0) { + chan_err(d40c, "Invalid RX channel address (%d)\n", + conf->dev_type); res = -EINVAL; } @@ -1925,7 +1901,7 @@ out: static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user) { - int dev_type; + int dev_type = d40c->dma_cfg.dev_type; int event_group; int event_line; struct d40_phy_res *phys; @@ -1940,13 +1916,11 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user) num_phy_chans = d40c->base->num_phy_chans; if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) { - dev_type = d40c->dma_cfg.src_dev_type; log_num = 2 * dev_type; is_src = true; } else if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_PERIPH || d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) { /* dst event lines are used for logical memcpy */ - dev_type = d40c->dma_cfg.dst_dev_type; log_num = 2 * dev_type + 1; is_src = false; } else @@ -2058,8 +2032,7 @@ static int d40_config_memcpy(struct d40_chan *d40c) if (dma_has_cap(DMA_MEMCPY, cap) && !dma_has_cap(DMA_SLAVE, cap)) { d40c->dma_cfg = dma40_memcpy_conf_log; - d40c->dma_cfg.src_dev_type = STEDMA40_DEV_SRC_MEMORY; - d40c->dma_cfg.dst_dev_type = dma40_memcpy_channels[d40c->chan.chan_id]; + d40c->dma_cfg.dev_type = dma40_memcpy_channels[d40c->chan.chan_id]; } else if (dma_has_cap(DMA_MEMCPY, cap) && dma_has_cap(DMA_SLAVE, cap)) { @@ -2076,7 +2049,7 @@ static int d40_free_dma(struct d40_chan *d40c) { int res = 0; - u32 event; + u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dev_type); struct d40_phy_res *phy = d40c->phy_chan; bool is_src; @@ -2095,13 +2068,11 @@ static int d40_free_dma(struct d40_chan *d40c) } if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_PERIPH || - d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) { - event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type); + d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) is_src = false; - } else if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) { - event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type); + else if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) is_src = true; - } else { + else { chan_err(d40c, "Unknown direction\n"); return -EINVAL; } @@ -2142,7 +2113,7 @@ static bool d40_is_paused(struct d40_chan *d40c) unsigned long flags; void __iomem *active_reg; u32 status; - u32 event; + u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dev_type); spin_lock_irqsave(&d40c->lock, flags); @@ -2163,10 +2134,8 @@ static bool d40_is_paused(struct d40_chan *d40c) if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_PERIPH || d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) { - event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type); status = readl(chanbase + D40_CHAN_REG_SDLNK); } else if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) { - event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type); status = readl(chanbase + D40_CHAN_REG_SSLNK); } else { chan_err(d40c, "Unknown direction\n"); @@ -2308,9 +2277,9 @@ d40_get_dev_addr(struct d40_chan *chan, enum dma_transfer_direction direction) return chan->runtime_addr; if (direction == DMA_DEV_TO_MEM) - addr = plat->dev_rx[cfg->src_dev_type]; + addr = plat->dev_rx[cfg->dev_type]; else if (direction == DMA_MEM_TO_DEV) - addr = plat->dev_tx[cfg->dst_dev_type]; + addr = plat->dev_tx[cfg->dev_type]; return addr; } @@ -2441,11 +2410,11 @@ static void d40_set_prio_realtime(struct d40_chan *d40c) if ((d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) || (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH)) - __d40_set_prio_rt(d40c, d40c->dma_cfg.src_dev_type, true); + __d40_set_prio_rt(d40c, d40c->dma_cfg.dev_type, true); if ((d40c->dma_cfg.dir == STEDMA40_MEM_TO_PERIPH) || (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH)) - __d40_set_prio_rt(d40c, d40c->dma_cfg.dst_dev_type, false); + __d40_set_prio_rt(d40c, d40c->dma_cfg.dev_type, false); } /* DMA ENGINE functions */ @@ -2489,10 +2458,10 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) d40c->lcpa = d40c->base->lcpa_base + - d40c->dma_cfg.src_dev_type * D40_LCPA_CHAN_SIZE; + d40c->dma_cfg.dev_type * D40_LCPA_CHAN_SIZE; else d40c->lcpa = d40c->base->lcpa_base + - d40c->dma_cfg.dst_dev_type * + d40c->dma_cfg.dev_type * D40_LCPA_CHAN_SIZE + D40_LCPA_CHAN_DST_DELTA; } @@ -2755,7 +2724,7 @@ static int d40_set_runtime_config(struct dma_chan *chan, if (config->direction == DMA_DEV_TO_MEM) { dma_addr_t dev_addr_rx = - d40c->base->plat_data->dev_rx[cfg->src_dev_type]; + d40c->base->plat_data->dev_rx[cfg->dev_type]; config_addr = config->src_addr; if (dev_addr_rx) @@ -2778,7 +2747,7 @@ static int d40_set_runtime_config(struct dma_chan *chan, } else if (config->direction == DMA_MEM_TO_DEV) { dma_addr_t dev_addr_tx = - d40c->base->plat_data->dev_tx[cfg->dst_dev_type]; + d40c->base->plat_data->dev_tx[cfg->dev_type]; config_addr = config->dst_addr; if (dev_addr_tx) diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c index 7180e0d41722..5eb6c10beae1 100644 --- a/drivers/dma/ste_dma40_ll.c +++ b/drivers/dma/ste_dma40_ll.c @@ -63,7 +63,7 @@ void d40_phy_cfg(struct stedma40_chan_cfg *cfg, (cfg->dir == STEDMA40_PERIPH_TO_PERIPH)) { /* Set master port to 1 */ src |= 1 << D40_SREG_CFG_MST_POS; - src |= D40_TYPE_TO_EVENT(cfg->src_dev_type); + src |= D40_TYPE_TO_EVENT(cfg->dev_type); if (cfg->src_info.flow_ctrl == STEDMA40_NO_FLOW_CTRL) src |= 1 << D40_SREG_CFG_PHY_TM_POS; @@ -74,7 +74,7 @@ void d40_phy_cfg(struct stedma40_chan_cfg *cfg, (cfg->dir == STEDMA40_PERIPH_TO_PERIPH)) { /* Set master port to 1 */ dst |= 1 << D40_SREG_CFG_MST_POS; - dst |= D40_TYPE_TO_EVENT(cfg->dst_dev_type); + dst |= D40_TYPE_TO_EVENT(cfg->dev_type); if (cfg->dst_info.flow_ctrl == STEDMA40_NO_FLOW_CTRL) dst |= 1 << D40_SREG_CFG_PHY_TM_POS; diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index 869c571c8c08..9e42a67d0cd5 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -109,8 +109,7 @@ struct stedma40_half_channel_info { * version 3+, i.e DB8500v2+ * @mode: channel mode: physical, logical, or operation * @mode_opt: options for the chosen channel mode - * @src_dev_type: Src device type - * @dst_dev_type: Dst device type + * @dev_type: src/dst device type (driver uses dir to figure out which) * @src_info: Parameters for dst half channel * @dst_info: Parameters for dst half channel * @use_fixed_channel: if true, use physical channel specified by phy_channel @@ -126,8 +125,7 @@ struct stedma40_chan_cfg { bool realtime; enum stedma40_mode mode; enum stedma40_mode_opt mode_opt; - int src_dev_type; - int dst_dev_type; + int dev_type; struct stedma40_half_channel_info src_info; struct stedma40_half_channel_info dst_info; -- cgit v1.3-18-gd494 From db72da92103e3023e6a4fdfe65183b21bfe5d883 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 3 May 2013 15:32:03 +0100 Subject: dmaengine: ste_dma40: Calculate number of logical channels from physical ones This change will cost ~25KB of memory, but it's worth the trade-off, as it removes a great deal of overhead. It means that instead of only allocating memory for the logical channels in use, it does so for all available ones, which is 32 per physical channel. However, this now means we can remove some platform data and we don't have to worry about adding vendor specific variables to Device Tree. Acked-by: Vinod Koul Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- arch/arm/mach-ux500/devices-db8500.c | 1 - drivers/dma/ste_dma40.c | 16 ++++++---------- include/linux/platform_data/dma-ste-dma40.h | 2 -- 3 files changed, 6 insertions(+), 13 deletions(-) (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-ux500/devices-db8500.c b/arch/arm/mach-ux500/devices-db8500.c index 7989c564e47a..130f3d9917e7 100644 --- a/arch/arm/mach-ux500/devices-db8500.c +++ b/arch/arm/mach-ux500/devices-db8500.c @@ -121,7 +121,6 @@ static const dma_addr_t dma40_rx_map[DB8500_DMA_NR_DEV] = { }; static struct stedma40_platform_data dma40_plat_data = { - .dev_len = DB8500_DMA_NR_DEV, .dev_rx = dma40_rx_map, .dev_tx = dma40_tx_map, .disabled_channels = {-1}, diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 367ef15a3cd8..f25c9ccf28e8 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -45,6 +45,9 @@ #define D40_LCLA_LINK_PER_EVENT_GRP 128 #define D40_LCLA_END D40_LCLA_LINK_PER_EVENT_GRP +/* Max number of logical channels per physical channel */ +#define D40_MAX_LOG_CHAN_PER_PHY 32 + /* Attempts before giving up to trying to get pages that are aligned */ #define MAX_LCLA_ALLOC_ATTEMPTS 256 @@ -3210,6 +3213,8 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) else num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4; + num_log_chans = num_phy_chans * D40_MAX_LOG_CHAN_PER_PHY; + dev_info(&pdev->dev, "hardware revision: %d @ 0x%x with %d physical channels\n", rev, res->start, num_phy_chans); @@ -3219,15 +3224,6 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) goto failure; } - /* Count the number of logical channels in use */ - for (i = 0; i < plat_data->dev_len; i++) - if (plat_data->dev_rx[i] != 0) - num_log_chans++; - - for (i = 0; i < plat_data->dev_len; i++) - if (plat_data->dev_tx[i] != 0) - num_log_chans++; - base = kzalloc(ALIGN(sizeof(struct d40_base), 4) + (num_phy_chans + num_log_chans + ARRAY_SIZE(dma40_memcpy_channels)) * sizeof(struct d40_chan), GFP_KERNEL); @@ -3295,7 +3291,7 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) * The max number of logical channels are event lines for all * src devices and dst devices */ - base->lookup_log_chans = kzalloc(plat_data->dev_len * 2 * + base->lookup_log_chans = kzalloc(num_log_chans * sizeof(struct d40_chan *), GFP_KERNEL); if (!base->lookup_log_chans) diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index 9e42a67d0cd5..c54af61c9e48 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -136,7 +136,6 @@ struct stedma40_chan_cfg { /** * struct stedma40_platform_data - Configuration struct for the dma device. * - * @dev_len: length of dev_tx and dev_rx * @dev_tx: mapping between destination event line and io address * @dev_rx: mapping between source event line and io address * @disabled_channels: A vector, ending with -1, that marks physical channels @@ -153,7 +152,6 @@ struct stedma40_chan_cfg { * for 'multiple of 4' channels, like 8. */ struct stedma40_platform_data { - u32 dev_len; const dma_addr_t *dev_tx; const dma_addr_t *dev_rx; int disabled_channels[STEDMA40_MAX_PHYS]; -- cgit v1.3-18-gd494 From 9c3c95147cdd30756a08c511c3c80e8fdf05a36a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 15 May 2013 10:51:32 +0100 Subject: ARM: ux500: Remove DMA address look-up table DMA addresses are now passed as part of the dmaengine API by invoking dmaengine_slave_config(). So there's no requirement for the DMA40 driver to look them up in a table provided by platform data. This method does not fit in well using Device Tree either. Acked-by: Vinod Koul Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- arch/arm/mach-ux500/devices-db8500.c | 80 ----------------------------- include/linux/platform_data/dma-ste-dma40.h | 2 - 2 files changed, 82 deletions(-) (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-ux500/devices-db8500.c b/arch/arm/mach-ux500/devices-db8500.c index bed25a39e68b..e21ffd8c1412 100644 --- a/arch/arm/mach-ux500/devices-db8500.c +++ b/arch/arm/mach-ux500/devices-db8500.c @@ -42,87 +42,7 @@ static struct resource dma40_resources[] = { } }; -/* - * Mapping between destination event lines and physical device address. - * The event line is tied to a device and therefore the address is constant. - * When the address comes from a primecell it will be configured in runtime - * and we set the address to -1 as a placeholder. - */ -static const dma_addr_t dma40_tx_map[DB8500_DMA_NR_DEV] = { - /* MUSB - these will be runtime-reconfigured */ - [DB8500_DMA_DEV39_USB_OTG_IEP_AND_OEP_8] = -1, - [DB8500_DMA_DEV16_USB_OTG_IEP_AND_OEP_7_15] = -1, - [DB8500_DMA_DEV17_USB_OTG_IEP_AND_OEP_6_14] = -1, - [DB8500_DMA_DEV18_USB_OTG_IEP_AND_OEP_5_13] = -1, - [DB8500_DMA_DEV19_USB_OTG_IEP_AND_OEP_4_12] = -1, - [DB8500_DMA_DEV36_USB_OTG_IEP_AND_OEP_3_11] = -1, - [DB8500_DMA_DEV37_USB_OTG_IEP_AND_OEP_2_10] = -1, - [DB8500_DMA_DEV38_USB_OTG_IEP_AND_OEP_1_9] = -1, - /* PrimeCells - run-time configured */ - [DB8500_DMA_DEV0_SPI0] = -1, - [DB8500_DMA_DEV1_SD_MMC0] = -1, - [DB8500_DMA_DEV2_SD_MMC1] = -1, - [DB8500_DMA_DEV3_SD_MMC2] = -1, - [DB8500_DMA_DEV8_SSP0] = -1, - [DB8500_DMA_DEV9_SSP1] = -1, - [DB8500_DMA_DEV11_UART2] = -1, - [DB8500_DMA_DEV12_UART1] = -1, - [DB8500_DMA_DEV13_UART0] = -1, - [DB8500_DMA_DEV28_SD_MM2] = -1, - [DB8500_DMA_DEV29_SD_MM0] = -1, - [DB8500_DMA_DEV32_SD_MM1] = -1, - [DB8500_DMA_DEV33_SPI2] = -1, - [DB8500_DMA_DEV35_SPI1] = -1, - [DB8500_DMA_DEV40_SPI3] = -1, - [DB8500_DMA_DEV41_SD_MM3] = -1, - [DB8500_DMA_DEV42_SD_MM4] = -1, - [DB8500_DMA_DEV43_SD_MM5] = -1, - [DB8500_DMA_DEV14_MSP2] = U8500_MSP2_BASE + MSP_TX_RX_REG_OFFSET, - [DB8500_DMA_DEV30_MSP1] = U8500_MSP1_BASE + MSP_TX_RX_REG_OFFSET, - [DB8500_DMA_DEV31_MSP0_SLIM0_CH0] = U8500_MSP0_BASE + MSP_TX_RX_REG_OFFSET, - [DB8500_DMA_DEV48_CAC1] = U8500_CRYP1_BASE + CRYP1_TX_REG_OFFSET, - [DB8500_DMA_DEV50_HAC1_TX] = U8500_HASH1_BASE + HASH1_TX_REG_OFFSET, -}; - -/* Mapping between source event lines and physical device address */ -static const dma_addr_t dma40_rx_map[DB8500_DMA_NR_DEV] = { - /* MUSB - these will be runtime-reconfigured */ - [DB8500_DMA_DEV39_USB_OTG_IEP_AND_OEP_8] = -1, - [DB8500_DMA_DEV16_USB_OTG_IEP_AND_OEP_7_15] = -1, - [DB8500_DMA_DEV17_USB_OTG_IEP_AND_OEP_6_14] = -1, - [DB8500_DMA_DEV18_USB_OTG_IEP_AND_OEP_5_13] = -1, - [DB8500_DMA_DEV19_USB_OTG_IEP_AND_OEP_4_12] = -1, - [DB8500_DMA_DEV36_USB_OTG_IEP_AND_OEP_3_11] = -1, - [DB8500_DMA_DEV37_USB_OTG_IEP_AND_OEP_2_10] = -1, - [DB8500_DMA_DEV38_USB_OTG_IEP_AND_OEP_1_9] = -1, - /* PrimeCells */ - [DB8500_DMA_DEV0_SPI0] = -1, - [DB8500_DMA_DEV1_SD_MMC0] = -1, - [DB8500_DMA_DEV2_SD_MMC1] = -1, - [DB8500_DMA_DEV3_SD_MMC2] = -1, - [DB8500_DMA_DEV8_SSP0] = -1, - [DB8500_DMA_DEV9_SSP1] = -1, - [DB8500_DMA_DEV11_UART2] = -1, - [DB8500_DMA_DEV12_UART1] = -1, - [DB8500_DMA_DEV13_UART0] = -1, - [DB8500_DMA_DEV28_SD_MM2] = -1, - [DB8500_DMA_DEV29_SD_MM0] = -1, - [DB8500_DMA_DEV32_SD_MM1] = -1, - [DB8500_DMA_DEV33_SPI2] = -1, - [DB8500_DMA_DEV35_SPI1] = -1, - [DB8500_DMA_DEV40_SPI3] = -1, - [DB8500_DMA_DEV41_SD_MM3] = -1, - [DB8500_DMA_DEV42_SD_MM4] = -1, - [DB8500_DMA_DEV43_SD_MM5] = -1, - [DB8500_DMA_DEV14_MSP2] = U8500_MSP2_BASE + MSP_TX_RX_REG_OFFSET, - [DB8500_DMA_DEV30_MSP3] = U8500_MSP3_BASE + MSP_TX_RX_REG_OFFSET, - [DB8500_DMA_DEV31_MSP0_SLIM0_CH0] = U8500_MSP0_BASE + MSP_TX_RX_REG_OFFSET, - [DB8500_DMA_DEV48_CAC1] = U8500_CRYP1_BASE + CRYP1_RX_REG_OFFSET, -}; - struct stedma40_platform_data dma40_plat_data = { - .dev_rx = dma40_rx_map, - .dev_tx = dma40_tx_map, .disabled_channels = {-1}, }; diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index c54af61c9e48..af0064e3e139 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -152,8 +152,6 @@ struct stedma40_chan_cfg { * for 'multiple of 4' channels, like 8. */ struct stedma40_platform_data { - const dma_addr_t *dev_tx; - const dma_addr_t *dev_rx; int disabled_channels[STEDMA40_MAX_PHYS]; int *soft_lli_chans; int num_of_soft_lli_chans; -- cgit v1.3-18-gd494 From 5d489a435169d6e95bafcab048b5acc677c9bca7 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 15 May 2013 10:51:33 +0100 Subject: dmaengine: ste_dma40: Correct copy/paste error 'struct stedma40_half_channel_info's header comment says that it's called 'struct stedma40_chan_cfg'. Let's straighten that out. Signed-off-by: Lee Jones Acked-by: Vinod Koul Signed-off-by: Linus Walleij --- include/linux/platform_data/dma-ste-dma40.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index af0064e3e139..288dc2420ee6 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -86,7 +86,7 @@ enum stedma40_xfer_dir { /** - * struct stedma40_chan_cfg - dst/src channel configuration + * struct stedma40_half_channel_info - dst/src channel configuration * * @big_endian: true if the src/dst should be read as big endian * @data_width: Data width of the src/dst hardware -- cgit v1.3-18-gd494 From b6b5e76bb8bb22ecff90a7840dc4845d63328289 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 23 May 2013 20:14:50 +0200 Subject: ASoC: Add ssm2518 support This patch adds a ASoC CODEC driver for the SSM2516. The SSM2516 is a stereo Class-D audio amplifier with an I2S interface for audio in and a built-in dynamic range control processor. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/ssm2518.txt | 20 + include/linux/platform_data/ssm2518.h | 22 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/ssm2518.c | 856 +++++++++++++++++++++ sound/soc/codecs/ssm2518.h | 20 + 6 files changed, 924 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/ssm2518.txt create mode 100644 include/linux/platform_data/ssm2518.h create mode 100644 sound/soc/codecs/ssm2518.c create mode 100644 sound/soc/codecs/ssm2518.h (limited to 'include/linux/platform_data') diff --git a/Documentation/devicetree/bindings/sound/ssm2518.txt b/Documentation/devicetree/bindings/sound/ssm2518.txt new file mode 100644 index 000000000000..59381a778c79 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ssm2518.txt @@ -0,0 +1,20 @@ +SSM2518 audio amplifier + +This device supports I2C only. + +Required properties: + - compatible : Must be "adi,ssm2518" + - reg : the I2C address of the device. This will either be 0x34 (ADDR pin low) + or 0x35 (ADDR pin high) + +Optional properties: + - gpios : GPIO connected to the nSD pin. If the property is not present it is + assumed that the nSD pin is hardwired to always on. + +Example: + + ssm2518: ssm2518@34 { + compatible = "adi,ssm2518"; + reg = <0x34>; + gpios = <&gpio 5 0>; + }; diff --git a/include/linux/platform_data/ssm2518.h b/include/linux/platform_data/ssm2518.h new file mode 100644 index 000000000000..9a8e3ea287e3 --- /dev/null +++ b/include/linux/platform_data/ssm2518.h @@ -0,0 +1,22 @@ +/* + * SSM2518 amplifier audio driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#ifndef __LINUX_PLATFORM_DATA_SSM2518_H__ +#define __LINUX_PLATFORM_DATA_SSM2518_H__ + +/** + * struct ssm2518_platform_data - Platform data for the ssm2518 driver + * @enable_gpio: GPIO connected to the nSD pin. Set to -1 if the nSD pin is + * hardwired. + */ +struct ssm2518_platform_data { + int enable_gpio; +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 2f45f00e31b0..d76609adb85b 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -60,6 +60,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_SI476X if MFD_SI476X_CORE select SND_SOC_SN95031 if INTEL_SCU_IPC select SND_SOC_SPDIF + select SND_SOC_SSM2518 if I2C select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI select SND_SOC_STA32X if I2C select SND_SOC_STA529 if I2C @@ -313,6 +314,9 @@ config SND_SOC_SN95031 config SND_SOC_SPDIF tristate +config SND_SOC_SSM2518 + tristate + config SND_SOC_SSM2602 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b9e41c9a1f4c..d85be48a6c07 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -52,6 +52,7 @@ snd-soc-si476x-objs := si476x.o snd-soc-sn95031-objs := sn95031.o snd-soc-spdif-tx-objs := spdif_transciever.o snd-soc-spdif-rx-objs := spdif_receiver.o +snd-soc-ssm2518-objs := ssm2518.o snd-soc-ssm2602-objs := ssm2602.o snd-soc-sta32x-objs := sta32x.o snd-soc-sta529-objs := sta529.o @@ -176,6 +177,7 @@ obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o +obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c new file mode 100644 index 000000000000..3139a1bde295 --- /dev/null +++ b/sound/soc/codecs/ssm2518.c @@ -0,0 +1,856 @@ +/* + * SSM2518 amplifier audio driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ssm2518.h" + +#define SSM2518_REG_POWER1 0x00 +#define SSM2518_REG_CLOCK 0x01 +#define SSM2518_REG_SAI_CTRL1 0x02 +#define SSM2518_REG_SAI_CTRL2 0x03 +#define SSM2518_REG_CHAN_MAP 0x04 +#define SSM2518_REG_LEFT_VOL 0x05 +#define SSM2518_REG_RIGHT_VOL 0x06 +#define SSM2518_REG_MUTE_CTRL 0x07 +#define SSM2518_REG_FAULT_CTRL 0x08 +#define SSM2518_REG_POWER2 0x09 +#define SSM2518_REG_DRC_1 0x0a +#define SSM2518_REG_DRC_2 0x0b +#define SSM2518_REG_DRC_3 0x0c +#define SSM2518_REG_DRC_4 0x0d +#define SSM2518_REG_DRC_5 0x0e +#define SSM2518_REG_DRC_6 0x0f +#define SSM2518_REG_DRC_7 0x10 +#define SSM2518_REG_DRC_8 0x11 +#define SSM2518_REG_DRC_9 0x12 + +#define SSM2518_POWER1_RESET BIT(7) +#define SSM2518_POWER1_NO_BCLK BIT(5) +#define SSM2518_POWER1_MCS_MASK (0xf << 1) +#define SSM2518_POWER1_MCS_64FS (0x0 << 1) +#define SSM2518_POWER1_MCS_128FS (0x1 << 1) +#define SSM2518_POWER1_MCS_256FS (0x2 << 1) +#define SSM2518_POWER1_MCS_384FS (0x3 << 1) +#define SSM2518_POWER1_MCS_512FS (0x4 << 1) +#define SSM2518_POWER1_MCS_768FS (0x5 << 1) +#define SSM2518_POWER1_MCS_100FS (0x6 << 1) +#define SSM2518_POWER1_MCS_200FS (0x7 << 1) +#define SSM2518_POWER1_MCS_400FS (0x8 << 1) +#define SSM2518_POWER1_SPWDN BIT(0) + +#define SSM2518_CLOCK_ASR BIT(0) + +#define SSM2518_SAI_CTRL1_FMT_MASK (0x3 << 5) +#define SSM2518_SAI_CTRL1_FMT_I2S (0x0 << 5) +#define SSM2518_SAI_CTRL1_FMT_LJ (0x1 << 5) +#define SSM2518_SAI_CTRL1_FMT_RJ_24BIT (0x2 << 5) +#define SSM2518_SAI_CTRL1_FMT_RJ_16BIT (0x3 << 5) + +#define SSM2518_SAI_CTRL1_SAI_MASK (0x7 << 2) +#define SSM2518_SAI_CTRL1_SAI_I2S (0x0 << 2) +#define SSM2518_SAI_CTRL1_SAI_TDM_2 (0x1 << 2) +#define SSM2518_SAI_CTRL1_SAI_TDM_4 (0x2 << 2) +#define SSM2518_SAI_CTRL1_SAI_TDM_8 (0x3 << 2) +#define SSM2518_SAI_CTRL1_SAI_TDM_16 (0x4 << 2) +#define SSM2518_SAI_CTRL1_SAI_MONO (0x5 << 2) + +#define SSM2518_SAI_CTRL1_FS_MASK (0x3) +#define SSM2518_SAI_CTRL1_FS_8000_12000 (0x0) +#define SSM2518_SAI_CTRL1_FS_16000_24000 (0x1) +#define SSM2518_SAI_CTRL1_FS_32000_48000 (0x2) +#define SSM2518_SAI_CTRL1_FS_64000_96000 (0x3) + +#define SSM2518_SAI_CTRL2_BCLK_INTERAL BIT(7) +#define SSM2518_SAI_CTRL2_LRCLK_PULSE BIT(6) +#define SSM2518_SAI_CTRL2_LRCLK_INVERT BIT(5) +#define SSM2518_SAI_CTRL2_MSB BIT(4) +#define SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK (0x3 << 2) +#define SSM2518_SAI_CTRL2_SLOT_WIDTH_32 (0x0 << 2) +#define SSM2518_SAI_CTRL2_SLOT_WIDTH_24 (0x1 << 2) +#define SSM2518_SAI_CTRL2_SLOT_WIDTH_16 (0x2 << 2) +#define SSM2518_SAI_CTRL2_BCLK_INVERT BIT(1) + +#define SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET 4 +#define SSM2518_CHAN_MAP_RIGHT_SLOT_MASK 0xf0 +#define SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET 0 +#define SSM2518_CHAN_MAP_LEFT_SLOT_MASK 0x0f + +#define SSM2518_MUTE_CTRL_ANA_GAIN BIT(5) +#define SSM2518_MUTE_CTRL_MUTE_MASTER BIT(0) + +#define SSM2518_POWER2_APWDN BIT(0) + +#define SSM2518_DAC_MUTE BIT(6) +#define SSM2518_DAC_FS_MASK 0x07 +#define SSM2518_DAC_FS_8000 0x00 +#define SSM2518_DAC_FS_16000 0x01 +#define SSM2518_DAC_FS_32000 0x02 +#define SSM2518_DAC_FS_64000 0x03 +#define SSM2518_DAC_FS_128000 0x04 + +struct ssm2518 { + struct regmap *regmap; + bool right_j; + + unsigned int sysclk; + const struct snd_pcm_hw_constraint_list *constraints; + + int enable_gpio; +}; + +static const struct reg_default ssm2518_reg_defaults[] = { + { 0x00, 0x05 }, + { 0x01, 0x00 }, + { 0x02, 0x02 }, + { 0x03, 0x00 }, + { 0x04, 0x10 }, + { 0x05, 0x40 }, + { 0x06, 0x40 }, + { 0x07, 0x81 }, + { 0x08, 0x0c }, + { 0x09, 0x99 }, + { 0x0a, 0x7c }, + { 0x0b, 0x5b }, + { 0x0c, 0x57 }, + { 0x0d, 0x89 }, + { 0x0e, 0x8c }, + { 0x0f, 0x77 }, + { 0x10, 0x26 }, + { 0x11, 0x1c }, + { 0x12, 0x97 }, +}; + +static const DECLARE_TLV_DB_MINMAX_MUTE(ssm2518_vol_tlv, -7125, 2400); +static const DECLARE_TLV_DB_SCALE(ssm2518_compressor_tlv, -3400, 200, 0); +static const DECLARE_TLV_DB_SCALE(ssm2518_expander_tlv, -8100, 300, 0); +static const DECLARE_TLV_DB_SCALE(ssm2518_noise_gate_tlv, -9600, 300, 0); +static const DECLARE_TLV_DB_SCALE(ssm2518_post_drc_tlv, -2400, 300, 0); + +static const DECLARE_TLV_DB_RANGE(ssm2518_limiter_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-2200, 200, 0), + 7, 15, TLV_DB_SCALE_ITEM(-800, 100, 0), +); + +static const char * const ssm2518_drc_peak_detector_attack_time_text[] = { + "0 ms", "0.1 ms", "0.19 ms", "0.37 ms", "0.75 ms", "1.5 ms", "3 ms", + "6 ms", "12 ms", "24 ms", "48 ms", "96 ms", "192 ms", "384 ms", + "768 ms", "1536 ms", +}; + +static const char * const ssm2518_drc_peak_detector_release_time_text[] = { + "0 ms", "1.5 ms", "3 ms", "6 ms", "12 ms", "24 ms", "48 ms", "96 ms", + "192 ms", "384 ms", "768 ms", "1536 ms", "3072 ms", "6144 ms", + "12288 ms", "24576 ms" +}; + +static const char * const ssm2518_drc_hold_time_text[] = { + "0 ms", "0.67 ms", "1.33 ms", "2.67 ms", "5.33 ms", "10.66 ms", + "21.32 ms", "42.64 ms", "85.28 ms", "170.56 ms", "341.12 ms", + "682.24 ms", "1364 ms", +}; + +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum, + SSM2518_REG_DRC_2, 4, ssm2518_drc_peak_detector_attack_time_text); +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum, + SSM2518_REG_DRC_2, 0, ssm2518_drc_peak_detector_release_time_text); +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum, + SSM2518_REG_DRC_6, 4, ssm2518_drc_peak_detector_attack_time_text); +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum, + SSM2518_REG_DRC_6, 0, ssm2518_drc_peak_detector_release_time_text); +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum, + SSM2518_REG_DRC_7, 4, ssm2518_drc_hold_time_text); +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum, + SSM2518_REG_DRC_7, 0, ssm2518_drc_hold_time_text); +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum, + SSM2518_REG_DRC_9, 0, ssm2518_drc_peak_detector_release_time_text); + +static const struct snd_kcontrol_new ssm2518_snd_controls[] = { + SOC_SINGLE("Playback De-emphasis Switch", SSM2518_REG_MUTE_CTRL, + 4, 1, 0), + SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2518_REG_LEFT_VOL, + SSM2518_REG_RIGHT_VOL, 0, 0xff, 1, ssm2518_vol_tlv), + SOC_DOUBLE("Master Playback Switch", SSM2518_REG_MUTE_CTRL, 2, 1, 1, 1), + + SOC_SINGLE("Amp Low Power Mode Switch", SSM2518_REG_POWER2, 4, 1, 0), + SOC_SINGLE("DAC Low Power Mode Switch", SSM2518_REG_POWER2, 3, 1, 0), + + SOC_SINGLE("DRC Limiter Switch", SSM2518_REG_DRC_1, 5, 1, 0), + SOC_SINGLE("DRC Compressor Switch", SSM2518_REG_DRC_1, 4, 1, 0), + SOC_SINGLE("DRC Expander Switch", SSM2518_REG_DRC_1, 3, 1, 0), + SOC_SINGLE("DRC Noise Gate Switch", SSM2518_REG_DRC_1, 2, 1, 0), + SOC_DOUBLE("DRC Switch", SSM2518_REG_DRC_1, 0, 1, 1, 0), + + SOC_SINGLE_TLV("DRC Limiter Threshold Volume", + SSM2518_REG_DRC_3, 4, 15, 1, ssm2518_limiter_tlv), + SOC_SINGLE_TLV("DRC Compressor Lower Threshold Volume", + SSM2518_REG_DRC_3, 0, 15, 1, ssm2518_compressor_tlv), + SOC_SINGLE_TLV("DRC Expander Upper Threshold Volume", SSM2518_REG_DRC_4, + 4, 15, 1, ssm2518_expander_tlv), + SOC_SINGLE_TLV("DRC Noise Gate Threshold Volume", + SSM2518_REG_DRC_4, 0, 15, 1, ssm2518_noise_gate_tlv), + SOC_SINGLE_TLV("DRC Upper Output Threshold Volume", + SSM2518_REG_DRC_5, 4, 15, 1, ssm2518_limiter_tlv), + SOC_SINGLE_TLV("DRC Lower Output Threshold Volume", + SSM2518_REG_DRC_5, 0, 15, 1, ssm2518_noise_gate_tlv), + SOC_SINGLE_TLV("DRC Post Volume", SSM2518_REG_DRC_8, + 2, 15, 1, ssm2518_post_drc_tlv), + + SOC_ENUM("DRC Peak Detector Attack Time", + ssm2518_drc_peak_detector_attack_time_enum), + SOC_ENUM("DRC Peak Detector Release Time", + ssm2518_drc_peak_detector_release_time_enum), + SOC_ENUM("DRC Attack Time", ssm2518_drc_attack_time_enum), + SOC_ENUM("DRC Decay Time", ssm2518_drc_decay_time_enum), + SOC_ENUM("DRC Hold Time", ssm2518_drc_hold_time_enum), + SOC_ENUM("DRC Noise Gate Hold Time", + ssm2518_drc_noise_gate_hold_time_enum), + SOC_ENUM("DRC RMS Averaging Time", ssm2518_drc_rms_averaging_time_enum), +}; + +static const struct snd_soc_dapm_widget ssm2518_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DACL", "HiFi Playback", SSM2518_REG_POWER2, 1, 1), + SND_SOC_DAPM_DAC("DACR", "HiFi Playback", SSM2518_REG_POWER2, 2, 1), + + SND_SOC_DAPM_OUTPUT("OUTL"), + SND_SOC_DAPM_OUTPUT("OUTR"), +}; + +static const struct snd_soc_dapm_route ssm2518_routes[] = { + { "OUTL", NULL, "DACL" }, + { "OUTR", NULL, "DACR" }, +}; + +struct ssm2518_mcs_lut { + unsigned int rate; + const unsigned int *sysclks; +}; + +static const unsigned int ssm2518_sysclks_2048000[] = { + 2048000, 4096000, 8192000, 12288000, 16384000, 24576000, + 3200000, 6400000, 12800000, 0 +}; + +static const unsigned int ssm2518_sysclks_2822000[] = { + 2822000, 5644800, 11289600, 16934400, 22579200, 33868800, + 4410000, 8820000, 17640000, 0 +}; + +static const unsigned int ssm2518_sysclks_3072000[] = { + 3072000, 6144000, 12288000, 16384000, 24576000, 38864000, + 4800000, 9600000, 19200000, 0 +}; + +static const struct ssm2518_mcs_lut ssm2518_mcs_lut[] = { + { 8000, ssm2518_sysclks_2048000, }, + { 11025, ssm2518_sysclks_2822000, }, + { 12000, ssm2518_sysclks_3072000, }, + { 16000, ssm2518_sysclks_2048000, }, + { 24000, ssm2518_sysclks_3072000, }, + { 22050, ssm2518_sysclks_2822000, }, + { 32000, ssm2518_sysclks_2048000, }, + { 44100, ssm2518_sysclks_2822000, }, + { 48000, ssm2518_sysclks_3072000, }, + { 96000, ssm2518_sysclks_3072000, }, +}; + +static const unsigned int ssm2518_rates_2048000[] = { + 8000, 16000, 32000, +}; + +static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2048000 = { + .list = ssm2518_rates_2048000, + .count = ARRAY_SIZE(ssm2518_rates_2048000), +}; + +static const unsigned int ssm2518_rates_2822000[] = { + 11025, 22050, 44100, +}; + +static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2822000 = { + .list = ssm2518_rates_2822000, + .count = ARRAY_SIZE(ssm2518_rates_2822000), +}; + +static const unsigned int ssm2518_rates_3072000[] = { + 12000, 24000, 48000, 96000, +}; + +static const struct snd_pcm_hw_constraint_list ssm2518_constraints_3072000 = { + .list = ssm2518_rates_3072000, + .count = ARRAY_SIZE(ssm2518_rates_3072000), +}; + +static const unsigned int ssm2518_rates_12288000[] = { + 8000, 12000, 16000, 24000, 32000, 48000, 96000, +}; + +static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = { + .list = ssm2518_rates_12288000, + .count = ARRAY_SIZE(ssm2518_rates_12288000), +}; + +static unsigned int ssm2518_lookup_mcs(struct ssm2518 *ssm2518, + unsigned int rate) +{ + const unsigned int *sysclks = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(ssm2518_mcs_lut); i++) { + if (ssm2518_mcs_lut[i].rate == rate) { + sysclks = ssm2518_mcs_lut[i].sysclks; + break; + } + } + + if (!sysclks) + return -EINVAL; + + for (i = 0; sysclks[i]; i++) { + if (sysclks[i] == ssm2518->sysclk) + return i; + } + + return -EINVAL; +} + +static int ssm2518_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec); + unsigned int rate = params_rate(params); + unsigned int ctrl1, ctrl1_mask; + int mcs; + int ret; + + mcs = ssm2518_lookup_mcs(ssm2518, rate); + if (mcs < 0) + return mcs; + + ctrl1_mask = SSM2518_SAI_CTRL1_FS_MASK; + + if (rate >= 8000 && rate <= 12000) + ctrl1 = SSM2518_SAI_CTRL1_FS_8000_12000; + else if (rate >= 16000 && rate <= 24000) + ctrl1 = SSM2518_SAI_CTRL1_FS_16000_24000; + else if (rate >= 32000 && rate <= 48000) + ctrl1 = SSM2518_SAI_CTRL1_FS_32000_48000; + else if (rate >= 64000 && rate <= 96000) + ctrl1 = SSM2518_SAI_CTRL1_FS_64000_96000; + else + return -EINVAL; + + if (ssm2518->right_j) { + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_16BIT; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT; + break; + default: + return -EINVAL; + } + ctrl1_mask |= SSM2518_SAI_CTRL1_FMT_MASK; + } + + /* Disable auto samplerate detection */ + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_CLOCK, + SSM2518_CLOCK_ASR, SSM2518_CLOCK_ASR); + if (ret < 0) + return ret; + + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, + ctrl1_mask, ctrl1); + if (ret < 0) + return ret; + + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_MCS_MASK, mcs << 1); +} + +static int ssm2518_mute(struct snd_soc_dai *dai, int mute) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int val; + + if (mute) + val = SSM2518_MUTE_CTRL_MUTE_MASTER; + else + val = 0; + + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_MUTE_CTRL, + SSM2518_MUTE_CTRL_MUTE_MASTER, val); +} + +static int ssm2518_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int ctrl1 = 0, ctrl2 = 0; + bool invert_fclk; + int ret; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_fclk = false; + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT; + invert_fclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_fclk = true; + break; + case SND_SOC_DAIFMT_IB_IF: + ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT; + invert_fclk = true; + break; + default: + return -EINVAL; + } + + ssm2518->right_j = false; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ; + invert_fclk = !invert_fclk; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT; + ssm2518->right_j = true; + invert_fclk = !invert_fclk; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE; + ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S; + invert_fclk = false; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE; + ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ; + invert_fclk = false; + break; + default: + return -EINVAL; + } + + if (invert_fclk) + ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_INVERT; + + ret = regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, ctrl1); + if (ret) + return ret; + + return regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, ctrl2); +} + +static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable) +{ + int ret = 0; + + if (!enable) { + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_SPWDN, SSM2518_POWER1_SPWDN); + regcache_mark_dirty(ssm2518->regmap); + } + + if (gpio_is_valid(ssm2518->enable_gpio)) + gpio_set_value(ssm2518->enable_gpio, enable); + + regcache_cache_only(ssm2518->regmap, !enable); + + if (enable) { + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_SPWDN | SSM2518_POWER1_RESET, 0x00); + regcache_sync(ssm2518->regmap); + } + + return ret; +} + +static int ssm2518_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + ret = ssm2518_set_power(ssm2518, true); + break; + case SND_SOC_BIAS_OFF: + ret = ssm2518_set_power(ssm2518, false); + break; + } + + if (ret) + return ret; + + codec->dapm.bias_level = level; + + return 0; +} + +static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int ctrl1, ctrl2; + int left_slot, right_slot; + int ret; + + if (slots == 0) + return regmap_update_bits(ssm2518->regmap, + SSM2518_REG_SAI_CTRL1, SSM2518_SAI_CTRL1_SAI_MASK, + SSM2518_SAI_CTRL1_SAI_I2S); + + if (tx_mask == 0 || tx_mask != 0) + return -EINVAL; + + if (slots == 1) { + if (tx_mask != 1) + return -EINVAL; + left_slot = 0; + right_slot = 0; + } else { + /* We assume the left channel < right channel */ + left_slot = ffs(tx_mask); + tx_mask &= ~(1 << tx_mask); + if (tx_mask == 0) { + right_slot = left_slot; + } else { + right_slot = ffs(tx_mask); + tx_mask &= ~(1 << tx_mask); + } + } + + if (tx_mask != 0 || left_slot >= slots || right_slot >= slots) + return -EINVAL; + + switch (width) { + case 16: + ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_16; + break; + case 24: + ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_24; + break; + case 32: + ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_32; + break; + default: + return -EINVAL; + } + + switch (slots) { + case 1: + ctrl1 = SSM2518_SAI_CTRL1_SAI_MONO; + break; + case 2: + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_2; + break; + case 4: + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_4; + break; + case 8: + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_8; + break; + case 16: + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_16; + break; + default: + return -EINVAL; + } + + ret = regmap_write(ssm2518->regmap, SSM2518_REG_CHAN_MAP, + (left_slot << SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET) | + (right_slot << SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET)); + if (ret) + return ret; + + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, + SSM2518_SAI_CTRL1_SAI_MASK, ctrl1); + if (ret) + return ret; + + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, + SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK, ctrl2); +} + +static int ssm2518_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec); + + if (ssm2518->constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, ssm2518->constraints); + + return 0; +} + +#define SSM2518_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32) + +static const struct snd_soc_dai_ops ssm2518_dai_ops = { + .startup = ssm2518_startup, + .hw_params = ssm2518_hw_params, + .digital_mute = ssm2518_mute, + .set_fmt = ssm2518_set_dai_fmt, + .set_tdm_slot = ssm2518_set_tdm_slot, +}; + +static struct snd_soc_dai_driver ssm2518_dai = { + .name = "ssm2518-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SSM2518_FORMATS, + }, + .ops = &ssm2518_dai_ops, +}; + +static int ssm2518_probe(struct snd_soc_codec *codec) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec); + int ret; + + codec->control_data = ssm2518->regmap; + ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + return ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF); +} + +static int ssm2518_remove(struct snd_soc_codec *codec) +{ + ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id, + int source, unsigned int freq, int dir) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec); + unsigned int val; + + if (clk_id != SSM2518_SYSCLK) + return -EINVAL; + + switch (source) { + case SSM2518_SYSCLK_SRC_MCLK: + val = 0; + break; + case SSM2518_SYSCLK_SRC_BCLK: + /* In this case the bitclock is used as the system clock, and + * the bitclock signal needs to be connected to the MCLK pin and + * the BCLK pin is left unconnected */ + val = SSM2518_POWER1_NO_BCLK; + break; + default: + return -EINVAL; + } + + switch (freq) { + case 0: + ssm2518->constraints = NULL; + break; + case 2048000: + case 4096000: + case 8192000: + case 3200000: + case 6400000: + case 12800000: + ssm2518->constraints = &ssm2518_constraints_2048000; + break; + case 2822000: + case 5644800: + case 11289600: + case 16934400: + case 22579200: + case 33868800: + case 4410000: + case 8820000: + case 17640000: + ssm2518->constraints = &ssm2518_constraints_2822000; + break; + case 3072000: + case 6144000: + case 38864000: + case 4800000: + case 9600000: + case 19200000: + ssm2518->constraints = &ssm2518_constraints_3072000; + break; + case 12288000: + case 16384000: + case 24576000: + ssm2518->constraints = &ssm2518_constraints_12288000; + break; + default: + return -EINVAL; + } + + ssm2518->sysclk = freq; + + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_NO_BCLK, val); +} + +static struct snd_soc_codec_driver ssm2518_codec_driver = { + .probe = ssm2518_probe, + .remove = ssm2518_remove, + .set_bias_level = ssm2518_set_bias_level, + .set_sysclk = ssm2518_set_sysclk, + .idle_bias_off = true, + + .controls = ssm2518_snd_controls, + .num_controls = ARRAY_SIZE(ssm2518_snd_controls), + .dapm_widgets = ssm2518_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets), + .dapm_routes = ssm2518_routes, + .num_dapm_routes = ARRAY_SIZE(ssm2518_routes), +}; + +static bool ssm2518_register_volatile(struct device *dev, unsigned int reg) +{ + return false; +} + +static const struct regmap_config ssm2518_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + + .max_register = SSM2518_REG_DRC_9, + .volatile_reg = ssm2518_register_volatile, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = ssm2518_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults), +}; + +static int ssm2518_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct ssm2518_platform_data *pdata = i2c->dev.platform_data; + struct ssm2518 *ssm2518; + int ret; + + ssm2518 = devm_kzalloc(&i2c->dev, sizeof(*ssm2518), GFP_KERNEL); + if (ssm2518 == NULL) + return -ENOMEM; + + if (pdata) { + ssm2518->enable_gpio = pdata->enable_gpio; + } else if (i2c->dev.of_node) { + ssm2518->enable_gpio = of_get_gpio(i2c->dev.of_node, 0); + if (ssm2518->enable_gpio < 0 && ssm2518->enable_gpio != -ENOENT) + return ssm2518->enable_gpio; + } else { + ssm2518->enable_gpio = -1; + } + + if (gpio_is_valid(ssm2518->enable_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, ssm2518->enable_gpio, + GPIOF_OUT_INIT_HIGH, "SSM2518 nSD"); + if (ret) + return ret; + } + + i2c_set_clientdata(i2c, ssm2518); + + ssm2518->regmap = devm_regmap_init_i2c(i2c, &ssm2518_regmap_config); + if (IS_ERR(ssm2518->regmap)) + return PTR_ERR(ssm2518->regmap); + + /* + * The reset bit is obviously volatile, but we need to be able to cache + * the other bits in the register, so we can't just mark the whole + * register as volatile. Since this is the only place where we'll ever + * touch the reset bit just bypass the cache for this operation. + */ + regcache_cache_bypass(ssm2518->regmap, true); + ret = regmap_write(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_RESET); + regcache_cache_bypass(ssm2518->regmap, false); + if (ret) + return ret; + + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER2, + SSM2518_POWER2_APWDN, 0x00); + if (ret) + return ret; + + ret = ssm2518_set_power(ssm2518, false); + if (ret) + return ret; + + return snd_soc_register_codec(&i2c->dev, &ssm2518_codec_driver, + &ssm2518_dai, 1); +} + +static int ssm2518_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id ssm2518_i2c_ids[] = { + { "ssm2518", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids); + +static struct i2c_driver ssm2518_driver = { + .driver = { + .name = "ssm2518", + .owner = THIS_MODULE, + }, + .probe = ssm2518_i2c_probe, + .remove = ssm2518_i2c_remove, + .id_table = ssm2518_i2c_ids, +}; +module_i2c_driver(ssm2518_driver); + +MODULE_DESCRIPTION("ASoC SSM2518 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ssm2518.h b/sound/soc/codecs/ssm2518.h new file mode 100644 index 000000000000..62511d80518e --- /dev/null +++ b/sound/soc/codecs/ssm2518.h @@ -0,0 +1,20 @@ +/* + * SSM2518 amplifier audio driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#ifndef __SND_SOC_CODECS_SSM2518_H__ +#define __SND_SOC_CODECS_SSM2518_H__ + +#define SSM2518_SYSCLK 0 + +enum ssm2518_sysclk_src { + SSM2518_SYSCLK_SRC_MCLK = 0, + SSM2518_SYSCLK_SRC_BCLK = 1, +}; + +#endif -- cgit v1.3-18-gd494 From 1a0483d2a4c2c5e218d415c90d1a62b3b917d34e Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Fri, 3 May 2013 07:33:27 +0200 Subject: clk: si5351: Allow user to define disabled state for every clock output This patch adds platform data and DT bindings to allow to overwrite the stored disabled state for each clock output. Signed-off-by: Marek Belisko Signed-off-by: Sebastian Hesselbarth Signed-off-by: Mike Turquette --- .../devicetree/bindings/clock/silabs,si5351.txt | 5 ++ drivers/clk/clk-si5351.c | 74 +++++++++++++++++++++- drivers/clk/clk-si5351.h | 1 + include/linux/platform_data/si5351.h | 18 ++++++ 4 files changed, 95 insertions(+), 3 deletions(-) (limited to 'include/linux/platform_data') diff --git a/Documentation/devicetree/bindings/clock/silabs,si5351.txt b/Documentation/devicetree/bindings/clock/silabs,si5351.txt index cc374651662c..66c75b2d6158 100644 --- a/Documentation/devicetree/bindings/clock/silabs,si5351.txt +++ b/Documentation/devicetree/bindings/clock/silabs,si5351.txt @@ -44,6 +44,11 @@ Optional child node properties: - silabs,multisynth-source: source pll A(0) or B(1) of corresponding multisynth divider. - silabs,pll-master: boolean, multisynth can change pll frequency. +- silabs,disable-state : clock output disable state, shall be + 0 = clock output is driven LOW when disabled + 1 = clock output is driven HIGH when disabled + 2 = clock output is FLOATING (HIGH-Z) when disabled + 3 = clock output is NEVER disabled ==Example== diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 892728412e9d..efc6d5e9268b 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -851,6 +851,41 @@ static int _si5351_clkout_set_drive_strength( return 0; } +static int _si5351_clkout_set_disable_state( + struct si5351_driver_data *drvdata, int num, + enum si5351_disable_state state) +{ + u8 reg = (num < 4) ? SI5351_CLK3_0_DISABLE_STATE : + SI5351_CLK7_4_DISABLE_STATE; + u8 shift = (num < 4) ? (2 * num) : (2 * (num-4)); + u8 mask = SI5351_CLK_DISABLE_STATE_MASK << shift; + u8 val; + + if (num > 8) + return -EINVAL; + + switch (state) { + case SI5351_DISABLE_LOW: + val = SI5351_CLK_DISABLE_STATE_LOW; + break; + case SI5351_DISABLE_HIGH: + val = SI5351_CLK_DISABLE_STATE_HIGH; + break; + case SI5351_DISABLE_FLOATING: + val = SI5351_CLK_DISABLE_STATE_FLOAT; + break; + case SI5351_DISABLE_NEVER: + val = SI5351_CLK_DISABLE_STATE_NEVER; + break; + default: + return 0; + } + + si5351_set_bits(drvdata, reg, mask, val << shift); + + return 0; +} + static int si5351_clkout_prepare(struct clk_hw *hw) { struct si5351_hw_data *hwdata = @@ -1225,6 +1260,33 @@ static int si5351_dt_parse(struct i2c_client *client) } } + if (!of_property_read_u32(child, "silabs,disable-state", + &val)) { + switch (val) { + case 0: + pdata->clkout[num].disable_state = + SI5351_DISABLE_LOW; + break; + case 1: + pdata->clkout[num].disable_state = + SI5351_DISABLE_HIGH; + break; + case 2: + pdata->clkout[num].disable_state = + SI5351_DISABLE_FLOATING; + break; + case 3: + pdata->clkout[num].disable_state = + SI5351_DISABLE_NEVER; + break; + default: + dev_err(&client->dev, + "invalid disable state %d for clkout %d\n", + val, num); + return -EINVAL; + } + } + if (!of_property_read_u32(child, "clock-frequency", &val)) pdata->clkout[num].rate = val; @@ -1281,9 +1343,6 @@ static int si5351_i2c_probe(struct i2c_client *client, /* Disable interrupts */ si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0); - /* Set disabled output drivers to drive low */ - si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00); - si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00); /* Ensure pll select is on XTAL for Si5351A/B */ if (drvdata->variant != SI5351_VARIANT_C) si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, @@ -1327,6 +1386,15 @@ static int si5351_i2c_probe(struct i2c_client *client, n, pdata->clkout[n].drive); return ret; } + + ret = _si5351_clkout_set_disable_state(drvdata, n, + pdata->clkout[n].disable_state); + if (ret) { + dev_err(&client->dev, + "failed set disable state of clkout%d to %d\n", + n, pdata->clkout[n].disable_state); + return ret; + } } /* register xtal input clock gate */ diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h index af41b5080f43..c0dbf2676872 100644 --- a/drivers/clk/clk-si5351.h +++ b/drivers/clk/clk-si5351.h @@ -81,6 +81,7 @@ #define SI5351_CLK3_0_DISABLE_STATE 24 #define SI5351_CLK7_4_DISABLE_STATE 25 +#define SI5351_CLK_DISABLE_STATE_MASK 3 #define SI5351_CLK_DISABLE_STATE_LOW 0 #define SI5351_CLK_DISABLE_STATE_HIGH 1 #define SI5351_CLK_DISABLE_STATE_FLOAT 2 diff --git a/include/linux/platform_data/si5351.h b/include/linux/platform_data/si5351.h index 92dabcaf6499..54334393ab92 100644 --- a/include/linux/platform_data/si5351.h +++ b/include/linux/platform_data/si5351.h @@ -78,6 +78,23 @@ enum si5351_drive_strength { SI5351_DRIVE_8MA = 8, }; +/** + * enum si5351_disable_state - Si5351 clock output disable state + * @SI5351_DISABLE_DEFAULT: default, do not change eeprom config + * @SI5351_DISABLE_LOW: CLKx is set to a LOW state when disabled + * @SI5351_DISABLE_HIGH: CLKx is set to a HIGH state when disabled + * @SI5351_DISABLE_FLOATING: CLKx is set to a FLOATING state when + * disabled + * @SI5351_DISABLE_NEVER: CLKx is NEVER disabled + */ +enum si5351_disable_state { + SI5351_DISABLE_DEFAULT = 0, + SI5351_DISABLE_LOW, + SI5351_DISABLE_HIGH, + SI5351_DISABLE_FLOATING, + SI5351_DISABLE_NEVER, +}; + /** * struct si5351_clkout_config - Si5351 clock output configuration * @clkout: clkout number @@ -91,6 +108,7 @@ struct si5351_clkout_config { enum si5351_multisynth_src multisynth_src; enum si5351_clkout_src clkout_src; enum si5351_drive_strength drive; + enum si5351_disable_state disable_state; bool pll_master; unsigned long rate; }; -- cgit v1.3-18-gd494 From e8e44a4896a5f0bde1af36a31b7ec662bdaa44ef Mon Sep 17 00:00:00 2001 From: Dongjin Kim Date: Wed, 22 May 2013 05:20:08 +0900 Subject: usb: misc: usb3503: Add to select the ports to disable This patch is to disable the USB ports unconnected to USB3503. In order to disable the port, 'port_off_mask' must be set. * Disable PORT1 only .port_off_mask = USB3503_OFF_PORT1; * Disable PORT1 and PORT3 only .port_off_mask = USB3503_OFF_PORT1 | USB3503_OFF_PORT3; * Enables all ports .port_off_mask = 0; Signed-off-by: Dongjin Kim Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb3503.c | 19 ++++++++++--------- include/linux/platform_data/usb3503.h | 5 +++++ 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index d3a1cce1bf9c..ab24bb345979 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -42,9 +42,6 @@ #define USB3503_NRD 0x09 #define USB3503_PDS 0x0a -#define USB3503_PORT1 (1 << 1) -#define USB3503_PORT2 (1 << 2) -#define USB3503_PORT3 (1 << 3) #define USB3503_SP_ILOCK 0xe7 #define USB3503_SPILOCK_CONNECT (1 << 1) @@ -56,6 +53,7 @@ struct usb3503 { enum usb3503_mode mode; struct i2c_client *client; + u8 port_off_mask; int gpio_intn; int gpio_reset; int gpio_connect; @@ -134,12 +132,14 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) goto err_hubmode; } - /* PDS : Port2,3 Disable For Self Powered Operation */ - err = usb3503_set_bits(i2c, USB3503_PDS, - (USB3503_PORT2 | USB3503_PORT3)); - if (err < 0) { - dev_err(&i2c->dev, "PDS failed (%d)\n", err); - goto err_hubmode; + /* PDS : Disable For Self Powered Operation */ + if (hub->port_off_mask) { + err = usb3503_set_bits(i2c, USB3503_PDS, + hub->port_off_mask); + if (err < 0) { + dev_err(&i2c->dev, "PDS failed (%d)\n", err); + goto err_hubmode; + } } /* CFG1 : SELF_BUS_PWR -> Self-Powerd operation */ @@ -197,6 +197,7 @@ static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) hub->client = i2c; if (pdata) { + hub->port_off_mask = pdata->port_off_mask; hub->gpio_intn = pdata->gpio_intn; hub->gpio_connect = pdata->gpio_connect; hub->gpio_reset = pdata->gpio_reset; diff --git a/include/linux/platform_data/usb3503.h b/include/linux/platform_data/usb3503.h index 85dcc709f7e9..1d1b6ef871f6 100644 --- a/include/linux/platform_data/usb3503.h +++ b/include/linux/platform_data/usb3503.h @@ -3,6 +3,10 @@ #define USB3503_I2C_NAME "usb3503" +#define USB3503_OFF_PORT1 (1 << 1) +#define USB3503_OFF_PORT2 (1 << 2) +#define USB3503_OFF_PORT3 (1 << 3) + enum usb3503_mode { USB3503_MODE_UNKNOWN, USB3503_MODE_HUB, @@ -11,6 +15,7 @@ enum usb3503_mode { struct usb3503_platform_data { enum usb3503_mode initial_mode; + u8 port_off_mask; int gpio_intn; int gpio_connect; int gpio_reset; -- cgit v1.3-18-gd494 From 82c5cde1c4b01a62c9a19c9118b4b434c333ce86 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 30 May 2013 12:53:07 -0700 Subject: ARM: OMAP2+: Remove omap4 ocp2scp pdata This is omap4+ only and no longer needed as omap4+ can be booted using device tree. Also remove the related pdata handling from the driver and the now unneeded platform_data/omap_ocp2scp.h. Cc: Arnd Bergmann Cc: Olof Johansson Reviewed-by: Kishon Vijay Abraham I Acked-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/devices.c | 78 ------------------------------ arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 21 -------- drivers/bus/omap-ocp2scp.c | 60 ----------------------- include/linux/platform_data/omap_ocp2scp.h | 31 ------------ 4 files changed, 190 deletions(-) delete mode 100644 include/linux/platform_data/omap_ocp2scp.h (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 350116906f18..721da484d8ec 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -515,82 +514,6 @@ static void omap_init_vout(void) static inline void omap_init_vout(void) {} #endif -#if defined(CONFIG_OMAP_OCP2SCP) || defined(CONFIG_OMAP_OCP2SCP_MODULE) -static int count_ocp2scp_devices(struct omap_ocp2scp_dev *ocp2scp_dev) -{ - int cnt = 0; - - while (ocp2scp_dev->drv_name != NULL) { - cnt++; - ocp2scp_dev++; - } - - return cnt; -} - -static void __init omap_init_ocp2scp(void) -{ - struct omap_hwmod *oh; - struct platform_device *pdev; - int bus_id = -1, dev_cnt = 0, i; - struct omap_ocp2scp_dev *ocp2scp_dev; - const char *oh_name, *name; - struct omap_ocp2scp_platform_data *pdata; - - if (!cpu_is_omap44xx()) - return; - - oh_name = "ocp2scp_usb_phy"; - name = "omap-ocp2scp"; - - oh = omap_hwmod_lookup(oh_name); - if (!oh) { - pr_err("%s: could not find omap_hwmod for %s\n", __func__, - oh_name); - return; - } - - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - pr_err("%s: No memory for ocp2scp pdata\n", __func__); - return; - } - - ocp2scp_dev = oh->dev_attr; - dev_cnt = count_ocp2scp_devices(ocp2scp_dev); - - if (!dev_cnt) { - pr_err("%s: No devices connected to ocp2scp\n", __func__); - kfree(pdata); - return; - } - - pdata->devices = kzalloc(sizeof(struct omap_ocp2scp_dev *) - * dev_cnt, GFP_KERNEL); - if (!pdata->devices) { - pr_err("%s: No memory for ocp2scp pdata devices\n", __func__); - kfree(pdata); - return; - } - - for (i = 0; i < dev_cnt; i++, ocp2scp_dev++) - pdata->devices[i] = ocp2scp_dev; - - pdata->dev_cnt = dev_cnt; - - pdev = omap_device_build(name, bus_id, oh, pdata, sizeof(*pdata)); - if (IS_ERR(pdev)) { - pr_err("Could not build omap_device for %s %s\n", - name, oh_name); - kfree(pdata->devices); - kfree(pdata); - return; - } -} -#else -static inline void omap_init_ocp2scp(void) { } -#endif - #if IS_ENABLED(CONFIG_WL12XX) static struct wl12xx_platform_data wl12xx __initdata; @@ -655,7 +578,6 @@ static int __init omap2_init_devices(void) omap_init_sti(); omap_init_rng(); omap_init_vout(); - omap_init_ocp2scp(); return 0; } diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index 848b6dc67590..d6dd70728c61 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -2695,25 +2694,6 @@ static struct omap_hwmod_class omap44xx_ocp2scp_hwmod_class = { .sysc = &omap44xx_ocp2scp_sysc, }; -/* ocp2scp dev_attr */ -static struct resource omap44xx_usb_phy_and_pll_addrs[] = { - { - .name = "usb_phy", - .start = 0x4a0ad080, - .end = 0x4a0ae000, - .flags = IORESOURCE_MEM, - }, - { } -}; - -static struct omap_ocp2scp_dev ocp2scp_dev_attr[] = { - { - .drv_name = "omap-usb2", - .res = omap44xx_usb_phy_and_pll_addrs, - }, - { } -}; - /* ocp2scp_usb_phy */ static struct omap_hwmod omap44xx_ocp2scp_usb_phy_hwmod = { .name = "ocp2scp_usb_phy", @@ -2737,7 +2717,6 @@ static struct omap_hwmod omap44xx_ocp2scp_usb_phy_hwmod = { .modulemode = MODULEMODE_HWCTRL, }, }, - .dev_attr = ocp2scp_dev_attr, }; /* diff --git a/drivers/bus/omap-ocp2scp.c b/drivers/bus/omap-ocp2scp.c index fe7191663bbd..5511f9814ddd 100644 --- a/drivers/bus/omap-ocp2scp.c +++ b/drivers/bus/omap-ocp2scp.c @@ -22,26 +22,6 @@ #include #include #include -#include - -/** - * _count_resources - count for the number of resources - * @res: struct resource * - * - * Count and return the number of resources populated for the device that is - * connected to ocp2scp. - */ -static unsigned _count_resources(struct resource *res) -{ - int cnt = 0; - - while (res->start != res->end) { - cnt++; - res++; - } - - return cnt; -} static int ocp2scp_remove_devices(struct device *dev, void *c) { @@ -55,11 +35,7 @@ static int ocp2scp_remove_devices(struct device *dev, void *c) static int omap_ocp2scp_probe(struct platform_device *pdev) { int ret; - unsigned res_cnt, i; struct device_node *np = pdev->dev.of_node; - struct platform_device *pdev_child; - struct omap_ocp2scp_platform_data *pdata = pdev->dev.platform_data; - struct omap_ocp2scp_dev *dev; if (np) { ret = of_platform_populate(np, NULL, NULL, &pdev->dev); @@ -68,48 +44,12 @@ static int omap_ocp2scp_probe(struct platform_device *pdev) "failed to add resources for ocp2scp child\n"); goto err0; } - } else if (pdata) { - for (i = 0, dev = *pdata->devices; i < pdata->dev_cnt; i++, - dev++) { - res_cnt = _count_resources(dev->res); - - pdev_child = platform_device_alloc(dev->drv_name, - PLATFORM_DEVID_AUTO); - if (!pdev_child) { - dev_err(&pdev->dev, - "failed to allocate mem for ocp2scp child\n"); - goto err0; - } - - ret = platform_device_add_resources(pdev_child, - dev->res, res_cnt); - if (ret) { - dev_err(&pdev->dev, - "failed to add resources for ocp2scp child\n"); - goto err1; - } - - pdev_child->dev.parent = &pdev->dev; - - ret = platform_device_add(pdev_child); - if (ret) { - dev_err(&pdev->dev, - "failed to register ocp2scp child device\n"); - goto err1; - } - } - } else { - dev_err(&pdev->dev, "OCP2SCP initialized without plat data\n"); - return -EINVAL; } pm_runtime_enable(&pdev->dev); return 0; -err1: - platform_device_put(pdev_child); - err0: device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices); diff --git a/include/linux/platform_data/omap_ocp2scp.h b/include/linux/platform_data/omap_ocp2scp.h deleted file mode 100644 index 5c6c3939355f..000000000000 --- a/include/linux/platform_data/omap_ocp2scp.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * omap_ocp2scp.h -- ocp2scp header file - * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Kishon Vijay Abraham I - * - * 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. - * - */ - -#ifndef __DRIVERS_OMAP_OCP2SCP_H -#define __DRIVERS_OMAP_OCP2SCP_H - -struct omap_ocp2scp_dev { - const char *drv_name; - struct resource *res; -}; - -struct omap_ocp2scp_platform_data { - int dev_cnt; - struct omap_ocp2scp_dev **devices; -}; -#endif /* __DRIVERS_OMAP_OCP2SCP_H */ -- cgit v1.3-18-gd494 From c992219825823cad5e11fab3854eb93df2e9ffa1 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Sun, 2 Jun 2013 09:53:01 -0400 Subject: cw1200: move platform_data header to correct location. (As suggested by Arnd Bergmann) Signed-off-by: Solomon Peachy Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/cw1200_sagrad.c | 2 +- drivers/net/wireless/cw1200/cw1200_sdio.c | 2 +- drivers/net/wireless/cw1200/cw1200_spi.c | 2 +- include/linux/cw1200_platform.h | 46 --------------------------- include/linux/platform_data/cw1200_platform.h | 46 +++++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 49 deletions(-) delete mode 100644 include/linux/cw1200_platform.h create mode 100644 include/linux/platform_data/cw1200_platform.h (limited to 'include/linux/platform_data') diff --git a/drivers/net/wireless/cw1200/cw1200_sagrad.c b/drivers/net/wireless/cw1200/cw1200_sagrad.c index a5ada0eda1dd..14c2a186b493 100644 --- a/drivers/net/wireless/cw1200/cw1200_sagrad.c +++ b/drivers/net/wireless/cw1200/cw1200_sagrad.c @@ -10,7 +10,7 @@ */ #include -#include +#include MODULE_AUTHOR("Solomon Peachy "); MODULE_DESCRIPTION("ST-Ericsson CW1200 Platform glue driver"); diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c index 78c3bc55cd0b..9f25ec5641fe 100644 --- a/drivers/net/wireless/cw1200/cw1200_sdio.c +++ b/drivers/net/wireless/cw1200/cw1200_sdio.c @@ -20,7 +20,7 @@ #include "cw1200.h" #include "hwbus.h" -#include +#include #include "hwio.h" MODULE_AUTHOR("Dmitry Tarnyagin "); diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c index 75efe54e495f..940e0947a5dc 100644 --- a/drivers/net/wireless/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -25,7 +25,7 @@ #include "cw1200.h" #include "hwbus.h" -#include +#include #include "hwio.h" MODULE_AUTHOR("Solomon Peachy "); diff --git a/include/linux/cw1200_platform.h b/include/linux/cw1200_platform.h deleted file mode 100644 index c168fa512347..000000000000 --- a/include/linux/cw1200_platform.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2011 - * - * Author: Dmitry Tarnyagin - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef CW1200_PLAT_H_INCLUDED -#define CW1200_PLAT_H_INCLUDED - -struct cw1200_platform_data_spi { - u8 spi_bits_per_word; /* REQUIRED */ - u16 ref_clk; /* REQUIRED (in KHz) */ - - /* All others are optional */ - bool have_5ghz; - const struct resource *reset; /* GPIO to RSTn signal */ - const struct resource *powerup; /* GPIO to POWERUP signal */ - int (*power_ctrl)(const struct cw1200_platform_data_spi *pdata, - bool enable); /* Control 3v3 / 1v8 supply */ - int (*clk_ctrl)(const struct cw1200_platform_data_spi *pdata, - bool enable); /* Control CLK32K */ - const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */ - const char *sdd_file; /* if NULL, will use default for detected hw type */ -}; - -struct cw1200_platform_data_sdio { - u16 ref_clk; /* REQUIRED (in KHz) */ - - /* All others are optional */ - const struct resource *irq; /* if using GPIO for IRQ */ - bool have_5ghz; - bool no_nptb; /* SDIO hardware does not support non-power-of-2-blocksizes */ - const struct resource *reset; /* GPIO to RSTn signal */ - const struct resource *powerup; /* GPIO to POWERUP signal */ - int (*power_ctrl)(const struct cw1200_platform_data_sdio *pdata, - bool enable); /* Control 3v3 / 1v8 supply */ - int (*clk_ctrl)(const struct cw1200_platform_data_sdio *pdata, - bool enable); /* Control CLK32K */ - const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */ - const char *sdd_file; /* if NULL, will use default for detected hw type */ -}; - -const void *cw1200_get_platform_data(void); - -#endif /* CW1200_PLAT_H_INCLUDED */ diff --git a/include/linux/platform_data/cw1200_platform.h b/include/linux/platform_data/cw1200_platform.h new file mode 100644 index 000000000000..c168fa512347 --- /dev/null +++ b/include/linux/platform_data/cw1200_platform.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Dmitry Tarnyagin + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef CW1200_PLAT_H_INCLUDED +#define CW1200_PLAT_H_INCLUDED + +struct cw1200_platform_data_spi { + u8 spi_bits_per_word; /* REQUIRED */ + u16 ref_clk; /* REQUIRED (in KHz) */ + + /* All others are optional */ + bool have_5ghz; + const struct resource *reset; /* GPIO to RSTn signal */ + const struct resource *powerup; /* GPIO to POWERUP signal */ + int (*power_ctrl)(const struct cw1200_platform_data_spi *pdata, + bool enable); /* Control 3v3 / 1v8 supply */ + int (*clk_ctrl)(const struct cw1200_platform_data_spi *pdata, + bool enable); /* Control CLK32K */ + const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */ + const char *sdd_file; /* if NULL, will use default for detected hw type */ +}; + +struct cw1200_platform_data_sdio { + u16 ref_clk; /* REQUIRED (in KHz) */ + + /* All others are optional */ + const struct resource *irq; /* if using GPIO for IRQ */ + bool have_5ghz; + bool no_nptb; /* SDIO hardware does not support non-power-of-2-blocksizes */ + const struct resource *reset; /* GPIO to RSTn signal */ + const struct resource *powerup; /* GPIO to POWERUP signal */ + int (*power_ctrl)(const struct cw1200_platform_data_sdio *pdata, + bool enable); /* Control 3v3 / 1v8 supply */ + int (*clk_ctrl)(const struct cw1200_platform_data_sdio *pdata, + bool enable); /* Control CLK32K */ + const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */ + const char *sdd_file; /* if NULL, will use default for detected hw type */ +}; + +const void *cw1200_get_platform_data(void); + +#endif /* CW1200_PLAT_H_INCLUDED */ -- cgit v1.3-18-gd494 From 6dd64a304eff76ca7dd41bf63df55efa965fa9ec Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Sun, 2 Jun 2013 09:53:03 -0400 Subject: cw1200: Replace use of 'struct resource' with 'int' for GPIO fields. The only advantage of 'struct resource' is that it lets us assign names as part of the platform data. Unfortunately since we are using platform data, we are already limited to a single instance of each driver, rendering this moot. So, replace the struct resources with ints, resulting in cleaner code. This was based on a suggestion from Arnd Bergmann. Signed-off-by: Solomon Peachy Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/cw1200_sagrad.c | 49 +++----------------------- drivers/net/wireless/cw1200/cw1200_sdio.c | 50 ++++++++++++--------------- drivers/net/wireless/cw1200/cw1200_spi.c | 33 ++++++++---------- include/linux/platform_data/cw1200_platform.h | 12 +++---- 4 files changed, 47 insertions(+), 97 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/net/wireless/cw1200/cw1200_sagrad.c b/drivers/net/wireless/cw1200/cw1200_sagrad.c index 14c2a186b493..3f884ac96ccc 100644 --- a/drivers/net/wireless/cw1200/cw1200_sagrad.c +++ b/drivers/net/wireless/cw1200/cw1200_sagrad.c @@ -21,29 +21,6 @@ MODULE_LICENSE("GPL"); /* #define SAGRAD_1091_1098_EVK_SPI */ #ifdef SAGRAD_1091_1098_EVK_SDIO -#if 0 -static struct resource cw1200_href_resources[] = { - { - .start = 215, /* fix me as appropriate */ - .end = 215, /* ditto */ - .flags = IORESOURCE_IO, - .name = "cw1200_wlan_reset", - }, - { - .start = 216, /* fix me as appropriate */ - .end = 216, /* ditto */ - .flags = IORESOURCE_IO, - .name = "cw1200_wlan_powerup", - }, - { - .start = NOMADIK_GPIO_TO_IRQ(216), /* fix me as appropriate */ - .end = NOMADIK_GPIO_TO_IRQ(216), /* ditto */ - .flags = IORESOURCE_IRQ, - .name = "cw1200_wlan_irq", - }, -}; -#endif - static int cw1200_power_ctrl(const struct cw1200_platform_data_sdio *pdata, bool enable) { @@ -68,9 +45,9 @@ static struct cw1200_platform_data_sdio cw1200_platform_data = { .ref_clk = 38400, .have_5ghz = false, #if 0 - .reset = &cw1200_href_resources[0], - .powerup = &cw1200_href_resources[1], - .irq = &cw1200_href_resources[2], + .reset = GPIO_RF_RESET, /* Replace as appropriate */ + .powerup = GPIO_RF_POWERUP, /* Replace as appropriate */ + .irq = GPIO_TO_IRQ(GPIO_RF_IRQ), /* Replace as appropriate */ #endif .power_ctrl = cw1200_power_ctrl, .clk_ctrl = cw1200_clk_ctrl, @@ -80,22 +57,6 @@ static struct cw1200_platform_data_sdio cw1200_platform_data = { #endif #ifdef SAGRAD_1091_1098_EVK_SPI -/* Note that this is an example of integrating into your board support file */ -static struct resource cw1200_href_resources[] = { - { - .start = GPIO_RF_RESET, - .end = GPIO_RF_RESET, - .flags = IORESOURCE_IO, - .name = "cw1200_wlan_reset", - }, - { - .start = GPIO_RF_POWERUP, - .end = GPIO_RF_POWERUP, - .flags = IORESOURCE_IO, - .name = "cw1200_wlan_powerup", - }, -}; - static int cw1200_power_ctrl(const struct cw1200_platform_data_spi *pdata, bool enable) { @@ -118,8 +79,8 @@ static int cw1200_clk_ctrl(const struct cw1200_platform_data_spi *pdata, static struct cw1200_platform_data_spi cw1200_platform_data = { .ref_clk = 38400, .spi_bits_per_word = 16, - .reset = &cw1200_href_resources[0], - .powerup = &cw1200_href_resources[1], + .reset = GPIO_RF_RESET, /* Replace as appropriate */ + .powerup = GPIO_RF_POWERUP, /* Replace as appropriate */ .power_ctrl = cw1200_power_ctrl, .clk_ctrl = cw1200_clk_ctrl, /* .macaddr = ??? */ diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c index 0a882ca89bbb..574cf727567c 100644 --- a/drivers/net/wireless/cw1200/cw1200_sdio.c +++ b/drivers/net/wireless/cw1200/cw1200_sdio.c @@ -105,7 +105,6 @@ static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id) static int cw1200_request_irq(struct hwbus_priv *self) { int ret; - const struct resource *irq = self->pdata->irq; u8 cccr; cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret); @@ -122,15 +121,15 @@ static int cw1200_request_irq(struct hwbus_priv *self) if (WARN_ON(ret)) goto err; - ret = enable_irq_wake(irq->start); + ret = enable_irq_wake(self->pdata->irq); if (WARN_ON(ret)) goto err; /* Request the IRQ */ - ret = request_threaded_irq(irq->start, cw1200_gpio_hardirq, + ret = request_threaded_irq(self->pdata->irq, cw1200_gpio_hardirq, cw1200_gpio_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, - irq->name, self); + "cw1200_wlan_irq", self); if (WARN_ON(ret)) goto err; @@ -162,8 +161,8 @@ static int cw1200_sdio_irq_unsubscribe(struct hwbus_priv *self) pr_debug("SW IRQ unsubscribe\n"); if (self->pdata->irq) { - disable_irq_wake(self->pdata->irq->start); - free_irq(self->pdata->irq->start, self); + disable_irq_wake(self->pdata->irq); + free_irq(self->pdata->irq, self); } else { sdio_claim_host(self->func); ret = sdio_release_irq(self->func); @@ -174,12 +173,10 @@ static int cw1200_sdio_irq_unsubscribe(struct hwbus_priv *self) static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata) { - const struct resource *reset = pdata->reset; - - if (reset) { - gpio_set_value(reset->start, 0); + if (pdata->reset) { + gpio_set_value(pdata->reset, 0); msleep(30); /* Min is 2 * CLK32K cycles */ - gpio_free(reset->start); + gpio_free(pdata->reset); } if (pdata->power_ctrl) @@ -192,20 +189,17 @@ static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata) static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata) { - const struct resource *reset = pdata->reset; - const struct resource *powerup = pdata->powerup; - /* Ensure I/Os are pulled low */ - if (reset) { - gpio_request(reset->start, reset->name); - gpio_direction_output(reset->start, 0); + if (pdata->reset) { + gpio_request(pdata->reset, "cw1200_wlan_reset"); + gpio_direction_output(pdata->reset, 0); } - if (powerup) { - gpio_request(powerup->start, powerup->name); - gpio_direction_output(powerup->start, 0); + if (pdata->powerup) { + gpio_request(pdata->powerup, "cw1200_wlan_powerup"); + gpio_direction_output(pdata->powerup, 0); } - if (reset || powerup) - msleep(50); /* Settle time */ + if (pdata->reset || pdata->powerup) + msleep(10); /* Settle time? */ /* Enable 3v3 and 1v8 to hardware */ if (pdata->power_ctrl) { @@ -225,13 +219,13 @@ static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata) } /* Enable POWERUP signal */ - if (powerup) { - gpio_set_value(powerup->start, 1); + if (pdata->powerup) { + gpio_set_value(pdata->powerup, 1); msleep(250); /* or more..? */ } /* Enable RSTn signal */ - if (reset) { - gpio_set_value(reset->start, 1); + if (pdata->reset) { + gpio_set_value(pdata->reset, 1); msleep(50); /* Or more..? */ } return 0; @@ -252,7 +246,7 @@ static int cw1200_sdio_pm(struct hwbus_priv *self, bool suspend) int ret = 0; if (self->pdata->irq) - ret = irq_set_irq_wake(self->pdata->irq->start, suspend); + ret = irq_set_irq_wake(self->pdata->irq, suspend); return ret; } @@ -274,7 +268,7 @@ static int cw1200_sdio_probe(struct sdio_func *func, pr_info("cw1200_wlan_sdio: Probe called\n"); - /* We are only able to handle the wlan function */ + /* We are only able to handle the wlan function */ if (func->num != 0x01) return -ENODEV; diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c index da5d24cc67db..d8dc7c6bb96d 100644 --- a/drivers/net/wireless/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -268,12 +268,10 @@ static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self) static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata) { - const struct resource *reset = pdata->reset; - - if (reset) { - gpio_set_value(reset->start, 0); + if (pdata->reset) { + gpio_set_value(pdata->reset, 0); msleep(30); /* Min is 2 * CLK32K cycles */ - gpio_free(reset->start); + gpio_free(pdata->reset); } if (pdata->power_ctrl) @@ -286,19 +284,16 @@ static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata) static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata) { - const struct resource *reset = pdata->reset; - const struct resource *powerup = pdata->powerup; - /* Ensure I/Os are pulled low */ - if (reset) { - gpio_request(reset->start, reset->name); - gpio_direction_output(reset->start, 0); + if (pdata->reset) { + gpio_request(pdata->reset, "cw1200_wlan_reset"); + gpio_direction_output(pdata->reset, 0); } - if (powerup) { - gpio_request(powerup->start, powerup->name); - gpio_direction_output(powerup->start, 0); + if (pdata->powerup) { + gpio_request(pdata->powerup, "cw1200_wlan_powerup"); + gpio_direction_output(pdata->powerup, 0); } - if (reset || powerup) + if (pdata->reset || pdata->powerup) msleep(10); /* Settle time? */ /* Enable 3v3 and 1v8 to hardware */ @@ -319,13 +314,13 @@ static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata) } /* Enable POWERUP signal */ - if (powerup) { - gpio_set_value(powerup->start, 1); + if (pdata->powerup) { + gpio_set_value(pdata->powerup, 1); msleep(250); /* or more..? */ } /* Enable RSTn signal */ - if (reset) { - gpio_set_value(reset->start, 1); + if (pdata->reset) { + gpio_set_value(pdata->reset, 1); msleep(50); /* Or more..? */ } return 0; diff --git a/include/linux/platform_data/cw1200_platform.h b/include/linux/platform_data/cw1200_platform.h index c168fa512347..4454c16ea3e5 100644 --- a/include/linux/platform_data/cw1200_platform.h +++ b/include/linux/platform_data/cw1200_platform.h @@ -14,8 +14,8 @@ struct cw1200_platform_data_spi { /* All others are optional */ bool have_5ghz; - const struct resource *reset; /* GPIO to RSTn signal */ - const struct resource *powerup; /* GPIO to POWERUP signal */ + int reset; /* GPIO to RSTn signal (0 disables) */ + int powerup; /* GPIO to POWERUP signal (0 disables) */ int (*power_ctrl)(const struct cw1200_platform_data_spi *pdata, bool enable); /* Control 3v3 / 1v8 supply */ int (*clk_ctrl)(const struct cw1200_platform_data_spi *pdata, @@ -28,11 +28,11 @@ struct cw1200_platform_data_sdio { u16 ref_clk; /* REQUIRED (in KHz) */ /* All others are optional */ - const struct resource *irq; /* if using GPIO for IRQ */ bool have_5ghz; - bool no_nptb; /* SDIO hardware does not support non-power-of-2-blocksizes */ - const struct resource *reset; /* GPIO to RSTn signal */ - const struct resource *powerup; /* GPIO to POWERUP signal */ + bool no_nptb; /* SDIO hardware does not support non-power-of-2-blocksizes */ + int reset; /* GPIO to RSTn signal (0 disables) */ + int powerup; /* GPIO to POWERUP signal (0 disables) */ + int irq; /* IRQ line or 0 to use SDIO IRQ */ int (*power_ctrl)(const struct cw1200_platform_data_sdio *pdata, bool enable); /* Control 3v3 / 1v8 supply */ int (*clk_ctrl)(const struct cw1200_platform_data_sdio *pdata, -- cgit v1.3-18-gd494 From 7c0b6f49dbea0b09b1de8aa5c942af9c2ad8b54c Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Sun, 2 Jun 2013 11:35:31 -0400 Subject: cw1200: Rework SDIO platform support to prevent build problems. Based on discussions with And Bergmann, this patch changes the SDIO platform code to default to supporting the Sagrad devices, allowing for it to be overridden in board setup code. This renders the cw1200_sagrad module suplerflous, so it is now removed. It also moves the documentation that was in the cw1200_sagrad source to the platform header. Signed-off-by: Solomon Peachy Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/Kconfig | 17 ++--- drivers/net/wireless/cw1200/Makefile | 2 - drivers/net/wireless/cw1200/cw1200_sagrad.c | 106 -------------------------- drivers/net/wireless/cw1200/cw1200_sdio.c | 26 ++++++- include/linux/platform_data/cw1200_platform.h | 37 ++++++++- 5 files changed, 66 insertions(+), 122 deletions(-) delete mode 100644 drivers/net/wireless/cw1200/cw1200_sagrad.c (limited to 'include/linux/platform_data') diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/cw1200/Kconfig index 13e36119ae9f..7a585d4e9c8e 100644 --- a/drivers/net/wireless/cw1200/Kconfig +++ b/drivers/net/wireless/cw1200/Kconfig @@ -13,20 +13,19 @@ config CW1200_WLAN_SDIO depends on CW1200 && MMC help Enable support for the CW1200 connected via an SDIO bus. + By default this driver only supports the Sagrad SG901-1091/1098 EVK + and similar designs that utilize a hardware reset circuit. To + support different CW1200 SDIO designs you will need to override + the default platform data by calling cw1200_sdio_set_platform_data() + in your board setup file. config CW1200_WLAN_SPI tristate "Support SPI platforms" depends on CW1200 && SPI help - Enables support for the CW1200 connected via a SPI bus. - -config CW1200_WLAN_SAGRAD - tristate "Support Sagrad SG901-1091/1098 modules" - depends on CW1200_WLAN_SDIO - help - This provides the platform data glue to support the - Sagrad SG901-1091/1098 modules in their standard SDIO EVK. - It also includes example SPI platform data. + Enables support for the CW1200 connected via a SPI bus. You will + need to add appropriate platform data glue in your board setup + file. menu "Driver debug features" depends on CW1200 && DEBUG_FS diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/cw1200/Makefile index 1aa3682066e0..bc6cbf91f26e 100644 --- a/drivers/net/wireless/cw1200/Makefile +++ b/drivers/net/wireless/cw1200/Makefile @@ -16,9 +16,7 @@ cw1200_core-$(CONFIG_PM) += pm.o cw1200_wlan_sdio-y := cw1200_sdio.o cw1200_wlan_spi-y := cw1200_spi.o -cw1200_wlan_sagrad-y := cw1200_sagrad.o obj-$(CONFIG_CW1200) += cw1200_core.o obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o -obj-$(CONFIG_CW1200_WLAN_SAGRAD) += cw1200_wlan_sagrad.o diff --git a/drivers/net/wireless/cw1200/cw1200_sagrad.c b/drivers/net/wireless/cw1200/cw1200_sagrad.c deleted file mode 100644 index 3f884ac96ccc..000000000000 --- a/drivers/net/wireless/cw1200/cw1200_sagrad.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Platform glue data for ST-Ericsson CW1200 driver - * - * Copyright (c) 2013, Sagrad, Inc - * Author: Solomon Peachy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include - -MODULE_AUTHOR("Solomon Peachy "); -MODULE_DESCRIPTION("ST-Ericsson CW1200 Platform glue driver"); -MODULE_LICENSE("GPL"); - -/* Define just one of these. Feel free to customize as needed */ -#define SAGRAD_1091_1098_EVK_SDIO -/* #define SAGRAD_1091_1098_EVK_SPI */ - -#ifdef SAGRAD_1091_1098_EVK_SDIO -static int cw1200_power_ctrl(const struct cw1200_platform_data_sdio *pdata, - bool enable) -{ - /* Control 3v3 and 1v8 to hardware as appropriate */ - /* Note this is not needed if it's controlled elsewhere or always on */ - - /* May require delay for power to stabilize */ - return 0; -} - -static int cw1200_clk_ctrl(const struct cw1200_platform_data_sdio *pdata, - bool enable) -{ - /* Turn CLK_32K off and on as appropriate. */ - /* Note this is not needed if it's always on */ - - /* May require delay for clock to stabilize */ - return 0; -} - -static struct cw1200_platform_data_sdio cw1200_platform_data = { - .ref_clk = 38400, - .have_5ghz = false, -#if 0 - .reset = GPIO_RF_RESET, /* Replace as appropriate */ - .powerup = GPIO_RF_POWERUP, /* Replace as appropriate */ - .irq = GPIO_TO_IRQ(GPIO_RF_IRQ), /* Replace as appropriate */ -#endif - .power_ctrl = cw1200_power_ctrl, - .clk_ctrl = cw1200_clk_ctrl, -/* .macaddr = ??? */ - .sdd_file = "sdd_sagrad_1091_1098.bin", -}; -#endif - -#ifdef SAGRAD_1091_1098_EVK_SPI -static int cw1200_power_ctrl(const struct cw1200_platform_data_spi *pdata, - bool enable) -{ - /* Control 3v3 and 1v8 to hardware as appropriate */ - /* Note this is not needed if it's controlled elsewhere or always on */ - - /* May require delay for power to stabilize */ - return 0; -} -static int cw1200_clk_ctrl(const struct cw1200_platform_data_spi *pdata, - bool enable) -{ - /* Turn CLK_32K off and on as appropriate. */ - /* Note this is not needed if it's always on */ - - /* May require delay for clock to stabilize */ - return 0; -} - -static struct cw1200_platform_data_spi cw1200_platform_data = { - .ref_clk = 38400, - .spi_bits_per_word = 16, - .reset = GPIO_RF_RESET, /* Replace as appropriate */ - .powerup = GPIO_RF_POWERUP, /* Replace as appropriate */ - .power_ctrl = cw1200_power_ctrl, - .clk_ctrl = cw1200_clk_ctrl, -/* .macaddr = ??? */ - .sdd_file = "sdd_sagrad_1091_1098.bin", -}; -static struct spi_board_info myboard_spi_devices[] __initdata = { - { - .modalias = "cw1200_wlan_spi", - .max_speed_hz = 10000000, /* 52MHz Max */ - .bus_num = 0, - .irq = WIFI_IRQ, - .platform_data = &cw1200_platform_data, - .chip_select = 0, - }, -}; -#endif - - -const void *cw1200_get_platform_data(void) -{ - return &cw1200_platform_data; -} -EXPORT_SYMBOL_GPL(cw1200_get_platform_data); diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c index 574cf727567c..4b3148e47ee7 100644 --- a/drivers/net/wireless/cw1200/cw1200_sdio.c +++ b/drivers/net/wireless/cw1200/cw1200_sdio.c @@ -29,6 +29,21 @@ MODULE_LICENSE("GPL"); #define SDIO_BLOCK_SIZE (512) +/* Default platform data for Sagrad modules */ +static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = { + .ref_clk = 38400, + .have_5ghz = false, + .sdd_file = "sdd_sagrad_1091_1098.bin", +}; + +/* Allow platform data to be overridden */ +static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data; + +void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata) +{ + global_plat_data = pdata; +} + struct hwbus_priv { struct sdio_func *func; struct cw1200_common *core; @@ -261,7 +276,7 @@ static struct hwbus_ops cw1200_sdio_hwbus_ops = { /* Probe Function to be called by SDIO stack when device is discovered */ static int cw1200_sdio_probe(struct sdio_func *func, - const struct sdio_device_id *id) + const struct sdio_device_id *id) { struct hwbus_priv *self; int status; @@ -280,7 +295,7 @@ static int cw1200_sdio_probe(struct sdio_func *func, func->card->quirks |= MMC_QUIRK_LENIENT_FN0; - self->pdata = cw1200_get_platform_data(); + self->pdata = global_plat_data; /* FIXME */ self->func = func; sdio_set_drvdata(func, self); sdio_claim_host(func); @@ -374,7 +389,8 @@ static int __init cw1200_sdio_init(void) const struct cw1200_platform_data_sdio *pdata; int ret; - pdata = cw1200_get_platform_data(); + /* FIXME -- this won't support multiple devices */ + pdata = global_plat_data; if (cw1200_sdio_on(pdata)) { ret = -1; @@ -396,7 +412,9 @@ err: static void __exit cw1200_sdio_exit(void) { const struct cw1200_platform_data_sdio *pdata; - pdata = cw1200_get_platform_data(); + + /* FIXME -- this won't support multiple devices */ + pdata = global_plat_data; sdio_unregister_driver(&sdio_driver); cw1200_sdio_off(pdata); } diff --git a/include/linux/platform_data/cw1200_platform.h b/include/linux/platform_data/cw1200_platform.h index 4454c16ea3e5..c6fbc3ce4ab0 100644 --- a/include/linux/platform_data/cw1200_platform.h +++ b/include/linux/platform_data/cw1200_platform.h @@ -41,6 +41,41 @@ struct cw1200_platform_data_sdio { const char *sdd_file; /* if NULL, will use default for detected hw type */ }; -const void *cw1200_get_platform_data(void); + +/* An example of SPI support in your board setup file: + + static struct cw1200_platform_data_spi cw1200_platform_data = { + .ref_clk = 38400, + .spi_bits_per_word = 16, + .reset = GPIO_RF_RESET, + .powerup = GPIO_RF_POWERUP, + .macaddr = wifi_mac_addr, + .sdd_file = "sdd_sagrad_1091_1098.bin", + }; + static struct spi_board_info myboard_spi_devices[] __initdata = { + { + .modalias = "cw1200_wlan_spi", + .max_speed_hz = 52000000, + .bus_num = 0, + .irq = WIFI_IRQ, + .platform_data = &cw1200_platform_data, + .chip_select = 0, + }, + }; + + */ + +/* An example of SDIO support in your board setup file: + + static struct cw1200_platform_data_sdio my_cw1200_platform_data = { + .ref_clk = 38400, + .have_5ghz = false, + .sdd_file = "sdd_myplatform.bin", + }; + cw1200_sdio_set_platform_data(&my_cw1200_platform_data); + + */ + +void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata); #endif /* CW1200_PLAT_H_INCLUDED */ -- cgit v1.3-18-gd494 From 4da2a54a842db6921289e3e25b0739531a594dea Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Sun, 2 Jun 2013 11:35:32 -0400 Subject: cw1200: rename the cw1200 platform definition header My previous patch just moved the file, but it also needed to be renamed to conform to proper conventions. Signed-off-by: Solomon Peachy Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/cw1200_sdio.c | 2 +- drivers/net/wireless/cw1200/cw1200_spi.c | 2 +- include/linux/platform_data/cw1200_platform.h | 81 --------------------------- include/linux/platform_data/net-cw1200.h | 81 +++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 83 deletions(-) delete mode 100644 include/linux/platform_data/cw1200_platform.h create mode 100644 include/linux/platform_data/net-cw1200.h (limited to 'include/linux/platform_data') diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c index 4b3148e47ee7..bb1f405315e4 100644 --- a/drivers/net/wireless/cw1200/cw1200_sdio.c +++ b/drivers/net/wireless/cw1200/cw1200_sdio.c @@ -20,7 +20,7 @@ #include "cw1200.h" #include "hwbus.h" -#include +#include #include "hwio.h" MODULE_AUTHOR("Dmitry Tarnyagin "); diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c index d8dc7c6bb96d..e58f0a5bafa9 100644 --- a/drivers/net/wireless/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -25,7 +25,7 @@ #include "cw1200.h" #include "hwbus.h" -#include +#include #include "hwio.h" MODULE_AUTHOR("Solomon Peachy "); diff --git a/include/linux/platform_data/cw1200_platform.h b/include/linux/platform_data/cw1200_platform.h deleted file mode 100644 index c6fbc3ce4ab0..000000000000 --- a/include/linux/platform_data/cw1200_platform.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2011 - * - * Author: Dmitry Tarnyagin - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef CW1200_PLAT_H_INCLUDED -#define CW1200_PLAT_H_INCLUDED - -struct cw1200_platform_data_spi { - u8 spi_bits_per_word; /* REQUIRED */ - u16 ref_clk; /* REQUIRED (in KHz) */ - - /* All others are optional */ - bool have_5ghz; - int reset; /* GPIO to RSTn signal (0 disables) */ - int powerup; /* GPIO to POWERUP signal (0 disables) */ - int (*power_ctrl)(const struct cw1200_platform_data_spi *pdata, - bool enable); /* Control 3v3 / 1v8 supply */ - int (*clk_ctrl)(const struct cw1200_platform_data_spi *pdata, - bool enable); /* Control CLK32K */ - const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */ - const char *sdd_file; /* if NULL, will use default for detected hw type */ -}; - -struct cw1200_platform_data_sdio { - u16 ref_clk; /* REQUIRED (in KHz) */ - - /* All others are optional */ - bool have_5ghz; - bool no_nptb; /* SDIO hardware does not support non-power-of-2-blocksizes */ - int reset; /* GPIO to RSTn signal (0 disables) */ - int powerup; /* GPIO to POWERUP signal (0 disables) */ - int irq; /* IRQ line or 0 to use SDIO IRQ */ - int (*power_ctrl)(const struct cw1200_platform_data_sdio *pdata, - bool enable); /* Control 3v3 / 1v8 supply */ - int (*clk_ctrl)(const struct cw1200_platform_data_sdio *pdata, - bool enable); /* Control CLK32K */ - const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */ - const char *sdd_file; /* if NULL, will use default for detected hw type */ -}; - - -/* An example of SPI support in your board setup file: - - static struct cw1200_platform_data_spi cw1200_platform_data = { - .ref_clk = 38400, - .spi_bits_per_word = 16, - .reset = GPIO_RF_RESET, - .powerup = GPIO_RF_POWERUP, - .macaddr = wifi_mac_addr, - .sdd_file = "sdd_sagrad_1091_1098.bin", - }; - static struct spi_board_info myboard_spi_devices[] __initdata = { - { - .modalias = "cw1200_wlan_spi", - .max_speed_hz = 52000000, - .bus_num = 0, - .irq = WIFI_IRQ, - .platform_data = &cw1200_platform_data, - .chip_select = 0, - }, - }; - - */ - -/* An example of SDIO support in your board setup file: - - static struct cw1200_platform_data_sdio my_cw1200_platform_data = { - .ref_clk = 38400, - .have_5ghz = false, - .sdd_file = "sdd_myplatform.bin", - }; - cw1200_sdio_set_platform_data(&my_cw1200_platform_data); - - */ - -void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata); - -#endif /* CW1200_PLAT_H_INCLUDED */ diff --git a/include/linux/platform_data/net-cw1200.h b/include/linux/platform_data/net-cw1200.h new file mode 100644 index 000000000000..c6fbc3ce4ab0 --- /dev/null +++ b/include/linux/platform_data/net-cw1200.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Dmitry Tarnyagin + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef CW1200_PLAT_H_INCLUDED +#define CW1200_PLAT_H_INCLUDED + +struct cw1200_platform_data_spi { + u8 spi_bits_per_word; /* REQUIRED */ + u16 ref_clk; /* REQUIRED (in KHz) */ + + /* All others are optional */ + bool have_5ghz; + int reset; /* GPIO to RSTn signal (0 disables) */ + int powerup; /* GPIO to POWERUP signal (0 disables) */ + int (*power_ctrl)(const struct cw1200_platform_data_spi *pdata, + bool enable); /* Control 3v3 / 1v8 supply */ + int (*clk_ctrl)(const struct cw1200_platform_data_spi *pdata, + bool enable); /* Control CLK32K */ + const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */ + const char *sdd_file; /* if NULL, will use default for detected hw type */ +}; + +struct cw1200_platform_data_sdio { + u16 ref_clk; /* REQUIRED (in KHz) */ + + /* All others are optional */ + bool have_5ghz; + bool no_nptb; /* SDIO hardware does not support non-power-of-2-blocksizes */ + int reset; /* GPIO to RSTn signal (0 disables) */ + int powerup; /* GPIO to POWERUP signal (0 disables) */ + int irq; /* IRQ line or 0 to use SDIO IRQ */ + int (*power_ctrl)(const struct cw1200_platform_data_sdio *pdata, + bool enable); /* Control 3v3 / 1v8 supply */ + int (*clk_ctrl)(const struct cw1200_platform_data_sdio *pdata, + bool enable); /* Control CLK32K */ + const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */ + const char *sdd_file; /* if NULL, will use default for detected hw type */ +}; + + +/* An example of SPI support in your board setup file: + + static struct cw1200_platform_data_spi cw1200_platform_data = { + .ref_clk = 38400, + .spi_bits_per_word = 16, + .reset = GPIO_RF_RESET, + .powerup = GPIO_RF_POWERUP, + .macaddr = wifi_mac_addr, + .sdd_file = "sdd_sagrad_1091_1098.bin", + }; + static struct spi_board_info myboard_spi_devices[] __initdata = { + { + .modalias = "cw1200_wlan_spi", + .max_speed_hz = 52000000, + .bus_num = 0, + .irq = WIFI_IRQ, + .platform_data = &cw1200_platform_data, + .chip_select = 0, + }, + }; + + */ + +/* An example of SDIO support in your board setup file: + + static struct cw1200_platform_data_sdio my_cw1200_platform_data = { + .ref_clk = 38400, + .have_5ghz = false, + .sdd_file = "sdd_myplatform.bin", + }; + cw1200_sdio_set_platform_data(&my_cw1200_platform_data); + + */ + +void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata); + +#endif /* CW1200_PLAT_H_INCLUDED */ -- cgit v1.3-18-gd494 From be2dbb09a014cba5691c8483ad2d0747d3eeb514 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 15 May 2013 10:51:43 +0100 Subject: usb: musb: ux500: move channel number knowledge into the driver For all ux500 based platforms the maximum number of end-points are used. Move this knowledge into the driver so we can relinquish the burden from platform data. This also removes quite a bit of complexity from the driver and will aid us when we come to enable the driver for Device Tree. Cc: linux-usb@vger.kernel.org Acked-by: Felipe Balbi Acked-by: Fabio Baltieri Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- arch/arm/mach-ux500/usb.c | 14 ++++++------- drivers/usb/musb/ux500_dma.c | 30 +++++++++------------------- include/linux/platform_data/usb-musb-ux500.h | 5 +---- 3 files changed, 16 insertions(+), 33 deletions(-) (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-ux500/usb.c b/arch/arm/mach-ux500/usb.c index 72754e369417..a21c2e1b7333 100644 --- a/arch/arm/mach-ux500/usb.c +++ b/arch/arm/mach-ux500/usb.c @@ -22,7 +22,7 @@ .dir = STEDMA40_MEM_TO_PERIPH, \ } -static struct stedma40_chan_cfg musb_dma_rx_ch[UX500_MUSB_DMA_NUM_RX_CHANNELS] +static struct stedma40_chan_cfg musb_dma_rx_ch[UX500_MUSB_DMA_NUM_RX_TX_CHANNELS] = { MUSB_DMA40_RX_CH, MUSB_DMA40_RX_CH, @@ -34,7 +34,7 @@ static struct stedma40_chan_cfg musb_dma_rx_ch[UX500_MUSB_DMA_NUM_RX_CHANNELS] MUSB_DMA40_RX_CH }; -static struct stedma40_chan_cfg musb_dma_tx_ch[UX500_MUSB_DMA_NUM_TX_CHANNELS] +static struct stedma40_chan_cfg musb_dma_tx_ch[UX500_MUSB_DMA_NUM_RX_TX_CHANNELS] = { MUSB_DMA40_TX_CH, MUSB_DMA40_TX_CH, @@ -46,7 +46,7 @@ static struct stedma40_chan_cfg musb_dma_tx_ch[UX500_MUSB_DMA_NUM_TX_CHANNELS] MUSB_DMA40_TX_CH, }; -static void *ux500_dma_rx_param_array[UX500_MUSB_DMA_NUM_RX_CHANNELS] = { +static void *ux500_dma_rx_param_array[UX500_MUSB_DMA_NUM_RX_TX_CHANNELS] = { &musb_dma_rx_ch[0], &musb_dma_rx_ch[1], &musb_dma_rx_ch[2], @@ -57,7 +57,7 @@ static void *ux500_dma_rx_param_array[UX500_MUSB_DMA_NUM_RX_CHANNELS] = { &musb_dma_rx_ch[7] }; -static void *ux500_dma_tx_param_array[UX500_MUSB_DMA_NUM_TX_CHANNELS] = { +static void *ux500_dma_tx_param_array[UX500_MUSB_DMA_NUM_RX_TX_CHANNELS] = { &musb_dma_tx_ch[0], &musb_dma_tx_ch[1], &musb_dma_tx_ch[2], @@ -71,8 +71,6 @@ static void *ux500_dma_tx_param_array[UX500_MUSB_DMA_NUM_TX_CHANNELS] = { static struct ux500_musb_board_data musb_board_data = { .dma_rx_param_array = ux500_dma_rx_param_array, .dma_tx_param_array = ux500_dma_tx_param_array, - .num_rx_channels = UX500_MUSB_DMA_NUM_RX_CHANNELS, - .num_tx_channels = UX500_MUSB_DMA_NUM_TX_CHANNELS, .dma_filter = stedma40_filter, }; @@ -119,7 +117,7 @@ static inline void ux500_usb_dma_update_rx_ch_config(int *dev_type) { u32 idx; - for (idx = 0; idx < UX500_MUSB_DMA_NUM_RX_CHANNELS; idx++) + for (idx = 0; idx < UX500_MUSB_DMA_NUM_RX_TX_CHANNELS; idx++) musb_dma_rx_ch[idx].dev_type = dev_type[idx]; } @@ -127,7 +125,7 @@ static inline void ux500_usb_dma_update_tx_ch_config(int *dev_type) { u32 idx; - for (idx = 0; idx < UX500_MUSB_DMA_NUM_TX_CHANNELS; idx++) + for (idx = 0; idx < UX500_MUSB_DMA_NUM_RX_TX_CHANNELS; idx++) musb_dma_tx_ch[idx].dev_type = dev_type[idx]; } diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 338120641145..382291b91f7b 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -48,10 +48,8 @@ struct ux500_dma_channel { struct ux500_dma_controller { struct dma_controller controller; - struct ux500_dma_channel rx_channel[UX500_MUSB_DMA_NUM_RX_CHANNELS]; - struct ux500_dma_channel tx_channel[UX500_MUSB_DMA_NUM_TX_CHANNELS]; - u32 num_rx_channels; - u32 num_tx_channels; + struct ux500_dma_channel rx_channel[UX500_MUSB_DMA_NUM_RX_TX_CHANNELS]; + struct ux500_dma_channel tx_channel[UX500_MUSB_DMA_NUM_RX_TX_CHANNELS]; void *private_data; dma_addr_t phy_base; }; @@ -144,19 +142,15 @@ static struct dma_channel *ux500_dma_channel_allocate(struct dma_controller *c, struct ux500_dma_channel *ux500_channel = NULL; struct musb *musb = controller->private_data; u8 ch_num = hw_ep->epnum - 1; - u32 max_ch; - /* Max 8 DMA channels (0 - 7). Each DMA channel can only be allocated + /* 8 DMA channels (0 - 7). Each DMA channel can only be allocated * to specified hw_ep. For example DMA channel 0 can only be allocated * to hw_ep 1 and 9. */ if (ch_num > 7) ch_num -= 8; - max_ch = is_tx ? controller->num_tx_channels : - controller->num_rx_channels; - - if (ch_num >= max_ch) + if (ch_num >= UX500_MUSB_DMA_NUM_RX_TX_CHANNELS) return NULL; ux500_channel = is_tx ? &(controller->tx_channel[ch_num]) : @@ -264,7 +258,7 @@ static int ux500_dma_controller_stop(struct dma_controller *c) struct dma_channel *channel; u8 ch_num; - for (ch_num = 0; ch_num < controller->num_rx_channels; ch_num++) { + for (ch_num = 0; ch_num < UX500_MUSB_DMA_NUM_RX_TX_CHANNELS; ch_num++) { channel = &controller->rx_channel[ch_num].channel; ux500_channel = channel->private_data; @@ -274,7 +268,7 @@ static int ux500_dma_controller_stop(struct dma_controller *c) dma_release_channel(ux500_channel->dma_chan); } - for (ch_num = 0; ch_num < controller->num_tx_channels; ch_num++) { + for (ch_num = 0; ch_num < UX500_MUSB_DMA_NUM_RX_TX_CHANNELS; ch_num++) { channel = &controller->tx_channel[ch_num].channel; ux500_channel = channel->private_data; @@ -303,26 +297,21 @@ static int ux500_dma_controller_start(struct dma_controller *c) void **param_array; struct ux500_dma_channel *channel_array; - u32 ch_count; dma_cap_mask_t mask; - if ((data->num_rx_channels > UX500_MUSB_DMA_NUM_RX_CHANNELS) || - (data->num_tx_channels > UX500_MUSB_DMA_NUM_TX_CHANNELS)) - return -EINVAL; - controller->num_rx_channels = data->num_rx_channels; - controller->num_tx_channels = data->num_tx_channels; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); /* Prepare the loop for RX channels */ channel_array = controller->rx_channel; - ch_count = data->num_rx_channels; param_array = data->dma_rx_param_array; for (dir = 0; dir < 2; dir++) { - for (ch_num = 0; ch_num < ch_count; ch_num++) { + for (ch_num = 0; + ch_num < UX500_MUSB_DMA_NUM_RX_TX_CHANNELS; + ch_num++) { ux500_channel = &channel_array[ch_num]; ux500_channel->controller = controller; ux500_channel->ch_num = ch_num; @@ -350,7 +339,6 @@ static int ux500_dma_controller_start(struct dma_controller *c) /* Prepare the loop for TX channels */ channel_array = controller->tx_channel; - ch_count = data->num_tx_channels; param_array = data->dma_tx_param_array; is_tx = 1; } diff --git a/include/linux/platform_data/usb-musb-ux500.h b/include/linux/platform_data/usb-musb-ux500.h index 4c1cc50a595a..dd9c83ac7de0 100644 --- a/include/linux/platform_data/usb-musb-ux500.h +++ b/include/linux/platform_data/usb-musb-ux500.h @@ -9,14 +9,11 @@ #include -#define UX500_MUSB_DMA_NUM_RX_CHANNELS 8 -#define UX500_MUSB_DMA_NUM_TX_CHANNELS 8 +#define UX500_MUSB_DMA_NUM_RX_TX_CHANNELS 8 struct ux500_musb_board_data { void **dma_rx_param_array; void **dma_tx_param_array; - u32 num_rx_channels; - u32 num_tx_channels; bool (*dma_filter)(struct dma_chan *chan, void *filter_param); }; -- cgit v1.3-18-gd494 From e7bab58b73a653d4c226b11ead07fd30ffaea914 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 15 May 2013 10:51:55 +0100 Subject: ARM: ux500: Remove recently unused stedma40_xfer_dir enums We're now using the transfer direction definitions provided by the DMA sub-system, so the home-brew ones have become obsolete. Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- include/linux/platform_data/dma-ste-dma40.h | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index 288dc2420ee6..54ddca615cb4 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -77,14 +77,6 @@ enum stedma40_periph_data_width { STEDMA40_DOUBLEWORD_WIDTH = STEDMA40_ESIZE_64_BIT }; -enum stedma40_xfer_dir { - STEDMA40_MEM_TO_MEM = 1, - STEDMA40_MEM_TO_PERIPH, - STEDMA40_PERIPH_TO_MEM, - STEDMA40_PERIPH_TO_PERIPH -}; - - /** * struct stedma40_half_channel_info - dst/src channel configuration * @@ -120,7 +112,7 @@ struct stedma40_half_channel_info { * */ struct stedma40_chan_cfg { - enum stedma40_xfer_dir dir; + enum dma_transfer_direction dir; bool high_priority; bool realtime; enum stedma40_mode mode; -- cgit v1.3-18-gd494 From 43f2e1a3be5d83004f09bcb53c46f273e7473a00 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 15 May 2013 11:51:57 +0200 Subject: dmaengine: ste_dma40: Convert data_width from register bit format to value When a DMA client requests and configures a DMA channel, it requests data_width in Bytes. The DMA40 driver then swiftly converts it over to the necessary register bit value. Unfortunately, for any subsequent calculations we have to shift '1' by the bit pattern (1 << data_width) times to make any sense of it. This patch flips the semantics on its head and only converts the value to its respective register bit pattern when writing to registers. This way we can use the true data_width (in Bytes) value. Cc: Dan Williams Cc: Per Forlin Cc: Rabin Vincent Acked-by: Vinod Koul Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/dma/ste_dma40.c | 63 ++++++++++++----------------- drivers/dma/ste_dma40_ll.c | 43 +++++++++++++------- include/linux/platform_data/dma-ste-dma40.h | 9 +---- sound/soc/ux500/ux500_pcm.c | 10 ++--- 4 files changed, 60 insertions(+), 65 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 483da1660eae..76c255fcdc2d 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -80,11 +80,11 @@ struct stedma40_chan_cfg dma40_memcpy_conf_phy = { .mode = STEDMA40_MODE_PHYSICAL, .dir = DMA_MEM_TO_MEM, - .src_info.data_width = STEDMA40_BYTE_WIDTH, + .src_info.data_width = DMA_SLAVE_BUSWIDTH_1_BYTE, .src_info.psize = STEDMA40_PSIZE_PHY_1, .src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, - .dst_info.data_width = STEDMA40_BYTE_WIDTH, + .dst_info.data_width = DMA_SLAVE_BUSWIDTH_1_BYTE, .dst_info.psize = STEDMA40_PSIZE_PHY_1, .dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, }; @@ -94,11 +94,11 @@ struct stedma40_chan_cfg dma40_memcpy_conf_log = { .mode = STEDMA40_MODE_LOGICAL, .dir = DMA_MEM_TO_MEM, - .src_info.data_width = STEDMA40_BYTE_WIDTH, + .src_info.data_width = DMA_SLAVE_BUSWIDTH_1_BYTE, .src_info.psize = STEDMA40_PSIZE_LOG_1, .src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, - .dst_info.data_width = STEDMA40_BYTE_WIDTH, + .dst_info.data_width = DMA_SLAVE_BUSWIDTH_1_BYTE, .dst_info.psize = STEDMA40_PSIZE_LOG_1, .dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, }; @@ -1005,20 +1005,21 @@ static int d40_psize_2_burst_size(bool is_log, int psize) /* * The dma only supports transmitting packages up to - * STEDMA40_MAX_SEG_SIZE << data_width. Calculate the total number of - * dma elements required to send the entire sg list + * STEDMA40_MAX_SEG_SIZE * data_width, where data_width is stored in Bytes. + * + * Calculate the total number of dma elements required to send the entire sg list. */ static int d40_size_2_dmalen(int size, u32 data_width1, u32 data_width2) { int dmalen; u32 max_w = max(data_width1, data_width2); u32 min_w = min(data_width1, data_width2); - u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE << min_w, 1 << max_w); + u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE * min_w, max_w); if (seg_max > STEDMA40_MAX_SEG_SIZE) - seg_max -= (1 << max_w); + seg_max -= max_w; - if (!IS_ALIGNED(size, 1 << max_w)) + if (!IS_ALIGNED(size, max_w)) return -EINVAL; if (size <= seg_max) @@ -1464,7 +1465,7 @@ static u32 d40_residue(struct d40_chan *d40c) >> D40_SREG_ELEM_PHY_ECNT_POS; } - return num_elt * (1 << d40c->dma_cfg.dst_info.data_width); + return num_elt * d40c->dma_cfg.dst_info.data_width; } static bool d40_tx_is_linked(struct d40_chan *d40c) @@ -1784,9 +1785,9 @@ static int d40_validate_conf(struct d40_chan *d40c, } if (d40_psize_2_burst_size(is_log, conf->src_info.psize) * - (1 << conf->src_info.data_width) != + conf->src_info.data_width != d40_psize_2_burst_size(is_log, conf->dst_info.psize) * - (1 << conf->dst_info.data_width)) { + conf->dst_info.data_width) { /* * The DMAC hardware only supports * src (burst x width) == dst (burst x width) @@ -2673,33 +2674,10 @@ static void d40_terminate_all(struct dma_chan *chan) static int dma40_config_to_halfchannel(struct d40_chan *d40c, struct stedma40_half_channel_info *info, - enum dma_slave_buswidth width, u32 maxburst) { - enum stedma40_periph_data_width addr_width; int psize; - switch (width) { - case DMA_SLAVE_BUSWIDTH_1_BYTE: - addr_width = STEDMA40_BYTE_WIDTH; - break; - case DMA_SLAVE_BUSWIDTH_2_BYTES: - addr_width = STEDMA40_HALFWORD_WIDTH; - break; - case DMA_SLAVE_BUSWIDTH_4_BYTES: - addr_width = STEDMA40_WORD_WIDTH; - break; - case DMA_SLAVE_BUSWIDTH_8_BYTES: - addr_width = STEDMA40_DOUBLEWORD_WIDTH; - break; - default: - dev_err(d40c->base->dev, - "illegal peripheral address width " - "requested (%d)\n", - width); - return -EINVAL; - } - if (chan_is_logical(d40c)) { if (maxburst >= 16) psize = STEDMA40_PSIZE_LOG_16; @@ -2720,7 +2698,6 @@ dma40_config_to_halfchannel(struct d40_chan *d40c, psize = STEDMA40_PSIZE_PHY_1; } - info->data_width = addr_width; info->psize = psize; info->flow_ctrl = STEDMA40_NO_FLOW_CTRL; @@ -2804,14 +2781,24 @@ static int d40_set_runtime_config(struct dma_chan *chan, src_maxburst = dst_maxburst * dst_addr_width / src_addr_width; } + /* Only valid widths are; 1, 2, 4 and 8. */ + if (src_addr_width <= DMA_SLAVE_BUSWIDTH_UNDEFINED || + src_addr_width > DMA_SLAVE_BUSWIDTH_8_BYTES || + dst_addr_width <= DMA_SLAVE_BUSWIDTH_UNDEFINED || + dst_addr_width > DMA_SLAVE_BUSWIDTH_8_BYTES || + ((src_addr_width > 1) && (src_addr_width & 1)) || + ((dst_addr_width > 1) && (dst_addr_width & 1))) + return -EINVAL; + + cfg->src_info.data_width = src_addr_width; + cfg->dst_info.data_width = dst_addr_width; + ret = dma40_config_to_halfchannel(d40c, &cfg->src_info, - src_addr_width, src_maxburst); if (ret) return ret; ret = dma40_config_to_halfchannel(d40c, &cfg->dst_info, - dst_addr_width, dst_maxburst); if (ret) return ret; diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c index 5ddd724dcdc5..a035dfeab6cb 100644 --- a/drivers/dma/ste_dma40_ll.c +++ b/drivers/dma/ste_dma40_ll.c @@ -10,6 +10,18 @@ #include "ste_dma40_ll.h" +u8 d40_width_to_bits(enum dma_slave_buswidth width) +{ + if (width == DMA_SLAVE_BUSWIDTH_1_BYTE) + return STEDMA40_ESIZE_8_BIT; + else if (width == DMA_SLAVE_BUSWIDTH_2_BYTES) + return STEDMA40_ESIZE_16_BIT; + else if (width == DMA_SLAVE_BUSWIDTH_8_BYTES) + return STEDMA40_ESIZE_64_BIT; + else + return STEDMA40_ESIZE_32_BIT; +} + /* Sets up proper LCSP1 and LCSP3 register for a logical channel */ void d40_log_cfg(struct stedma40_chan_cfg *cfg, u32 *lcsp1, u32 *lcsp3) @@ -39,11 +51,13 @@ void d40_log_cfg(struct stedma40_chan_cfg *cfg, l3 |= BIT(D40_MEM_LCSP3_DCFG_EIM_POS); l3 |= cfg->dst_info.psize << D40_MEM_LCSP3_DCFG_PSIZE_POS; - l3 |= cfg->dst_info.data_width << D40_MEM_LCSP3_DCFG_ESIZE_POS; + l3 |= d40_width_to_bits(cfg->dst_info.data_width) + << D40_MEM_LCSP3_DCFG_ESIZE_POS; l1 |= BIT(D40_MEM_LCSP1_SCFG_EIM_POS); l1 |= cfg->src_info.psize << D40_MEM_LCSP1_SCFG_PSIZE_POS; - l1 |= cfg->src_info.data_width << D40_MEM_LCSP1_SCFG_ESIZE_POS; + l1 |= d40_width_to_bits(cfg->src_info.data_width) + << D40_MEM_LCSP1_SCFG_ESIZE_POS; *lcsp1 = l1; *lcsp3 = l3; @@ -95,8 +109,10 @@ void d40_phy_cfg(struct stedma40_chan_cfg *cfg, u32 *src_cfg, u32 *dst_cfg) } /* Element size */ - src |= cfg->src_info.data_width << D40_SREG_CFG_ESIZE_POS; - dst |= cfg->dst_info.data_width << D40_SREG_CFG_ESIZE_POS; + src |= d40_width_to_bits(cfg->src_info.data_width) + << D40_SREG_CFG_ESIZE_POS; + dst |= d40_width_to_bits(cfg->dst_info.data_width) + << D40_SREG_CFG_ESIZE_POS; /* Set the priority bit to high for the physical channel */ if (cfg->high_priority) { @@ -133,23 +149,22 @@ static int d40_phy_fill_lli(struct d40_phy_lli *lli, num_elems = 2 << psize; /* Must be aligned */ - if (!IS_ALIGNED(data, 0x1 << data_width)) + if (!IS_ALIGNED(data, data_width)) return -EINVAL; /* Transfer size can't be smaller than (num_elms * elem_size) */ - if (data_size < num_elems * (0x1 << data_width)) + if (data_size < num_elems * data_width) return -EINVAL; /* The number of elements. IE now many chunks */ - lli->reg_elt = (data_size >> data_width) << D40_SREG_ELEM_PHY_ECNT_POS; + lli->reg_elt = (data_size / data_width) << D40_SREG_ELEM_PHY_ECNT_POS; /* * Distance to next element sized entry. * Usually the size of the element unless you want gaps. */ if (addr_inc) - lli->reg_elt |= (0x1 << data_width) << - D40_SREG_ELEM_PHY_EIDX_POS; + lli->reg_elt |= data_width << D40_SREG_ELEM_PHY_EIDX_POS; /* Where the data is */ lli->reg_ptr = data; @@ -177,16 +192,16 @@ static int d40_seg_size(int size, int data_width1, int data_width2) { u32 max_w = max(data_width1, data_width2); u32 min_w = min(data_width1, data_width2); - u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE << min_w, 1 << max_w); + u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE * min_w, max_w); if (seg_max > STEDMA40_MAX_SEG_SIZE) - seg_max -= (1 << max_w); + seg_max -= max_w; if (size <= seg_max) return size; if (size <= 2 * seg_max) - return ALIGN(size / 2, 1 << max_w); + return ALIGN(size / 2, max_w); return seg_max; } @@ -352,10 +367,10 @@ static void d40_log_fill_lli(struct d40_log_lli *lli, lli->lcsp13 = reg_cfg; /* The number of elements to transfer */ - lli->lcsp02 = ((data_size >> data_width) << + lli->lcsp02 = ((data_size / data_width) << D40_MEM_LCSP0_ECNT_POS) & D40_MEM_LCSP0_ECNT_MASK; - BUG_ON((data_size >> data_width) > STEDMA40_MAX_SEG_SIZE); + BUG_ON((data_size / data_width) > STEDMA40_MAX_SEG_SIZE); /* 16 LSBs address of the current element */ lli->lcsp02 |= data & D40_MEM_LCSP0_SPTR_MASK; diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index 54ddca615cb4..ceba6dc566a9 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -70,13 +70,6 @@ enum stedma40_flow_ctrl { STEDMA40_FLOW_CTRL, }; -enum stedma40_periph_data_width { - STEDMA40_BYTE_WIDTH = STEDMA40_ESIZE_8_BIT, - STEDMA40_HALFWORD_WIDTH = STEDMA40_ESIZE_16_BIT, - STEDMA40_WORD_WIDTH = STEDMA40_ESIZE_32_BIT, - STEDMA40_DOUBLEWORD_WIDTH = STEDMA40_ESIZE_64_BIT -}; - /** * struct stedma40_half_channel_info - dst/src channel configuration * @@ -87,7 +80,7 @@ enum stedma40_periph_data_width { */ struct stedma40_half_channel_info { bool big_endian; - enum stedma40_periph_data_width data_width; + enum dma_slave_buswidth data_width; int psize; enum stedma40_flow_ctrl flow_ctrl; }; diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index b6e5ae277299..31f9bbc74521 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -76,20 +76,20 @@ static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, dma_params = snd_soc_dai_get_dma_data(dai, substream); dma_cfg = dma_params->dma_cfg; - mem_data_width = STEDMA40_HALFWORD_WIDTH; + mem_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; switch (dma_params->data_size) { case 32: - per_data_width = STEDMA40_WORD_WIDTH; + per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; break; case 16: - per_data_width = STEDMA40_HALFWORD_WIDTH; + per_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; break; case 8: - per_data_width = STEDMA40_BYTE_WIDTH; + per_data_width = DMA_SLAVE_BUSWIDTH_1_BYTE; break; default: - per_data_width = STEDMA40_WORD_WIDTH; + per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -- cgit v1.3-18-gd494 From a7dacb68b35a193d9bdaabde1e4e98140d81a991 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 15 May 2013 10:51:59 +0100 Subject: dmaengine: ste_dma40: Allow memcpy channels to be configured from DT At this moment in time the memcpy channels which can be used by the D40 are fixed, as each supported platform in Mainline uses the same ones. However, platforms do exist which don't follow this convention, so these will need to be tailored. Fortunately, these platforms will be DT only, so this change has very little impact on platform data. Cc: Dan Williams Cc: Per Forlin Cc: Rabin Vincent Acked-by: Vinod Koul Acked-by: Arnd Bergmann Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- .../devicetree/bindings/dma/ste-dma40.txt | 2 ++ drivers/dma/ste_dma40.c | 40 +++++++++++++++++----- include/linux/platform_data/dma-ste-dma40.h | 2 ++ 3 files changed, 36 insertions(+), 8 deletions(-) (limited to 'include/linux/platform_data') diff --git a/Documentation/devicetree/bindings/dma/ste-dma40.txt b/Documentation/devicetree/bindings/dma/ste-dma40.txt index 2679a873522d..aa272d866f6e 100644 --- a/Documentation/devicetree/bindings/dma/ste-dma40.txt +++ b/Documentation/devicetree/bindings/dma/ste-dma40.txt @@ -6,6 +6,7 @@ Required properties: - reg-names: Names of the above areas to use during resource look-up - interrupt: Should contain the DMAC interrupt number - #dma-cells: must be <3> +- memcpy-channels: Channels to be used for memcpy Optional properties: - dma-channels: Number of channels supported by hardware - if not present @@ -21,6 +22,7 @@ Example: interrupts = <0 25 0x4>; #dma-cells = <2>; + memcpy-channels = <56 57 58 59 60>; dma-channels = <8>; }; diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 76c255fcdc2d..ae462d352110 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -58,6 +58,8 @@ #define D40_ALLOC_PHY BIT(30) #define D40_ALLOC_LOG_FREE 0 +#define D40_MEMCPY_MAX_CHANS 8 + /* Reserved event lines for memcpy only. */ #define DB8500_DMA_MEMCPY_EV_0 51 #define DB8500_DMA_MEMCPY_EV_1 56 @@ -522,6 +524,8 @@ struct d40_gen_dmac { * @phy_start: Physical memory start of the DMA registers. * @phy_size: Size of the DMA register map. * @irq: The IRQ number. + * @num_memcpy_chans: The number of channels used for memcpy (mem-to-mem + * transfers). * @num_phy_chans: The number of physical channels. Read from HW. This * is the number of available channels for this driver, not counting "Secure * mode" allocated physical channels. @@ -565,6 +569,7 @@ struct d40_base { phys_addr_t phy_start; resource_size_t phy_size; int irq; + int num_memcpy_chans; int num_phy_chans; int num_log_chans; struct device_dma_parameters dma_parms; @@ -2938,7 +2943,7 @@ static int __init d40_dmaengine_init(struct d40_base *base, } d40_chan_init(base, &base->dma_memcpy, base->log_chans, - base->num_log_chans, ARRAY_SIZE(dma40_memcpy_channels)); + base->num_log_chans, base->num_memcpy_chans); dma_cap_zero(base->dma_memcpy.cap_mask); dma_cap_set(DMA_MEMCPY, base->dma_memcpy.cap_mask); @@ -3139,6 +3144,7 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) struct d40_base *base = NULL; int num_log_chans = 0; int num_phy_chans; + int num_memcpy_chans; int clk_ret = -EINVAL; int i; u32 pid; @@ -3209,6 +3215,12 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) else num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4; + /* The number of channels used for memcpy */ + if (plat_data->num_of_memcpy_chans) + num_memcpy_chans = plat_data->num_of_memcpy_chans; + else + num_memcpy_chans = ARRAY_SIZE(dma40_memcpy_channels); + num_log_chans = num_phy_chans * D40_MAX_LOG_CHAN_PER_PHY; dev_info(&pdev->dev, @@ -3216,7 +3228,7 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) rev, res->start, num_phy_chans, num_log_chans); base = kzalloc(ALIGN(sizeof(struct d40_base), 4) + - (num_phy_chans + num_log_chans + ARRAY_SIZE(dma40_memcpy_channels)) * + (num_phy_chans + num_log_chans + num_memcpy_chans) * sizeof(struct d40_chan), GFP_KERNEL); if (base == NULL) { @@ -3226,6 +3238,7 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) base->rev = rev; base->clk = clk; + base->num_memcpy_chans = num_memcpy_chans; base->num_phy_chans = num_phy_chans; base->num_log_chans = num_log_chans; base->phy_start = res->start; @@ -3469,12 +3482,8 @@ static int __init d40_of_probe(struct platform_device *pdev, struct device_node *np) { struct stedma40_platform_data *pdata; - - /* - * FIXME: Fill in this routine as more support is added. - * First platform enabled (u8500) doens't need any extra - * properties to run, so this is fairly sparce currently. - */ + int num_memcpy = 0; + const const __be32 *list; pdata = devm_kzalloc(&pdev->dev, sizeof(struct stedma40_platform_data), @@ -3482,6 +3491,21 @@ static int __init d40_of_probe(struct platform_device *pdev, if (!pdata) return -ENOMEM; + list = of_get_property(np, "memcpy-channels", &num_memcpy); + num_memcpy /= sizeof(*list); + + if (num_memcpy > D40_MEMCPY_MAX_CHANS || num_memcpy <= 0) { + d40_err(&pdev->dev, + "Invalid number of memcpy channels specified (%d)\n", + num_memcpy); + return -EINVAL; + } + pdata->num_of_memcpy_chans = num_memcpy; + + of_property_read_u32_array(np, "memcpy-channels", + dma40_memcpy_channels, + num_memcpy); + pdev->dev.platform_data = pdata; return 0; diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index ceba6dc566a9..1bb9b1852256 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -132,6 +132,7 @@ struct stedma40_chan_cfg { * @num_of_soft_lli_chans: The number of channels that needs to be configured * to use SoftLLI. * @use_esram_lcla: flag for mapping the lcla into esram region + * @num_of_memcpy_chans: The number of channels reserved for memcpy. * @num_of_phy_chans: The number of physical channels implemented in HW. * 0 means reading the number of channels from DMA HW but this is only valid * for 'multiple of 4' channels, like 8. @@ -141,6 +142,7 @@ struct stedma40_platform_data { int *soft_lli_chans; int num_of_soft_lli_chans; bool use_esram_lcla; + int num_of_memcpy_chans; int num_of_phy_chans; }; -- cgit v1.3-18-gd494 From 36cb0066ffc55fd326c8a21ffe80aaa5bd16021b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 10 May 2013 16:48:36 +0200 Subject: gpio-rcar: Make the platform data gpio_base field signed The gpio_base field is used to specify the desired GPIO base for the GPIO controller. The GPIO core can automatically allocate a GPIO number range when the base is set to -1. To make this possible, make the field signed. Signed-off-by: Laurent Pinchart Acked-by: Linus Walleij Signed-off-by: Simon Horman --- include/linux/platform_data/gpio-rcar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/gpio-rcar.h b/include/linux/platform_data/gpio-rcar.h index b253f77a7ddf..aba7079ccc95 100644 --- a/include/linux/platform_data/gpio-rcar.h +++ b/include/linux/platform_data/gpio-rcar.h @@ -17,7 +17,7 @@ #define __GPIO_RCAR_H__ struct gpio_rcar_config { - unsigned int gpio_base; + int gpio_base; unsigned int irq_base; unsigned int number_of_pins; const char *pctl_name; -- cgit v1.3-18-gd494 From 7e1092b5a264c484001b0cdd1f49bea7884e3366 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 24 May 2013 18:47:24 +0900 Subject: gpio-rcar: Add support for IRQ_TYPE_EDGE_BOTH As hardware support for this feature is not universal for all SoCs a flag, has_both_edge_trigger, has been added to the platform data of the driver to allow this feature to be enabled. The motivation for this is to allow use of the gpio-keys driver on the lager board which is based on the r8a7790 SoC. The V2 of this patch has been fully exercised using that driver on that board. Signed-off-by: Magnus Damm Signed-off-by: Simon Horman --- drivers/gpio/gpio-rcar.c | 26 +++++++++++++++++++++----- include/linux/platform_data/gpio-rcar.h | 1 + 2 files changed, 22 insertions(+), 5 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 0f3d6473bf89..d173d56dbb8c 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -49,6 +49,7 @@ struct gpio_rcar_priv { #define POSNEG 0x20 #define EDGLEVEL 0x24 #define FILONOFF 0x28 +#define BOTHEDGE 0x4c static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs) { @@ -91,7 +92,8 @@ static void gpio_rcar_irq_enable(struct irq_data *d) static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p, unsigned int hwirq, bool active_high_rising_edge, - bool level_trigger) + bool level_trigger, + bool both) { unsigned long flags; @@ -108,6 +110,10 @@ static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p, /* Configure edge or level trigger in EDGLEVEL */ gpio_rcar_modify_bit(p, EDGLEVEL, hwirq, !level_trigger); + /* Select one edge or both edges in BOTHEDGE */ + if (p->config.has_both_edge_trigger) + gpio_rcar_modify_bit(p, BOTHEDGE, hwirq, both); + /* Select "Interrupt Input Mode" in IOINTSEL */ gpio_rcar_modify_bit(p, IOINTSEL, hwirq, true); @@ -127,16 +133,26 @@ static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type) switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_LEVEL_HIGH: - gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true); + gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true, + false); break; case IRQ_TYPE_LEVEL_LOW: - gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true); + gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true, + false); break; case IRQ_TYPE_EDGE_RISING: - gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false); + gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, + false); break; case IRQ_TYPE_EDGE_FALLING: - gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false); + gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false, + false); + break; + case IRQ_TYPE_EDGE_BOTH: + if (!p->config.has_both_edge_trigger) + return -EINVAL; + gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, + true); break; default: return -EINVAL; diff --git a/include/linux/platform_data/gpio-rcar.h b/include/linux/platform_data/gpio-rcar.h index aba7079ccc95..6c0027a3c370 100644 --- a/include/linux/platform_data/gpio-rcar.h +++ b/include/linux/platform_data/gpio-rcar.h @@ -21,6 +21,7 @@ struct gpio_rcar_config { unsigned int irq_base; unsigned int number_of_pins; const char *pctl_name; + unsigned has_both_edge_trigger:1; }; #endif /* __GPIO_RCAR_H__ */ -- cgit v1.3-18-gd494 From 11df28ab76cd9e98e3e0bbbff8648d0a02509507 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 8 Apr 2013 11:36:13 +0200 Subject: gpio-rcar: Add RCAR_GP_PIN macro Pins are numbered in the R-Car family documentation using a bank number and a pin number in the bank. As the Linux pin number space is linear, we need to flatten this by multiplying the bank number by 32 and adding the pin number. The resulting number bear no directly visible relationship to the documentation, making it error-prone. Add a RCAR_GP_PIN macro to convert from the documentation pin number space to the linear Linux space. Signed-off-by: Laurent Pinchart [horms+renesas@verge.net.au: non-trivial rebase on top of "sh-pfc: r8a7779: Don't group USB OVC and PENC pins"] Signed-off-by: Simon Horman --- include/linux/platform_data/gpio-rcar.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/gpio-rcar.h b/include/linux/platform_data/gpio-rcar.h index 6c0027a3c370..2d8d69432813 100644 --- a/include/linux/platform_data/gpio-rcar.h +++ b/include/linux/platform_data/gpio-rcar.h @@ -24,4 +24,6 @@ struct gpio_rcar_config { unsigned has_both_edge_trigger:1; }; +#define RCAR_GP_PIN(bank, pin) (((bank) * 32) + (pin)) + #endif /* __GPIO_RCAR_H__ */ -- cgit v1.3-18-gd494 From 1237e598a94b5a44a0162a4f4534d18ef8a81a7d Mon Sep 17 00:00:00 2001 From: Philippe Begnic Date: Mon, 27 May 2013 14:41:29 +0200 Subject: clk: ux500: Pass clock base adresses in initcall for u8540 and u9540 Align on u8500 version, pass clock base address in clk_init functions for u8540 and u9540. Signed-off-by: Linus Walleij Signed-off-by: Philippe Begnic Reviewed-by: Ulf Hansson Signed-off-by: Mike Turquette --- arch/arm/mach-ux500/cpu.c | 6 ++++-- drivers/clk/ux500/u8540_clk.c | 4 ++-- drivers/clk/ux500/u9540_clk.c | 4 ++-- include/linux/platform_data/clk-ux500.h | 6 ++++-- 4 files changed, 12 insertions(+), 8 deletions(-) (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c index b6145ea51641..e6fb0239151b 100644 --- a/arch/arm/mach-ux500/cpu.c +++ b/arch/arm/mach-ux500/cpu.c @@ -76,13 +76,15 @@ void __init ux500_init_irq(void) } else if (cpu_is_u9540()) { prcmu_early_init(U8500_PRCMU_BASE, SZ_8K - 1); ux500_pm_init(U8500_PRCMU_BASE, SZ_8K - 1); - u8500_clk_init(U8500_CLKRST1_BASE, U8500_CLKRST2_BASE, + u9540_clk_init(U8500_CLKRST1_BASE, U8500_CLKRST2_BASE, U8500_CLKRST3_BASE, U8500_CLKRST5_BASE, U8500_CLKRST6_BASE); } else if (cpu_is_u8540()) { prcmu_early_init(U8500_PRCMU_BASE, SZ_8K + SZ_4K - 1); ux500_pm_init(U8500_PRCMU_BASE, SZ_8K + SZ_4K - 1); - u8540_clk_init(); + u8540_clk_init(U8500_CLKRST1_BASE, U8500_CLKRST2_BASE, + U8500_CLKRST3_BASE, U8500_CLKRST5_BASE, + U8500_CLKRST6_BASE); } } diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c index 10adfd2ead21..90f3c88694b9 100644 --- a/drivers/clk/ux500/u8540_clk.c +++ b/drivers/clk/ux500/u8540_clk.c @@ -12,10 +12,10 @@ #include #include #include - #include "clk.h" -void u8540_clk_init(void) +void u8540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base) { /* register clocks here */ } diff --git a/drivers/clk/ux500/u9540_clk.c b/drivers/clk/ux500/u9540_clk.c index dbc0191e16c8..44794782e7e0 100644 --- a/drivers/clk/ux500/u9540_clk.c +++ b/drivers/clk/ux500/u9540_clk.c @@ -12,10 +12,10 @@ #include #include #include - #include "clk.h" -void u9540_clk_init(void) +void u9540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base) { /* register clocks here */ } diff --git a/include/linux/platform_data/clk-ux500.h b/include/linux/platform_data/clk-ux500.h index 320d9c39ea0a..9d98f3aaa16c 100644 --- a/include/linux/platform_data/clk-ux500.h +++ b/include/linux/platform_data/clk-ux500.h @@ -12,7 +12,9 @@ void u8500_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, u32 clkrst5_base, u32 clkrst6_base); -void u9540_clk_init(void); -void u8540_clk_init(void); +void u9540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base); +void u8540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base); #endif /* __CLK_UX500_H */ -- cgit v1.3-18-gd494 From 6a82e2a83e568dc121326b4bab24035ce7a2f50e Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sun, 2 Jun 2013 01:52:28 +0400 Subject: phy-rcar-usb: add platform data Currently the driver hard-codes USBPCTRL0 register to 0. It is wrong since this register contains board-specific USB ports configuration and so its value should be somehow passed via the platform data. Add the global header file containing 'struct rcar_phy_platform_data' consisting of the various bit fields describing USB ports' pin configuration. Signed-off-by: Sergei Shtylyov Acked-by: Kuninori Morimoto Acked-by: Felipe Balbi Signed-off-by: Simon Horman --- include/linux/platform_data/usb-rcar-phy.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 include/linux/platform_data/usb-rcar-phy.h (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/usb-rcar-phy.h b/include/linux/platform_data/usb-rcar-phy.h new file mode 100644 index 000000000000..c49f35ab14c7 --- /dev/null +++ b/include/linux/platform_data/usb-rcar-phy.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __USB_RCAR_PHY_H +#define __USB_RCAR_PHY_H + +#include + +struct rcar_phy_platform_data { + bool port1_func:1; /* true: port 1 used by function, false: host */ + unsigned penc1:1; /* Output of the PENC1 pin in function mode */ + struct { /* Overcurrent pin control for ports 0..2 */ + bool select_3_3v:1; /* true: USB_OVCn pin, false: OVCn pin */ + /* Set to false on port 1 in function mode */ + bool active_high:1; /* true: active high, false: active low */ + /* Set to true on port 1 in function mode */ + } ovc_pin[3]; +}; + +#endif /* __USB_RCAR_PHY_H */ -- cgit v1.3-18-gd494 From 54407f190c8d542572a9547ba5460d811810b6e4 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sun, 9 Jun 2013 00:34:36 +0400 Subject: phy-rcar-usb: add R8A7778 support The driver currently only supports R8A7779 SoC. Compared to it, R8A7778 USB-PHY has extra register range containing two high-speed signal quality characteristic control registers which should be set up during USB-PHY startup depending on whether a ferrite bead is in use or not. So, we now handle an optional second memory range in the driver's probe method, add the 'ferrite_bead' field to the driver's platform data, and add an extra (optional) step to the USB-PHY startup routine which sets up the extended registers. Also mark in the driver's Kconfig section that R8A7778 is now supported and generally clarify that section, uppercasing the word "phy" and also changing the module name that got lost in the big driver rename, while at it... The patch has been tested on the Marzen and BOCK-W boards. Signed-off-by: Sergei Shtylyov Acked-by: Felipe Balbi Signed-off-by: Simon Horman --- drivers/usb/phy/Kconfig | 10 ++++---- drivers/usb/phy/phy-rcar-usb.c | 37 +++++++++++++++++++++++++----- include/linux/platform_data/usb-rcar-phy.h | 4 +++- 3 files changed, 39 insertions(+), 12 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 371d0e74e909..ac0b98f78c70 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -181,15 +181,15 @@ config USB_MXS_PHY MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x. config USB_RCAR_PHY - tristate "Renesas R-Car USB phy support" + tristate "Renesas R-Car USB PHY support" depends on USB || USB_GADGET help - Say Y here to add support for the Renesas R-Car USB phy driver. - This chip is typically used as USB phy for USB host, gadget. - This driver supports: R8A7779 + Say Y here to add support for the Renesas R-Car USB common PHY driver. + This chip is typically used as USB PHY for USB host, gadget. + This driver supports R8A7778 and R8A7779. To compile this driver as a module, choose M here: the - module will be called rcar-phy. + module will be called phy-rcar-usb. config USB_ULPI bool "Generic ULPI Transceiver Driver" diff --git a/drivers/usb/phy/phy-rcar-usb.c b/drivers/usb/phy/phy-rcar-usb.c index 823b2bb807d2..ae909408958d 100644 --- a/drivers/usb/phy/phy-rcar-usb.c +++ b/drivers/usb/phy/phy-rcar-usb.c @@ -26,15 +26,21 @@ #define USBOH0 0x1C #define USBCTL0 0x58 +/* High-speed signal quality characteristic control registers (R8A7778 only) */ +#define HSQCTL1 0x24 +#define HSQCTL2 0x28 + /* USBPCTRL0 */ -#define OVC2 (1 << 10) /* Switches the OVC input pin for port 2: */ +#define OVC2 (1 << 10) /* (R8A7779 only) */ + /* Switches the OVC input pin for port 2: */ /* 1: USB_OVC2, 0: OVC2 */ #define OVC1_VBUS1 (1 << 9) /* Switches the OVC input pin for port 1: */ /* 1: USB_OVC1, 0: OVC1/VBUS1 */ /* Function mode: set to 0 */ #define OVC0 (1 << 8) /* Switches the OVC input pin for port 0: */ /* 1: USB_OVC0 pin, 0: OVC0 */ -#define OVC2_ACT (1 << 6) /* Host mode: OVC2 polarity: */ +#define OVC2_ACT (1 << 6) /* (R8A7779 only) */ + /* Host mode: OVC2 polarity: */ /* 1: active-high, 0: active-low */ #define PENC (1 << 4) /* Function mode: output level of PENC1 pin: */ /* 1: high, 0: low */ @@ -59,6 +65,7 @@ struct rcar_usb_phy_priv { spinlock_t lock; void __iomem *reg0; + void __iomem *reg1; int counter; }; @@ -78,6 +85,7 @@ static int rcar_usb_phy_init(struct usb_phy *phy) struct device *dev = phy->dev; struct rcar_phy_platform_data *pdata = dev->platform_data; void __iomem *reg0 = priv->reg0; + void __iomem *reg1 = priv->reg1; static const u8 ovcn_act[] = { OVC0_ACT, OVC1_ACT, OVC2_ACT }; int i; u32 val; @@ -96,7 +104,16 @@ static int rcar_usb_phy_init(struct usb_phy *phy) /* (2) start USB-PHY internal PLL */ iowrite32(PHY_ENB | PLL_ENB, (reg0 + USBPCTRL1)); - /* (3) USB module status check */ + /* (3) set USB-PHY in accord with the conditions of usage */ + if (reg1) { + u32 hsqctl1 = pdata->ferrite_bead ? 0x41 : 0; + u32 hsqctl2 = pdata->ferrite_bead ? 0x0d : 7; + + iowrite32(hsqctl1, reg1 + HSQCTL1); + iowrite32(hsqctl2, reg1 + HSQCTL2); + } + + /* (4) USB module status check */ for (i = 0; i < 1024; i++) { udelay(10); val = ioread32(reg0 + USBST); @@ -109,7 +126,7 @@ static int rcar_usb_phy_init(struct usb_phy *phy) goto phy_init_end; } - /* (4) USB-PHY reset clear */ + /* (5) USB-PHY reset clear */ iowrite32(PHY_ENB | PLL_ENB | PHY_RST, (reg0 + USBPCTRL1)); /* Board specific port settings */ @@ -162,9 +179,9 @@ static void rcar_usb_phy_shutdown(struct usb_phy *phy) static int rcar_usb_phy_probe(struct platform_device *pdev) { struct rcar_usb_phy_priv *priv; - struct resource *res0; + struct resource *res0, *res1; struct device *dev = &pdev->dev; - void __iomem *reg0; + void __iomem *reg0, *reg1 = NULL; int ret; if (!pdev->dev.platform_data) { @@ -182,6 +199,13 @@ static int rcar_usb_phy_probe(struct platform_device *pdev) if (IS_ERR(reg0)) return PTR_ERR(reg0); + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res1) { + reg1 = devm_ioremap_resource(dev, res1); + if (IS_ERR(reg1)) + return PTR_ERR(reg1); + } + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(dev, "priv data allocation error\n"); @@ -189,6 +213,7 @@ static int rcar_usb_phy_probe(struct platform_device *pdev) } priv->reg0 = reg0; + priv->reg1 = reg1; priv->counter = 0; priv->phy.dev = dev; priv->phy.label = dev_name(dev); diff --git a/include/linux/platform_data/usb-rcar-phy.h b/include/linux/platform_data/usb-rcar-phy.h index c49f35ab14c7..8ec6964a32a5 100644 --- a/include/linux/platform_data/usb-rcar-phy.h +++ b/include/linux/platform_data/usb-rcar-phy.h @@ -13,6 +13,8 @@ #include struct rcar_phy_platform_data { + bool ferrite_bead:1; /* (R8A7778 only) */ + bool port1_func:1; /* true: port 1 used by function, false: host */ unsigned penc1:1; /* Output of the PENC1 pin in function mode */ struct { /* Overcurrent pin control for ports 0..2 */ @@ -20,7 +22,7 @@ struct rcar_phy_platform_data { /* Set to false on port 1 in function mode */ bool active_high:1; /* true: active high, false: active low */ /* Set to true on port 1 in function mode */ - } ovc_pin[3]; + } ovc_pin[3]; /* (R8A7778 only has 2 ports) */ }; #endif /* __USB_RCAR_PHY_H */ -- cgit v1.3-18-gd494 From b8a7cf8e2b15a3abac0a9e376b6b7ed4bbb6ee8e Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Mon, 28 Jan 2013 17:21:58 -0600 Subject: ARM: OMAP2+: mbox: remove dependencies with soc.h The OMAP mailbox platform driver code has been cleaned up to remove the dependencies with soc.h in preparation for moving the mailbox code to drivers folder. The code relied on cpu_is_xxx/soc_is_xxx macros previously to pick the the right set of mailbox devices and register with the mailbox driver. This data is now represented in a concise format and moved to the respective omap_hwmod data files and published to the driver through the platform data. Cc: Paul Walmsley Signed-off-by: Suman Anna --- arch/arm/mach-omap2/devices.c | 9 +- arch/arm/mach-omap2/mailbox.c | 254 +++++++++++------------------ arch/arm/mach-omap2/omap_hwmod_2420_data.c | 12 ++ arch/arm/mach-omap2/omap_hwmod_2430_data.c | 11 ++ arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 11 ++ arch/arm/plat-omap/include/plat/mailbox.h | 2 +- include/linux/platform_data/mailbox-omap.h | 53 ++++++ 7 files changed, 189 insertions(+), 163 deletions(-) create mode 100644 include/linux/platform_data/mailbox-omap.h (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 4269fc145698..4c97a86115e6 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -332,14 +333,20 @@ static inline void __init omap_init_mbox(void) { struct omap_hwmod *oh; struct platform_device *pdev; + struct omap_mbox_pdata *pdata; oh = omap_hwmod_lookup("mailbox"); if (!oh) { pr_err("%s: unable to find hwmod\n", __func__); return; } + if (!oh->dev_attr) { + pr_err("%s: hwmod doesn't have valid attrs\n", __func__); + return; + } - pdev = omap_device_build("omap-mailbox", -1, oh, NULL, 0); + pdata = (struct omap_mbox_pdata *)oh->dev_attr; + pdev = omap_device_build("omap-mailbox", -1, oh, pdata, sizeof(*pdata)); WARN(IS_ERR(pdev), "%s: could not build device, err %ld\n", __func__, PTR_ERR(pdev)); } diff --git a/arch/arm/mach-omap2/mailbox.c b/arch/arm/mach-omap2/mailbox.c index b01aae69010f..de21198d54ff 100644 --- a/arch/arm/mach-omap2/mailbox.c +++ b/arch/arm/mach-omap2/mailbox.c @@ -11,16 +11,16 @@ */ #include +#include #include #include #include #include #include +#include #include -#include "soc.h" - #define MAILBOX_REVISION 0x000 #define MAILBOX_MESSAGE(m) (0x040 + 4 * (m)) #define MAILBOX_FIFOSTATUS(m) (0x080 + 4 * (m)) @@ -59,6 +59,7 @@ struct omap_mbox2_priv { u32 notfull_bit; u32 ctx[OMAP4_MBOX_NR_REGS]; unsigned long irqdisable; + u32 intr_type; }; static inline unsigned int mbox_read_reg(size_t ofs) @@ -136,7 +137,11 @@ static void omap2_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) struct omap_mbox2_priv *p = mbox->priv; u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit; - if (!cpu_is_omap44xx()) + /* + * Read and update the interrupt configuration register for pre-OMAP4. + * OMAP4 and later SoCs have a dedicated interrupt disabling register. + */ + if (!p->intr_type) bit = mbox_read_reg(p->irqdisable) & ~bit; mbox_write_reg(bit, p->irqdisable); @@ -168,7 +173,8 @@ static void omap2_mbox_save_ctx(struct omap_mbox *mbox) int i; struct omap_mbox2_priv *p = mbox->priv; int nr_regs; - if (cpu_is_omap44xx()) + + if (p->intr_type) nr_regs = OMAP4_MBOX_NR_REGS; else nr_regs = MBOX_NR_REGS; @@ -185,7 +191,8 @@ static void omap2_mbox_restore_ctx(struct omap_mbox *mbox) int i; struct omap_mbox2_priv *p = mbox->priv; int nr_regs; - if (cpu_is_omap44xx()) + + if (p->intr_type) nr_regs = OMAP4_MBOX_NR_REGS; else nr_regs = MBOX_NR_REGS; @@ -213,188 +220,113 @@ static struct omap_mbox_ops omap2_mbox_ops = { .restore_ctx = omap2_mbox_restore_ctx, }; -/* - * MAILBOX 0: ARM -> DSP, - * MAILBOX 1: ARM <- DSP. - * MAILBOX 2: ARM -> IVA, - * MAILBOX 3: ARM <- IVA. - */ - -/* FIXME: the following structs should be filled automatically by the user id */ - -#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP2) -/* DSP */ -static struct omap_mbox2_priv omap2_mbox_dsp_priv = { - .tx_fifo = { - .msg = MAILBOX_MESSAGE(0), - .fifo_stat = MAILBOX_FIFOSTATUS(0), - }, - .rx_fifo = { - .msg = MAILBOX_MESSAGE(1), - .msg_stat = MAILBOX_MSGSTATUS(1), - }, - .irqenable = MAILBOX_IRQENABLE(0), - .irqstatus = MAILBOX_IRQSTATUS(0), - .notfull_bit = MAILBOX_IRQ_NOTFULL(0), - .newmsg_bit = MAILBOX_IRQ_NEWMSG(1), - .irqdisable = MAILBOX_IRQENABLE(0), -}; - -struct omap_mbox mbox_dsp_info = { - .name = "dsp", - .ops = &omap2_mbox_ops, - .priv = &omap2_mbox_dsp_priv, -}; -#endif - -#if defined(CONFIG_ARCH_OMAP3) -struct omap_mbox *omap3_mboxes[] = { &mbox_dsp_info, NULL }; -#endif - -#if defined(CONFIG_SOC_OMAP2420) -/* IVA */ -static struct omap_mbox2_priv omap2_mbox_iva_priv = { - .tx_fifo = { - .msg = MAILBOX_MESSAGE(2), - .fifo_stat = MAILBOX_FIFOSTATUS(2), - }, - .rx_fifo = { - .msg = MAILBOX_MESSAGE(3), - .msg_stat = MAILBOX_MSGSTATUS(3), - }, - .irqenable = MAILBOX_IRQENABLE(3), - .irqstatus = MAILBOX_IRQSTATUS(3), - .notfull_bit = MAILBOX_IRQ_NOTFULL(2), - .newmsg_bit = MAILBOX_IRQ_NEWMSG(3), - .irqdisable = MAILBOX_IRQENABLE(3), -}; - -static struct omap_mbox mbox_iva_info = { - .name = "iva", - .ops = &omap2_mbox_ops, - .priv = &omap2_mbox_iva_priv, -}; -#endif - -#ifdef CONFIG_ARCH_OMAP2 -struct omap_mbox *omap2_mboxes[] = { - &mbox_dsp_info, -#ifdef CONFIG_SOC_OMAP2420 - &mbox_iva_info, -#endif - NULL -}; -#endif - -#if defined(CONFIG_ARCH_OMAP4) -/* OMAP4 */ -static struct omap_mbox2_priv omap2_mbox_1_priv = { - .tx_fifo = { - .msg = MAILBOX_MESSAGE(0), - .fifo_stat = MAILBOX_FIFOSTATUS(0), - }, - .rx_fifo = { - .msg = MAILBOX_MESSAGE(1), - .msg_stat = MAILBOX_MSGSTATUS(1), - }, - .irqenable = OMAP4_MAILBOX_IRQENABLE(0), - .irqstatus = OMAP4_MAILBOX_IRQSTATUS(0), - .notfull_bit = MAILBOX_IRQ_NOTFULL(0), - .newmsg_bit = MAILBOX_IRQ_NEWMSG(1), - .irqdisable = OMAP4_MAILBOX_IRQENABLE_CLR(0), -}; - -struct omap_mbox mbox_1_info = { - .name = "mailbox-1", - .ops = &omap2_mbox_ops, - .priv = &omap2_mbox_1_priv, -}; - -static struct omap_mbox2_priv omap2_mbox_2_priv = { - .tx_fifo = { - .msg = MAILBOX_MESSAGE(3), - .fifo_stat = MAILBOX_FIFOSTATUS(3), - }, - .rx_fifo = { - .msg = MAILBOX_MESSAGE(2), - .msg_stat = MAILBOX_MSGSTATUS(2), - }, - .irqenable = OMAP4_MAILBOX_IRQENABLE(0), - .irqstatus = OMAP4_MAILBOX_IRQSTATUS(0), - .notfull_bit = MAILBOX_IRQ_NOTFULL(3), - .newmsg_bit = MAILBOX_IRQ_NEWMSG(2), - .irqdisable = OMAP4_MAILBOX_IRQENABLE_CLR(0), -}; - -struct omap_mbox mbox_2_info = { - .name = "mailbox-2", - .ops = &omap2_mbox_ops, - .priv = &omap2_mbox_2_priv, -}; - -struct omap_mbox *omap4_mboxes[] = { &mbox_1_info, &mbox_2_info, NULL }; -#endif - static int omap2_mbox_probe(struct platform_device *pdev) { struct resource *mem; int ret; - struct omap_mbox **list; - - if (false) - ; -#if defined(CONFIG_ARCH_OMAP3) - else if (cpu_is_omap34xx()) { - list = omap3_mboxes; + struct omap_mbox **list, *mbox, *mboxblk; + struct omap_mbox2_priv *priv, *privblk; + struct omap_mbox_pdata *pdata = pdev->dev.platform_data; + struct omap_mbox_dev_info *info; + int i; - list[0]->irq = platform_get_irq(pdev, 0); + if (!pdata || !pdata->info_cnt || !pdata->info) { + pr_err("%s: platform not supported\n", __func__); + return -ENODEV; } -#endif -#if defined(CONFIG_ARCH_OMAP2) - else if (cpu_is_omap2430()) { - list = omap2_mboxes; - list[0]->irq = platform_get_irq(pdev, 0); - } else if (cpu_is_omap2420()) { - list = omap2_mboxes; + /* allocate one extra for marking end of list */ + list = kzalloc((pdata->info_cnt + 1) * sizeof(*list), GFP_KERNEL); + if (!list) + return -ENOMEM; - list[0]->irq = platform_get_irq_byname(pdev, "dsp"); - list[1]->irq = platform_get_irq_byname(pdev, "iva"); + mboxblk = mbox = kzalloc(pdata->info_cnt * sizeof(*mbox), GFP_KERNEL); + if (!mboxblk) { + ret = -ENOMEM; + goto free_list; } -#endif -#if defined(CONFIG_ARCH_OMAP4) - else if (cpu_is_omap44xx()) { - list = omap4_mboxes; - list[0]->irq = list[1]->irq = platform_get_irq(pdev, 0); + privblk = priv = kzalloc(pdata->info_cnt * sizeof(*priv), GFP_KERNEL); + if (!privblk) { + ret = -ENOMEM; + goto free_mboxblk; } -#endif - else { - pr_err("%s: platform not supported\n", __func__); - return -ENODEV; + + info = pdata->info; + for (i = 0; i < pdata->info_cnt; i++, info++, priv++) { + priv->tx_fifo.msg = MAILBOX_MESSAGE(info->tx_id); + priv->tx_fifo.fifo_stat = MAILBOX_FIFOSTATUS(info->tx_id); + priv->rx_fifo.msg = MAILBOX_MESSAGE(info->rx_id); + priv->rx_fifo.msg_stat = MAILBOX_MSGSTATUS(info->rx_id); + priv->notfull_bit = MAILBOX_IRQ_NOTFULL(info->tx_id); + priv->newmsg_bit = MAILBOX_IRQ_NEWMSG(info->rx_id); + if (pdata->intr_type) { + priv->irqenable = OMAP4_MAILBOX_IRQENABLE(info->usr_id); + priv->irqstatus = OMAP4_MAILBOX_IRQSTATUS(info->usr_id); + priv->irqdisable = + OMAP4_MAILBOX_IRQENABLE_CLR(info->usr_id); + } else { + priv->irqenable = MAILBOX_IRQENABLE(info->usr_id); + priv->irqstatus = MAILBOX_IRQSTATUS(info->usr_id); + priv->irqdisable = MAILBOX_IRQENABLE(info->usr_id); + } + priv->intr_type = pdata->intr_type; + + mbox->priv = priv; + mbox->name = info->name; + mbox->ops = &omap2_mbox_ops; + mbox->irq = platform_get_irq(pdev, info->irq_id); + if (mbox->irq < 0) { + ret = mbox->irq; + goto free_privblk; + } + list[i] = mbox++; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) - return -ENOENT; + if (!mem) { + ret = -ENOENT; + goto free_privblk; + } mbox_base = ioremap(mem->start, resource_size(mem)); - if (!mbox_base) - return -ENOMEM; + if (!mbox_base) { + ret = -ENOMEM; + goto free_privblk; + } ret = omap_mbox_register(&pdev->dev, list); - if (ret) { - iounmap(mbox_base); - return ret; - } + if (ret) + goto unmap_mbox; + platform_set_drvdata(pdev, list); return 0; + +unmap_mbox: + iounmap(mbox_base); +free_privblk: + kfree(privblk); +free_mboxblk: + kfree(mboxblk); +free_list: + kfree(list); + return ret; } static int omap2_mbox_remove(struct platform_device *pdev) { + struct omap_mbox2_priv *privblk; + struct omap_mbox **list = platform_get_drvdata(pdev); + struct omap_mbox *mboxblk = list[0]; + + privblk = mboxblk->priv; omap_mbox_unregister(); iounmap(mbox_base); + kfree(privblk); + kfree(mboxblk); + kfree(list); + platform_set_drvdata(pdev, NULL); + return 0; } diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c index 5137cc84b504..dbcb928eea50 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "omap_hwmod.h" @@ -166,6 +167,16 @@ static struct omap_hwmod omap2420_dma_system_hwmod = { }; /* mailbox */ +static struct omap_mbox_dev_info omap2420_mailbox_info[] = { + { .name = "dsp", .tx_id = 0, .rx_id = 1, .irq_id = 0, .usr_id = 0 }, + { .name = "iva", .tx_id = 2, .rx_id = 3, .irq_id = 1, .usr_id = 3 }, +}; + +static struct omap_mbox_pdata omap2420_mailbox_attrs = { + .info_cnt = ARRAY_SIZE(omap2420_mailbox_info), + .info = omap2420_mailbox_info, +}; + static struct omap_hwmod_irq_info omap2420_mailbox_irqs[] = { { .name = "dsp", .irq = 26 + OMAP_INTC_START, }, { .name = "iva", .irq = 34 + OMAP_INTC_START, }, @@ -186,6 +197,7 @@ static struct omap_hwmod omap2420_mailbox_hwmod = { .idlest_idle_bit = OMAP24XX_ST_MAILBOXES_SHIFT, }, }, + .dev_attr = &omap2420_mailbox_attrs, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c index 4ce999ee3ee9..df2f8742fe41 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "omap_hwmod.h" @@ -170,6 +171,15 @@ static struct omap_hwmod omap2430_dma_system_hwmod = { }; /* mailbox */ +static struct omap_mbox_dev_info omap2430_mailbox_info[] = { + { .name = "dsp", .tx_id = 0, .rx_id = 1 }, +}; + +static struct omap_mbox_pdata omap2430_mailbox_attrs = { + .info_cnt = ARRAY_SIZE(omap2430_mailbox_info), + .info = omap2430_mailbox_info, +}; + static struct omap_hwmod_irq_info omap2430_mailbox_irqs[] = { { .irq = 26 + OMAP_INTC_START, }, { .irq = -1 }, @@ -189,6 +199,7 @@ static struct omap_hwmod omap2430_mailbox_hwmod = { .idlest_idle_bit = OMAP24XX_ST_MAILBOXES_SHIFT, }, }, + .dev_attr = &omap2430_mailbox_attrs, }; /* mcspi3 */ diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 31c7126eb3bb..9ac5122396d8 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "am35xx.h" @@ -1505,6 +1506,15 @@ static struct omap_hwmod_class omap3xxx_mailbox_hwmod_class = { .sysc = &omap3xxx_mailbox_sysc, }; +static struct omap_mbox_dev_info omap3xxx_mailbox_info[] = { + { .name = "dsp", .tx_id = 0, .rx_id = 1 }, +}; + +static struct omap_mbox_pdata omap3xxx_mailbox_attrs = { + .info_cnt = ARRAY_SIZE(omap3xxx_mailbox_info), + .info = omap3xxx_mailbox_info, +}; + static struct omap_hwmod_irq_info omap3xxx_mailbox_irqs[] = { { .irq = 26 + OMAP_INTC_START, }, { .irq = -1 }, @@ -1524,6 +1534,7 @@ static struct omap_hwmod omap3xxx_mailbox_hwmod = { .idlest_idle_bit = OMAP3430_ST_MAILBOXES_SHIFT, }, }, + .dev_attr = &omap3xxx_mailbox_attrs, }; /* diff --git a/arch/arm/plat-omap/include/plat/mailbox.h b/arch/arm/plat-omap/include/plat/mailbox.h index cc3921e9059c..e98f7e234686 100644 --- a/arch/arm/plat-omap/include/plat/mailbox.h +++ b/arch/arm/plat-omap/include/plat/mailbox.h @@ -51,7 +51,7 @@ struct omap_mbox_queue { }; struct omap_mbox { - char *name; + const char *name; unsigned int irq; struct omap_mbox_queue *txq, *rxq; struct omap_mbox_ops *ops; diff --git a/include/linux/platform_data/mailbox-omap.h b/include/linux/platform_data/mailbox-omap.h new file mode 100644 index 000000000000..676cd642bb3f --- /dev/null +++ b/include/linux/platform_data/mailbox-omap.h @@ -0,0 +1,53 @@ +/* + * mailbox-omap.h + * + * Copyright (C) 2013 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _PLAT_MAILBOX_H +#define _PLAT_MAILBOX_H + +/* Interrupt register configuration types */ +#define MBOX_INTR_CFG_TYPE1 (0) +#define MBOX_INTR_CFG_TYPE2 (1) + +/** + * struct omap_mbox_dev_info - OMAP mailbox device attribute info + * @name: name of the mailbox device + * @tx_id: mailbox queue id used for transmitting messages + * @rx_id: mailbox queue id on which messages are received + * @irq_id: irq identifier number to use from the hwmod data + * @usr_id: mailbox user id for identifying the interrupt into + * the MPU interrupt controller. + */ +struct omap_mbox_dev_info { + const char *name; + u32 tx_id; + u32 rx_id; + u32 irq_id; + u32 usr_id; +}; + +/** + * struct omap_mbox_pdata - OMAP mailbox platform data + * @intr_type: type of interrupt configuration registers used + while programming mailbox queue interrupts + * @info_cnt: number of mailbox devices for the platform + * @info: array of mailbox device attributes + */ +struct omap_mbox_pdata { + u32 intr_type; + u32 info_cnt; + struct omap_mbox_dev_info *info; +}; + +#endif /* _PLAT_MAILBOX_H */ -- cgit v1.3-18-gd494 From fe32c1f6024e357f586b1d666237cab80a1215ce Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 7 May 2013 17:30:27 -0500 Subject: ARM: OMAP2+: add user and fifo info to mailbox platform data The different generations of OMAP2+ SoCs have almost the same mailbox IP, but the IP has configurable parameters for number of users (interrupts it can generate out towards processors) and number of fifos (the base unidirectional h/w communication channel). This data cannot be read from any registers, and so has been added to the platform data. This data together with the interrupt-type configuration can be used in properly figuring out the number of registers to save and restore in the OMAP mailbox driver code. Cc: Paul Walmsley Signed-off-by: Suman Anna --- arch/arm/mach-omap2/omap_hwmod_2420_data.c | 2 ++ arch/arm/mach-omap2/omap_hwmod_2430_data.c | 2 ++ arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 2 ++ include/linux/platform_data/mailbox-omap.h | 5 +++++ 4 files changed, 11 insertions(+) (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c index dbcb928eea50..d8b9d60f854f 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c @@ -173,6 +173,8 @@ static struct omap_mbox_dev_info omap2420_mailbox_info[] = { }; static struct omap_mbox_pdata omap2420_mailbox_attrs = { + .num_users = 4, + .num_fifos = 6, .info_cnt = ARRAY_SIZE(omap2420_mailbox_info), .info = omap2420_mailbox_info, }; diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c index df2f8742fe41..5b9083461dc5 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c @@ -176,6 +176,8 @@ static struct omap_mbox_dev_info omap2430_mailbox_info[] = { }; static struct omap_mbox_pdata omap2430_mailbox_attrs = { + .num_users = 4, + .num_fifos = 6, .info_cnt = ARRAY_SIZE(omap2430_mailbox_info), .info = omap2430_mailbox_info, }; diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 9ac5122396d8..8e4cbc99ce28 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -1511,6 +1511,8 @@ static struct omap_mbox_dev_info omap3xxx_mailbox_info[] = { }; static struct omap_mbox_pdata omap3xxx_mailbox_attrs = { + .num_users = 2, + .num_fifos = 2, .info_cnt = ARRAY_SIZE(omap3xxx_mailbox_info), .info = omap3xxx_mailbox_info, }; diff --git a/include/linux/platform_data/mailbox-omap.h b/include/linux/platform_data/mailbox-omap.h index 676cd642bb3f..4631dbb4255e 100644 --- a/include/linux/platform_data/mailbox-omap.h +++ b/include/linux/platform_data/mailbox-omap.h @@ -41,11 +41,16 @@ struct omap_mbox_dev_info { * struct omap_mbox_pdata - OMAP mailbox platform data * @intr_type: type of interrupt configuration registers used while programming mailbox queue interrupts + * @num_users: number of users (processor devices) that the mailbox + * h/w block can interrupt + * @num_fifos: number of h/w fifos within the mailbox h/w block * @info_cnt: number of mailbox devices for the platform * @info: array of mailbox device attributes */ struct omap_mbox_pdata { u32 intr_type; + u32 num_users; + u32 num_fifos; u32 info_cnt; struct omap_mbox_dev_info *info; }; -- cgit v1.3-18-gd494 From f83478240e742efe1103110c28d48cc2b4dcee5c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 11 Jun 2013 17:56:00 +0100 Subject: iio:dac: Add support for the AD7303 This patch adds support for the AD7303. The AD7303 is a simple 2 channel 8 bit DAC with an SPI interface. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/ad7303.txt | 23 ++ drivers/iio/dac/Kconfig | 10 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad7303.c | 315 +++++++++++++++++++++ include/linux/platform_data/ad7303.h | 21 ++ 5 files changed, 370 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/ad7303.txt create mode 100644 drivers/iio/dac/ad7303.c create mode 100644 include/linux/platform_data/ad7303.h (limited to 'include/linux/platform_data') diff --git a/Documentation/devicetree/bindings/iio/dac/ad7303.txt b/Documentation/devicetree/bindings/iio/dac/ad7303.txt new file mode 100644 index 000000000000..914610f0556e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/ad7303.txt @@ -0,0 +1,23 @@ +Analog Devices AD7303 DAC device driver + +Required properties: + - compatible: Must be "adi,ad7303" + - reg: SPI chip select number for the device + - spi-max-frequency: Max SPI frequency to use (< 30000000) + - Vdd-supply: Phandle to the Vdd power supply + +Optional properties: + - REF-supply: Phandle to the external reference voltage supply. This should + only be set if there is an external reference voltage connected to the REF + pin. If the property is not set Vdd/2 is used as the reference voltage. + +Example: + + ad7303@4 { + compatible = "adi,ad7303"; + reg = <4>; + spi-max-frequency = <10000000>; + Vdd-supply = <&vdd_supply>; + adi,use-external-reference; + REF-supply = <&vref_supply>; + }; diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index b61160bd935e..c9c33ce32d3a 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -130,6 +130,16 @@ config AD5686 To compile this driver as a module, choose M here: the module will be called ad5686. +config AD7303 + tristate "Analog Devices Analog Devices AD7303 DAC driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD7303 Digital to Analog + Converters (DAC). + + To compile this driver as module choose M here: the module will be called + ad7303. + config MAX517 tristate "Maxim MAX517/518/519 DAC driver" depends on I2C diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 5b528ebb3343..c8d7ab6bff01 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_AD5755) += ad5755.o obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o +obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_MAX517) += max517.o obj-$(CONFIG_MCP4725) += mcp4725.o diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c new file mode 100644 index 000000000000..85aeef60dc5f --- /dev/null +++ b/drivers/iio/dac/ad7303.c @@ -0,0 +1,315 @@ +/* + * AD7303 Digital to analog converters driver + * + * Copyright 2013 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define AD7303_CFG_EXTERNAL_VREF BIT(15) +#define AD7303_CFG_POWER_DOWN(ch) BIT(11 + (ch)) +#define AD7303_CFG_ADDR_OFFSET 10 + +#define AD7303_CMD_UPDATE_DAC (0x3 << 8) + +/** + * struct ad7303_state - driver instance specific data + * @spi: the device for this driver instance + * @config: cached config register value + * @dac_cache: current DAC raw value (chip does not support readback) + * @data: spi transfer buffer + */ + +struct ad7303_state { + struct spi_device *spi; + uint16_t config; + uint8_t dac_cache[2]; + + struct regulator *vdd_reg; + struct regulator *vref_reg; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be16 data ____cacheline_aligned; +}; + +static int ad7303_write(struct ad7303_state *st, unsigned int chan, + uint8_t val) +{ + st->data = cpu_to_be16(AD7303_CMD_UPDATE_DAC | + (chan << AD7303_CFG_ADDR_OFFSET) | + st->config | val); + + return spi_write(st->spi, &st->data, sizeof(st->data)); +} + +static ssize_t ad7303_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad7303_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", (bool)(st->config & + AD7303_CFG_POWER_DOWN(chan->channel))); +} + +static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + struct ad7303_state *st = iio_priv(indio_dev); + bool pwr_down; + int ret; + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + if (pwr_down) + st->config |= AD7303_CFG_POWER_DOWN(chan->channel); + else + st->config &= ~AD7303_CFG_POWER_DOWN(chan->channel); + + /* There is no noop cmd which allows us to only update the powerdown + * mode, so just write one of the DAC channels again */ + ad7303_write(st, chan->channel, st->dac_cache[chan->channel]); + + mutex_unlock(&indio_dev->mlock); + return ret ? ret : len; +} + +static int ad7303_get_vref(struct ad7303_state *st, + struct iio_chan_spec const *chan) +{ + int ret; + + if (st->config & AD7303_CFG_EXTERNAL_VREF) + return regulator_get_voltage(st->vref_reg); + + ret = regulator_get_voltage(st->vdd_reg); + if (ret < 0) + return ret; + return ret / 2; +} + +static int ad7303_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ + struct ad7303_state *st = iio_priv(indio_dev); + int vref_uv; + + switch (info) { + case IIO_CHAN_INFO_RAW: + *val = st->dac_cache[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + vref_uv = ad7303_get_vref(st, chan); + if (vref_uv < 0) + return vref_uv; + + *val = 2 * vref_uv / 1000; + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + break; + } + return -EINVAL; +} + +static int ad7303_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct ad7303_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + ret = ad7303_write(st, chan->address, val); + if (ret == 0) + st->dac_cache[chan->channel] = val; + mutex_unlock(&indio_dev->mlock); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info ad7303_info = { + .read_raw = ad7303_read_raw, + .write_raw = ad7303_write_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ad7303_ext_info[] = { + { + .name = "powerdown", + .read = ad7303_read_dac_powerdown, + .write = ad7303_write_dac_powerdown, + }, + { }, +}; + +#define AD7303_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = (chan), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = '8', \ + .storagebits = '8', \ + .shift = '0', \ + }, \ + .ext_info = ad7303_ext_info, \ +} + +static const struct iio_chan_spec ad7303_channels[] = { + AD7303_CHANNEL(0), + AD7303_CHANNEL(1), +}; + +static int ad7303_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct ad7303_state *st; + bool ext_ref; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + st->vdd_reg = regulator_get(&spi->dev, "Vdd"); + if (IS_ERR(st->vdd_reg)) { + ret = PTR_ERR(st->vdd_reg); + goto err_free; + } + + ret = regulator_enable(st->vdd_reg); + if (ret) + goto err_put_vdd_reg; + + if (spi->dev.of_node) { + ext_ref = of_property_read_bool(spi->dev.of_node, + "REF-supply"); + } else { + struct ad7303_platform_data *pdata = spi->dev.platform_data; + if (pdata && pdata->use_external_ref) + ext_ref = true; + else + ext_ref = false; + } + + if (ext_ref) { + st->vref_reg = regulator_get(&spi->dev, "REF"); + if (IS_ERR(st->vref_reg)) + goto err_disable_vdd_reg; + + ret = regulator_enable(st->vref_reg); + if (ret) + goto err_put_vref_reg; + + st->config |= AD7303_CFG_EXTERNAL_VREF; + } + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = id->name; + indio_dev->info = &ad7303_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7303_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7303_channels); + + ret = iio_device_register(indio_dev); + if (ret) + goto err_disable_vref_reg; + + return 0; + +err_disable_vref_reg: + if (st->vref_reg) + regulator_disable(st->vref_reg); +err_put_vref_reg: + if (st->vref_reg) + regulator_put(st->vref_reg); +err_disable_vdd_reg: + regulator_disable(st->vdd_reg); +err_put_vdd_reg: + regulator_put(st->vdd_reg); +err_free: + iio_device_free(indio_dev); + + return ret; +} + +static int ad7303_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7303_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (st->vref_reg) { + regulator_disable(st->vref_reg); + regulator_put(st->vref_reg); + } + regulator_disable(st->vdd_reg); + regulator_put(st->vdd_reg); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7303_spi_ids[] = { + { "ad7303", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7303_spi_ids); + +static struct spi_driver ad7303_driver = { + .driver = { + .name = "ad7303", + .owner = THIS_MODULE, + }, + .probe = ad7303_probe, + .remove = ad7303_remove, + .id_table = ad7303_spi_ids, +}; +module_spi_driver(ad7303_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/ad7303.h b/include/linux/platform_data/ad7303.h new file mode 100644 index 000000000000..de6a7a6b4bbf --- /dev/null +++ b/include/linux/platform_data/ad7303.h @@ -0,0 +1,21 @@ +/* + * Analog Devices AD7303 DAC driver + * + * Copyright 2013 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef __IIO_ADC_AD7303_H__ +#define __IIO_ADC_AD7303_H__ + +/** + * struct ad7303_platform_data - AD7303 platform data + * @use_external_ref: If set to true use an external voltage reference connected + * to the REF pin, otherwise use the internal reference derived from Vdd. + */ +struct ad7303_platform_data { + bool use_external_ref; +}; + +#endif -- cgit v1.3-18-gd494 From a3e509bb328287beba05017037e505bc53b62724 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 21 May 2013 18:49:58 +0200 Subject: input: mfd: ti_am335x_tsc remove remaining platform data pieces The two header files removed here are unused and have no users as this platform was never used with platform devices. Signed-off-by: Sebastian Andrzej Siewior --- include/linux/input/ti_am335x_tsc.h | 35 ----------------------------- include/linux/mfd/ti_am335x_tscadc.h | 5 ----- include/linux/platform_data/ti_am335x_adc.h | 14 ------------ 3 files changed, 54 deletions(-) delete mode 100644 include/linux/input/ti_am335x_tsc.h delete mode 100644 include/linux/platform_data/ti_am335x_adc.h (limited to 'include/linux/platform_data') diff --git a/include/linux/input/ti_am335x_tsc.h b/include/linux/input/ti_am335x_tsc.h deleted file mode 100644 index 6a66b4d1ac2c..000000000000 --- a/include/linux/input/ti_am335x_tsc.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef __LINUX_TI_AM335X_TSC_H -#define __LINUX_TI_AM335X_TSC_H - -/** - * struct tsc_data Touchscreen wire configuration - * @wires: Wires refer to application modes - * i.e. 4/5/8 wire touchscreen support - * on the platform. - * @x_plate_resistance: X plate resistance. - * @steps_to_configure: The sequencer supports a total of - * 16 programmable steps. - * A step configured to read a single - * co-ordinate value, can be applied - * more number of times for better results. - * @wire_config: Different EVM's could have a different order - * for connecting wires on touchscreen. - * We need to provide an 8 bit number where in - * the 1st four bits represent the analog lines - * and the next 4 bits represent positive/ - * negative terminal on that input line. - * Notations to represent the input lines and - * terminals resoectively is as follows: - * AIN0 = 0, AIN1 = 1 and so on till AIN7 = 7. - * XP = 0, XN = 1, YP = 2, YN = 3. - * - */ - -struct tsc_data { - int wires; - int x_plate_resistance; - int steps_to_configure; - int wire_config[10]; -}; - -#endif diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index fe54ba4a3b2f..533f200e6d0e 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -120,11 +120,6 @@ #define TSCADC_CELLS 2 -struct mfd_tscadc_board { - struct tsc_data *tsc_init; - struct adc_data *adc_init; -}; - struct ti_tscadc_dev { struct device *dev; struct regmap *regmap_tscadc; diff --git a/include/linux/platform_data/ti_am335x_adc.h b/include/linux/platform_data/ti_am335x_adc.h deleted file mode 100644 index e41d5834cb84..000000000000 --- a/include/linux/platform_data/ti_am335x_adc.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __LINUX_TI_AM335X_ADC_H -#define __LINUX_TI_AM335X_ADC_H - -/** - * struct adc_data ADC Input information - * @adc_channels: Number of analog inputs - * available for ADC. - */ - -struct adc_data { - unsigned int adc_channels; -}; - -#endif -- cgit v1.3-18-gd494 From b263e9b88746c6902c36d35a2dd83c119f8510a2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 23 May 2013 20:09:43 +0200 Subject: pinctrl: get rid of all platform data for coh901 This deletes the dependency on any platform data for the COH901 pin controller. There is only one user in the kernel, and if we at some point want to support more variants, they shall provide their variant info through the device tree. Signed-off-by: Linus Walleij --- arch/arm/mach-u300/core.c | 12 +----------- drivers/pinctrl/pinctrl-coh901.c | 23 +++++++++-------------- include/linux/platform_data/pinctrl-coh901.h | 22 ---------------------- 3 files changed, 10 insertions(+), 47 deletions(-) delete mode 100644 include/linux/platform_data/pinctrl-coh901.h (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 6e5744a2b85c..4f7ac2a11452 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -194,15 +193,6 @@ static void __init u300_map_io(void) iotable_init(u300_io_desc, ARRAY_SIZE(u300_io_desc)); } -/* - * The different variants have a few different versions of the - * GPIO block, with different number of ports. - */ -static struct u300_gpio_platform u300_gpio_plat = { - .ports = 7, - .gpio_base = 0, -}; - static unsigned long pin_pullup_conf[] = { PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_UP, 1), }; @@ -332,7 +322,7 @@ static struct of_dev_auxdata u300_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("stericsson,pinctrl-u300", U300_SYSCON_BASE, "pinctrl-u300", NULL), OF_DEV_AUXDATA("stericsson,gpio-coh901", U300_GPIO_BASE, - "u300-gpio", &u300_gpio_plat), + "u300-gpio", NULL), OF_DEV_AUXDATA("stericsson,coh901327", U300_WDOG_BASE, "coh901327_wdog", NULL), OF_DEV_AUXDATA("stericsson,coh901331", U300_RTC_BASE, diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index d4f8afdad1ad..46ff140cfa76 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -22,7 +22,6 @@ #include #include #include -#include #include "pinctrl-coh901.h" #define U300_GPIO_PORT_STRIDE (0x30) @@ -58,8 +57,9 @@ #define U300_GPIO_PXICR_IRQ_CONFIG_RISING_EDGE (0x00000001UL) /* 8 bits per port, no version has more than 7 ports */ +#define U300_GPIO_NUM_PORTS 7 #define U300_GPIO_PINS_PER_PORT 8 -#define U300_GPIO_MAX (U300_GPIO_PINS_PER_PORT * 7) +#define U300_GPIO_MAX (U300_GPIO_PINS_PER_PORT * U300_GPIO_NUM_PORTS) struct u300_gpio { struct gpio_chip chip; @@ -111,9 +111,6 @@ struct u300_gpio_confdata { int outval; }; -/* BS335 has seven ports of 8 bits each = GPIO pins 0..55 */ -#define BS335_GPIO_NUM_PORTS 7 - #define U300_FLOATING_INPUT { \ .bias_mode = PIN_CONFIG_BIAS_HIGH_IMPEDANCE, \ .output = false, \ @@ -136,7 +133,7 @@ struct u300_gpio_confdata { /* Initial configuration */ static const struct __initconst u300_gpio_confdata -bs335_gpio_config[BS335_GPIO_NUM_PORTS][U300_GPIO_PINS_PER_PORT] = { +bs335_gpio_config[U300_GPIO_NUM_PORTS][U300_GPIO_PINS_PER_PORT] = { /* Port 0, pins 0-7 */ { U300_FLOATING_INPUT, @@ -630,13 +627,12 @@ static void __init u300_gpio_init_pin(struct u300_gpio *gpio, } } -static void __init u300_gpio_init_coh901571(struct u300_gpio *gpio, - struct u300_gpio_platform *plat) +static void __init u300_gpio_init_coh901571(struct u300_gpio *gpio) { int i, j; /* Write default config and values to all pins */ - for (i = 0; i < plat->ports; i++) { + for (i = 0; i < U300_GPIO_NUM_PORTS; i++) { for (j = 0; j < 8; j++) { const struct u300_gpio_confdata *conf; int offset = (i*8) + j; @@ -693,7 +689,6 @@ static struct coh901_pinpair coh901_pintable[] = { static int __init u300_gpio_probe(struct platform_device *pdev) { - struct u300_gpio_platform *plat = dev_get_platdata(&pdev->dev); struct u300_gpio *gpio; struct resource *memres; int err = 0; @@ -707,9 +702,9 @@ static int __init u300_gpio_probe(struct platform_device *pdev) return -ENOMEM; gpio->chip = u300_gpio_chip; - gpio->chip.ngpio = plat->ports * U300_GPIO_PINS_PER_PORT; + gpio->chip.ngpio = U300_GPIO_NUM_PORTS * U300_GPIO_PINS_PER_PORT; gpio->chip.dev = &pdev->dev; - gpio->chip.base = plat->gpio_base; + gpio->chip.base = 0; gpio->dev = &pdev->dev; memres = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -755,11 +750,11 @@ static int __init u300_gpio_probe(struct platform_device *pdev) ((val & 0x0000FE00) >> 9) * 8); writel(U300_GPIO_CR_BLOCK_CLKRQ_ENABLE, gpio->base + U300_GPIO_CR); - u300_gpio_init_coh901571(gpio, plat); + u300_gpio_init_coh901571(gpio); /* Add each port with its IRQ separately */ INIT_LIST_HEAD(&gpio->port_list); - for (portno = 0 ; portno < plat->ports; portno++) { + for (portno = 0 ; portno < U300_GPIO_NUM_PORTS; portno++) { struct u300_gpio_port *port = kmalloc(sizeof(struct u300_gpio_port), GFP_KERNEL); diff --git a/include/linux/platform_data/pinctrl-coh901.h b/include/linux/platform_data/pinctrl-coh901.h deleted file mode 100644 index dfbc65d10484..000000000000 --- a/include/linux/platform_data/pinctrl-coh901.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2007-2012 ST-Ericsson AB - * License terms: GNU General Public License (GPL) version 2 - * GPIO block resgister definitions and inline macros for - * U300 GPIO COH 901 335 or COH 901 571/3 - * Author: Linus Walleij - */ - -#ifndef __MACH_U300_GPIO_U300_H -#define __MACH_U300_GPIO_U300_H - -/** - * struct u300_gpio_platform - U300 GPIO platform data - * @ports: number of GPIO block ports - * @gpio_base: first GPIO number for this block (use a free range) - */ -struct u300_gpio_platform { - u8 ports; - int gpio_base; -}; - -#endif /* __MACH_U300_GPIO_U300_H */ -- cgit v1.3-18-gd494 From 3ad7a42d5a9c3736cd6d2c6f7e6038d0ca8b316c Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Wed, 6 Mar 2013 11:15:31 -0500 Subject: ARM: davinci: move private EDMA API to arm/common Move mach-davinci/dma.c to common/edma.c so it can be used by OMAP (specifically AM33xx) as well. Signed-off-by: Matt Porter Acked-by: Chris Ball # davinci_mmc.c Acked-by: Mark Brown Acked-by: Olof Johansson [nsekhar@ti.com: dropped davinci sffsdr changes] Signed-off-by: Sekhar Nori --- arch/arm/Kconfig | 1 + arch/arm/common/Kconfig | 3 + arch/arm/common/Makefile | 1 + arch/arm/common/edma.c | 1591 +++++++++++++++++++++++++++ arch/arm/mach-davinci/Makefile | 2 +- arch/arm/mach-davinci/board-tnetv107x-evm.c | 2 +- arch/arm/mach-davinci/davinci.h | 2 +- arch/arm/mach-davinci/devices-tnetv107x.c | 2 +- arch/arm/mach-davinci/devices.c | 6 +- arch/arm/mach-davinci/dm355.c | 2 +- arch/arm/mach-davinci/dm365.c | 2 +- arch/arm/mach-davinci/dm644x.c | 2 +- arch/arm/mach-davinci/dm646x.c | 2 +- arch/arm/mach-davinci/dma.c | 1591 --------------------------- arch/arm/mach-davinci/include/mach/da8xx.h | 2 +- arch/arm/mach-davinci/include/mach/edma.h | 267 ----- drivers/dma/edma.c | 2 +- drivers/mmc/host/davinci_mmc.c | 1 + include/linux/mfd/davinci_voicecodec.h | 3 +- include/linux/platform_data/edma.h | 182 +++ include/linux/platform_data/spi-davinci.h | 2 +- sound/soc/davinci/davinci-evm.c | 1 + sound/soc/davinci/davinci-pcm.c | 1 + sound/soc/davinci/davinci-pcm.h | 2 +- 24 files changed, 1799 insertions(+), 1873 deletions(-) create mode 100644 arch/arm/common/edma.c delete mode 100644 arch/arm/mach-davinci/dma.c delete mode 100644 arch/arm/mach-davinci/include/mach/edma.h create mode 100644 include/linux/platform_data/edma.h (limited to 'include/linux/platform_data') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 49d993cee512..b1c66a48a454 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -840,6 +840,7 @@ config ARCH_DAVINCI select GENERIC_IRQ_CHIP select HAVE_IDE select NEED_MACH_GPIO_H + select TI_PRIV_EDMA select USE_OF select ZONE_DMA help diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index 9353184d730d..c3a4e9ceba34 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -17,3 +17,6 @@ config SHARP_PARAM config SHARP_SCOOP bool + +config TI_PRIV_EDMA + bool diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 48434cbe3e89..8c60f473e976 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o obj-$(CONFIG_MCPM) += mcpm_head.o mcpm_entry.o mcpm_platsmp.o vlock.o AFLAGS_mcpm_head.o := -march=armv7-a AFLAGS_vlock.o := -march=armv7-a +obj-$(CONFIG_TI_PRIV_EDMA) += edma.o diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c new file mode 100644 index 000000000000..dcaeb8ec5d38 --- /dev/null +++ b/arch/arm/common/edma.c @@ -0,0 +1,1591 @@ +/* + * EDMA3 support for DaVinci + * + * Copyright (C) 2006-2009 Texas Instruments. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Offsets matching "struct edmacc_param" */ +#define PARM_OPT 0x00 +#define PARM_SRC 0x04 +#define PARM_A_B_CNT 0x08 +#define PARM_DST 0x0c +#define PARM_SRC_DST_BIDX 0x10 +#define PARM_LINK_BCNTRLD 0x14 +#define PARM_SRC_DST_CIDX 0x18 +#define PARM_CCNT 0x1c + +#define PARM_SIZE 0x20 + +/* Offsets for EDMA CC global channel registers and their shadows */ +#define SH_ER 0x00 /* 64 bits */ +#define SH_ECR 0x08 /* 64 bits */ +#define SH_ESR 0x10 /* 64 bits */ +#define SH_CER 0x18 /* 64 bits */ +#define SH_EER 0x20 /* 64 bits */ +#define SH_EECR 0x28 /* 64 bits */ +#define SH_EESR 0x30 /* 64 bits */ +#define SH_SER 0x38 /* 64 bits */ +#define SH_SECR 0x40 /* 64 bits */ +#define SH_IER 0x50 /* 64 bits */ +#define SH_IECR 0x58 /* 64 bits */ +#define SH_IESR 0x60 /* 64 bits */ +#define SH_IPR 0x68 /* 64 bits */ +#define SH_ICR 0x70 /* 64 bits */ +#define SH_IEVAL 0x78 +#define SH_QER 0x80 +#define SH_QEER 0x84 +#define SH_QEECR 0x88 +#define SH_QEESR 0x8c +#define SH_QSER 0x90 +#define SH_QSECR 0x94 +#define SH_SIZE 0x200 + +/* Offsets for EDMA CC global registers */ +#define EDMA_REV 0x0000 +#define EDMA_CCCFG 0x0004 +#define EDMA_QCHMAP 0x0200 /* 8 registers */ +#define EDMA_DMAQNUM 0x0240 /* 8 registers (4 on OMAP-L1xx) */ +#define EDMA_QDMAQNUM 0x0260 +#define EDMA_QUETCMAP 0x0280 +#define EDMA_QUEPRI 0x0284 +#define EDMA_EMR 0x0300 /* 64 bits */ +#define EDMA_EMCR 0x0308 /* 64 bits */ +#define EDMA_QEMR 0x0310 +#define EDMA_QEMCR 0x0314 +#define EDMA_CCERR 0x0318 +#define EDMA_CCERRCLR 0x031c +#define EDMA_EEVAL 0x0320 +#define EDMA_DRAE 0x0340 /* 4 x 64 bits*/ +#define EDMA_QRAE 0x0380 /* 4 registers */ +#define EDMA_QUEEVTENTRY 0x0400 /* 2 x 16 registers */ +#define EDMA_QSTAT 0x0600 /* 2 registers */ +#define EDMA_QWMTHRA 0x0620 +#define EDMA_QWMTHRB 0x0624 +#define EDMA_CCSTAT 0x0640 + +#define EDMA_M 0x1000 /* global channel registers */ +#define EDMA_ECR 0x1008 +#define EDMA_ECRH 0x100C +#define EDMA_SHADOW0 0x2000 /* 4 regions shadowing global channels */ +#define EDMA_PARM 0x4000 /* 128 param entries */ + +#define PARM_OFFSET(param_no) (EDMA_PARM + ((param_no) << 5)) + +#define EDMA_DCHMAP 0x0100 /* 64 registers */ +#define CHMAP_EXIST BIT(24) + +#define EDMA_MAX_DMACH 64 +#define EDMA_MAX_PARAMENTRY 512 + +/*****************************************************************************/ + +static void __iomem *edmacc_regs_base[EDMA_MAX_CC]; + +static inline unsigned int edma_read(unsigned ctlr, int offset) +{ + return (unsigned int)__raw_readl(edmacc_regs_base[ctlr] + offset); +} + +static inline void edma_write(unsigned ctlr, int offset, int val) +{ + __raw_writel(val, edmacc_regs_base[ctlr] + offset); +} +static inline void edma_modify(unsigned ctlr, int offset, unsigned and, + unsigned or) +{ + unsigned val = edma_read(ctlr, offset); + val &= and; + val |= or; + edma_write(ctlr, offset, val); +} +static inline void edma_and(unsigned ctlr, int offset, unsigned and) +{ + unsigned val = edma_read(ctlr, offset); + val &= and; + edma_write(ctlr, offset, val); +} +static inline void edma_or(unsigned ctlr, int offset, unsigned or) +{ + unsigned val = edma_read(ctlr, offset); + val |= or; + edma_write(ctlr, offset, val); +} +static inline unsigned int edma_read_array(unsigned ctlr, int offset, int i) +{ + return edma_read(ctlr, offset + (i << 2)); +} +static inline void edma_write_array(unsigned ctlr, int offset, int i, + unsigned val) +{ + edma_write(ctlr, offset + (i << 2), val); +} +static inline void edma_modify_array(unsigned ctlr, int offset, int i, + unsigned and, unsigned or) +{ + edma_modify(ctlr, offset + (i << 2), and, or); +} +static inline void edma_or_array(unsigned ctlr, int offset, int i, unsigned or) +{ + edma_or(ctlr, offset + (i << 2), or); +} +static inline void edma_or_array2(unsigned ctlr, int offset, int i, int j, + unsigned or) +{ + edma_or(ctlr, offset + ((i*2 + j) << 2), or); +} +static inline void edma_write_array2(unsigned ctlr, int offset, int i, int j, + unsigned val) +{ + edma_write(ctlr, offset + ((i*2 + j) << 2), val); +} +static inline unsigned int edma_shadow0_read(unsigned ctlr, int offset) +{ + return edma_read(ctlr, EDMA_SHADOW0 + offset); +} +static inline unsigned int edma_shadow0_read_array(unsigned ctlr, int offset, + int i) +{ + return edma_read(ctlr, EDMA_SHADOW0 + offset + (i << 2)); +} +static inline void edma_shadow0_write(unsigned ctlr, int offset, unsigned val) +{ + edma_write(ctlr, EDMA_SHADOW0 + offset, val); +} +static inline void edma_shadow0_write_array(unsigned ctlr, int offset, int i, + unsigned val) +{ + edma_write(ctlr, EDMA_SHADOW0 + offset + (i << 2), val); +} +static inline unsigned int edma_parm_read(unsigned ctlr, int offset, + int param_no) +{ + return edma_read(ctlr, EDMA_PARM + offset + (param_no << 5)); +} +static inline void edma_parm_write(unsigned ctlr, int offset, int param_no, + unsigned val) +{ + edma_write(ctlr, EDMA_PARM + offset + (param_no << 5), val); +} +static inline void edma_parm_modify(unsigned ctlr, int offset, int param_no, + unsigned and, unsigned or) +{ + edma_modify(ctlr, EDMA_PARM + offset + (param_no << 5), and, or); +} +static inline void edma_parm_and(unsigned ctlr, int offset, int param_no, + unsigned and) +{ + edma_and(ctlr, EDMA_PARM + offset + (param_no << 5), and); +} +static inline void edma_parm_or(unsigned ctlr, int offset, int param_no, + unsigned or) +{ + edma_or(ctlr, EDMA_PARM + offset + (param_no << 5), or); +} + +static inline void set_bits(int offset, int len, unsigned long *p) +{ + for (; len > 0; len--) + set_bit(offset + (len - 1), p); +} + +static inline void clear_bits(int offset, int len, unsigned long *p) +{ + for (; len > 0; len--) + clear_bit(offset + (len - 1), p); +} + +/*****************************************************************************/ + +/* actual number of DMA channels and slots on this silicon */ +struct edma { + /* how many dma resources of each type */ + unsigned num_channels; + unsigned num_region; + unsigned num_slots; + unsigned num_tc; + unsigned num_cc; + enum dma_event_q default_queue; + + /* list of channels with no even trigger; terminated by "-1" */ + const s8 *noevent; + + /* The edma_inuse bit for each PaRAM slot is clear unless the + * channel is in use ... by ARM or DSP, for QDMA, or whatever. + */ + DECLARE_BITMAP(edma_inuse, EDMA_MAX_PARAMENTRY); + + /* The edma_unused bit for each channel is clear unless + * it is not being used on this platform. It uses a bit + * of SOC-specific initialization code. + */ + DECLARE_BITMAP(edma_unused, EDMA_MAX_DMACH); + + unsigned irq_res_start; + unsigned irq_res_end; + + struct dma_interrupt_data { + void (*callback)(unsigned channel, unsigned short ch_status, + void *data); + void *data; + } intr_data[EDMA_MAX_DMACH]; +}; + +static struct edma *edma_cc[EDMA_MAX_CC]; +static int arch_num_cc; + +/* dummy param set used to (re)initialize parameter RAM slots */ +static const struct edmacc_param dummy_paramset = { + .link_bcntrld = 0xffff, + .ccnt = 1, +}; + +/*****************************************************************************/ + +static void map_dmach_queue(unsigned ctlr, unsigned ch_no, + enum dma_event_q queue_no) +{ + int bit = (ch_no & 0x7) * 4; + + /* default to low priority queue */ + if (queue_no == EVENTQ_DEFAULT) + queue_no = edma_cc[ctlr]->default_queue; + + queue_no &= 7; + edma_modify_array(ctlr, EDMA_DMAQNUM, (ch_no >> 3), + ~(0x7 << bit), queue_no << bit); +} + +static void __init map_queue_tc(unsigned ctlr, int queue_no, int tc_no) +{ + int bit = queue_no * 4; + edma_modify(ctlr, EDMA_QUETCMAP, ~(0x7 << bit), ((tc_no & 0x7) << bit)); +} + +static void __init assign_priority_to_queue(unsigned ctlr, int queue_no, + int priority) +{ + int bit = queue_no * 4; + edma_modify(ctlr, EDMA_QUEPRI, ~(0x7 << bit), + ((priority & 0x7) << bit)); +} + +/** + * map_dmach_param - Maps channel number to param entry number + * + * This maps the dma channel number to param entry numberter. In + * other words using the DMA channel mapping registers a param entry + * can be mapped to any channel + * + * Callers are responsible for ensuring the channel mapping logic is + * included in that particular EDMA variant (Eg : dm646x) + * + */ +static void __init map_dmach_param(unsigned ctlr) +{ + int i; + for (i = 0; i < EDMA_MAX_DMACH; i++) + edma_write_array(ctlr, EDMA_DCHMAP , i , (i << 5)); +} + +static inline void +setup_dma_interrupt(unsigned lch, + void (*callback)(unsigned channel, u16 ch_status, void *data), + void *data) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(lch); + lch = EDMA_CHAN_SLOT(lch); + + if (!callback) + edma_shadow0_write_array(ctlr, SH_IECR, lch >> 5, + BIT(lch & 0x1f)); + + edma_cc[ctlr]->intr_data[lch].callback = callback; + edma_cc[ctlr]->intr_data[lch].data = data; + + if (callback) { + edma_shadow0_write_array(ctlr, SH_ICR, lch >> 5, + BIT(lch & 0x1f)); + edma_shadow0_write_array(ctlr, SH_IESR, lch >> 5, + BIT(lch & 0x1f)); + } +} + +static int irq2ctlr(int irq) +{ + if (irq >= edma_cc[0]->irq_res_start && irq <= edma_cc[0]->irq_res_end) + return 0; + else if (irq >= edma_cc[1]->irq_res_start && + irq <= edma_cc[1]->irq_res_end) + return 1; + + return -1; +} + +/****************************************************************************** + * + * DMA interrupt handler + * + *****************************************************************************/ +static irqreturn_t dma_irq_handler(int irq, void *data) +{ + int ctlr; + u32 sh_ier; + u32 sh_ipr; + u32 bank; + + ctlr = irq2ctlr(irq); + if (ctlr < 0) + return IRQ_NONE; + + dev_dbg(data, "dma_irq_handler\n"); + + sh_ipr = edma_shadow0_read_array(ctlr, SH_IPR, 0); + if (!sh_ipr) { + sh_ipr = edma_shadow0_read_array(ctlr, SH_IPR, 1); + if (!sh_ipr) + return IRQ_NONE; + sh_ier = edma_shadow0_read_array(ctlr, SH_IER, 1); + bank = 1; + } else { + sh_ier = edma_shadow0_read_array(ctlr, SH_IER, 0); + bank = 0; + } + + do { + u32 slot; + u32 channel; + + dev_dbg(data, "IPR%d %08x\n", bank, sh_ipr); + + slot = __ffs(sh_ipr); + sh_ipr &= ~(BIT(slot)); + + if (sh_ier & BIT(slot)) { + channel = (bank << 5) | slot; + /* Clear the corresponding IPR bits */ + edma_shadow0_write_array(ctlr, SH_ICR, bank, + BIT(slot)); + if (edma_cc[ctlr]->intr_data[channel].callback) + edma_cc[ctlr]->intr_data[channel].callback( + channel, DMA_COMPLETE, + edma_cc[ctlr]->intr_data[channel].data); + } + } while (sh_ipr); + + edma_shadow0_write(ctlr, SH_IEVAL, 1); + return IRQ_HANDLED; +} + +/****************************************************************************** + * + * DMA error interrupt handler + * + *****************************************************************************/ +static irqreturn_t dma_ccerr_handler(int irq, void *data) +{ + int i; + int ctlr; + unsigned int cnt = 0; + + ctlr = irq2ctlr(irq); + if (ctlr < 0) + return IRQ_NONE; + + dev_dbg(data, "dma_ccerr_handler\n"); + + if ((edma_read_array(ctlr, EDMA_EMR, 0) == 0) && + (edma_read_array(ctlr, EDMA_EMR, 1) == 0) && + (edma_read(ctlr, EDMA_QEMR) == 0) && + (edma_read(ctlr, EDMA_CCERR) == 0)) + return IRQ_NONE; + + while (1) { + int j = -1; + if (edma_read_array(ctlr, EDMA_EMR, 0)) + j = 0; + else if (edma_read_array(ctlr, EDMA_EMR, 1)) + j = 1; + if (j >= 0) { + dev_dbg(data, "EMR%d %08x\n", j, + edma_read_array(ctlr, EDMA_EMR, j)); + for (i = 0; i < 32; i++) { + int k = (j << 5) + i; + if (edma_read_array(ctlr, EDMA_EMR, j) & + BIT(i)) { + /* Clear the corresponding EMR bits */ + edma_write_array(ctlr, EDMA_EMCR, j, + BIT(i)); + /* Clear any SER */ + edma_shadow0_write_array(ctlr, SH_SECR, + j, BIT(i)); + if (edma_cc[ctlr]->intr_data[k]. + callback) { + edma_cc[ctlr]->intr_data[k]. + callback(k, + DMA_CC_ERROR, + edma_cc[ctlr]->intr_data + [k].data); + } + } + } + } else if (edma_read(ctlr, EDMA_QEMR)) { + dev_dbg(data, "QEMR %02x\n", + edma_read(ctlr, EDMA_QEMR)); + for (i = 0; i < 8; i++) { + if (edma_read(ctlr, EDMA_QEMR) & BIT(i)) { + /* Clear the corresponding IPR bits */ + edma_write(ctlr, EDMA_QEMCR, BIT(i)); + edma_shadow0_write(ctlr, SH_QSECR, + BIT(i)); + + /* NOTE: not reported!! */ + } + } + } else if (edma_read(ctlr, EDMA_CCERR)) { + dev_dbg(data, "CCERR %08x\n", + edma_read(ctlr, EDMA_CCERR)); + /* FIXME: CCERR.BIT(16) ignored! much better + * to just write CCERRCLR with CCERR value... + */ + for (i = 0; i < 8; i++) { + if (edma_read(ctlr, EDMA_CCERR) & BIT(i)) { + /* Clear the corresponding IPR bits */ + edma_write(ctlr, EDMA_CCERRCLR, BIT(i)); + + /* NOTE: not reported!! */ + } + } + } + if ((edma_read_array(ctlr, EDMA_EMR, 0) == 0) && + (edma_read_array(ctlr, EDMA_EMR, 1) == 0) && + (edma_read(ctlr, EDMA_QEMR) == 0) && + (edma_read(ctlr, EDMA_CCERR) == 0)) + break; + cnt++; + if (cnt > 10) + break; + } + edma_write(ctlr, EDMA_EEVAL, 1); + return IRQ_HANDLED; +} + +/****************************************************************************** + * + * Transfer controller error interrupt handlers + * + *****************************************************************************/ + +#define tc_errs_handled false /* disabled as long as they're NOPs */ + +static irqreturn_t dma_tc0err_handler(int irq, void *data) +{ + dev_dbg(data, "dma_tc0err_handler\n"); + return IRQ_HANDLED; +} + +static irqreturn_t dma_tc1err_handler(int irq, void *data) +{ + dev_dbg(data, "dma_tc1err_handler\n"); + return IRQ_HANDLED; +} + +static int reserve_contiguous_slots(int ctlr, unsigned int id, + unsigned int num_slots, + unsigned int start_slot) +{ + int i, j; + unsigned int count = num_slots; + int stop_slot = start_slot; + DECLARE_BITMAP(tmp_inuse, EDMA_MAX_PARAMENTRY); + + for (i = start_slot; i < edma_cc[ctlr]->num_slots; ++i) { + j = EDMA_CHAN_SLOT(i); + if (!test_and_set_bit(j, edma_cc[ctlr]->edma_inuse)) { + /* Record our current beginning slot */ + if (count == num_slots) + stop_slot = i; + + count--; + set_bit(j, tmp_inuse); + + if (count == 0) + break; + } else { + clear_bit(j, tmp_inuse); + + if (id == EDMA_CONT_PARAMS_FIXED_EXACT) { + stop_slot = i; + break; + } else { + count = num_slots; + } + } + } + + /* + * We have to clear any bits that we set + * if we run out parameter RAM slots, i.e we do find a set + * of contiguous parameter RAM slots but do not find the exact number + * requested as we may reach the total number of parameter RAM slots + */ + if (i == edma_cc[ctlr]->num_slots) + stop_slot = i; + + j = start_slot; + for_each_set_bit_from(j, tmp_inuse, stop_slot) + clear_bit(j, edma_cc[ctlr]->edma_inuse); + + if (count) + return -EBUSY; + + for (j = i - num_slots + 1; j <= i; ++j) + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(j), + &dummy_paramset, PARM_SIZE); + + return EDMA_CTLR_CHAN(ctlr, i - num_slots + 1); +} + +static int prepare_unused_channel_list(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + int i, ctlr; + + for (i = 0; i < pdev->num_resources; i++) { + if ((pdev->resource[i].flags & IORESOURCE_DMA) && + (int)pdev->resource[i].start >= 0) { + ctlr = EDMA_CTLR(pdev->resource[i].start); + clear_bit(EDMA_CHAN_SLOT(pdev->resource[i].start), + edma_cc[ctlr]->edma_unused); + } + } + + return 0; +} + +/*-----------------------------------------------------------------------*/ + +static bool unused_chan_list_done; + +/* Resource alloc/free: dma channels, parameter RAM slots */ + +/** + * edma_alloc_channel - allocate DMA channel and paired parameter RAM + * @channel: specific channel to allocate; negative for "any unmapped channel" + * @callback: optional; to be issued on DMA completion or errors + * @data: passed to callback + * @eventq_no: an EVENTQ_* constant, used to choose which Transfer + * Controller (TC) executes requests using this channel. Use + * EVENTQ_DEFAULT unless you really need a high priority queue. + * + * This allocates a DMA channel and its associated parameter RAM slot. + * The parameter RAM is initialized to hold a dummy transfer. + * + * Normal use is to pass a specific channel number as @channel, to make + * use of hardware events mapped to that channel. When the channel will + * be used only for software triggering or event chaining, channels not + * mapped to hardware events (or mapped to unused events) are preferable. + * + * DMA transfers start from a channel using edma_start(), or by + * chaining. When the transfer described in that channel's parameter RAM + * slot completes, that slot's data may be reloaded through a link. + * + * DMA errors are only reported to the @callback associated with the + * channel driving that transfer, but transfer completion callbacks can + * be sent to another channel under control of the TCC field in + * the option word of the transfer's parameter RAM set. Drivers must not + * use DMA transfer completion callbacks for channels they did not allocate. + * (The same applies to TCC codes used in transfer chaining.) + * + * Returns the number of the channel, else negative errno. + */ +int edma_alloc_channel(int channel, + void (*callback)(unsigned channel, u16 ch_status, void *data), + void *data, + enum dma_event_q eventq_no) +{ + unsigned i, done = 0, ctlr = 0; + int ret = 0; + + if (!unused_chan_list_done) { + /* + * Scan all the platform devices to find out the EDMA channels + * used and clear them in the unused list, making the rest + * available for ARM usage. + */ + ret = bus_for_each_dev(&platform_bus_type, NULL, NULL, + prepare_unused_channel_list); + if (ret < 0) + return ret; + + unused_chan_list_done = true; + } + + if (channel >= 0) { + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + } + + if (channel < 0) { + for (i = 0; i < arch_num_cc; i++) { + channel = 0; + for (;;) { + channel = find_next_bit(edma_cc[i]->edma_unused, + edma_cc[i]->num_channels, + channel); + if (channel == edma_cc[i]->num_channels) + break; + if (!test_and_set_bit(channel, + edma_cc[i]->edma_inuse)) { + done = 1; + ctlr = i; + break; + } + channel++; + } + if (done) + break; + } + if (!done) + return -ENOMEM; + } else if (channel >= edma_cc[ctlr]->num_channels) { + return -EINVAL; + } else if (test_and_set_bit(channel, edma_cc[ctlr]->edma_inuse)) { + return -EBUSY; + } + + /* ensure access through shadow region 0 */ + edma_or_array2(ctlr, EDMA_DRAE, 0, channel >> 5, BIT(channel & 0x1f)); + + /* ensure no events are pending */ + edma_stop(EDMA_CTLR_CHAN(ctlr, channel)); + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(channel), + &dummy_paramset, PARM_SIZE); + + if (callback) + setup_dma_interrupt(EDMA_CTLR_CHAN(ctlr, channel), + callback, data); + + map_dmach_queue(ctlr, channel, eventq_no); + + return EDMA_CTLR_CHAN(ctlr, channel); +} +EXPORT_SYMBOL(edma_alloc_channel); + + +/** + * edma_free_channel - deallocate DMA channel + * @channel: dma channel returned from edma_alloc_channel() + * + * This deallocates the DMA channel and associated parameter RAM slot + * allocated by edma_alloc_channel(). + * + * Callers are responsible for ensuring the channel is inactive, and + * will not be reactivated by linking, chaining, or software calls to + * edma_start(). + */ +void edma_free_channel(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel >= edma_cc[ctlr]->num_channels) + return; + + setup_dma_interrupt(channel, NULL, NULL); + /* REVISIT should probably take out of shadow region 0 */ + + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(channel), + &dummy_paramset, PARM_SIZE); + clear_bit(channel, edma_cc[ctlr]->edma_inuse); +} +EXPORT_SYMBOL(edma_free_channel); + +/** + * edma_alloc_slot - allocate DMA parameter RAM + * @slot: specific slot to allocate; negative for "any unused slot" + * + * This allocates a parameter RAM slot, initializing it to hold a + * dummy transfer. Slots allocated using this routine have not been + * mapped to a hardware DMA channel, and will normally be used by + * linking to them from a slot associated with a DMA channel. + * + * Normal use is to pass EDMA_SLOT_ANY as the @slot, but specific + * slots may be allocated on behalf of DSP firmware. + * + * Returns the number of the slot, else negative errno. + */ +int edma_alloc_slot(unsigned ctlr, int slot) +{ + if (!edma_cc[ctlr]) + return -EINVAL; + + if (slot >= 0) + slot = EDMA_CHAN_SLOT(slot); + + if (slot < 0) { + slot = edma_cc[ctlr]->num_channels; + for (;;) { + slot = find_next_zero_bit(edma_cc[ctlr]->edma_inuse, + edma_cc[ctlr]->num_slots, slot); + if (slot == edma_cc[ctlr]->num_slots) + return -ENOMEM; + if (!test_and_set_bit(slot, edma_cc[ctlr]->edma_inuse)) + break; + } + } else if (slot < edma_cc[ctlr]->num_channels || + slot >= edma_cc[ctlr]->num_slots) { + return -EINVAL; + } else if (test_and_set_bit(slot, edma_cc[ctlr]->edma_inuse)) { + return -EBUSY; + } + + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot), + &dummy_paramset, PARM_SIZE); + + return EDMA_CTLR_CHAN(ctlr, slot); +} +EXPORT_SYMBOL(edma_alloc_slot); + +/** + * edma_free_slot - deallocate DMA parameter RAM + * @slot: parameter RAM slot returned from edma_alloc_slot() + * + * This deallocates the parameter RAM slot allocated by edma_alloc_slot(). + * Callers are responsible for ensuring the slot is inactive, and will + * not be activated. + */ +void edma_free_slot(unsigned slot) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_channels || + slot >= edma_cc[ctlr]->num_slots) + return; + + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot), + &dummy_paramset, PARM_SIZE); + clear_bit(slot, edma_cc[ctlr]->edma_inuse); +} +EXPORT_SYMBOL(edma_free_slot); + + +/** + * edma_alloc_cont_slots- alloc contiguous parameter RAM slots + * The API will return the starting point of a set of + * contiguous parameter RAM slots that have been requested + * + * @id: can only be EDMA_CONT_PARAMS_ANY or EDMA_CONT_PARAMS_FIXED_EXACT + * or EDMA_CONT_PARAMS_FIXED_NOT_EXACT + * @count: number of contiguous Paramter RAM slots + * @slot - the start value of Parameter RAM slot that should be passed if id + * is EDMA_CONT_PARAMS_FIXED_EXACT or EDMA_CONT_PARAMS_FIXED_NOT_EXACT + * + * If id is EDMA_CONT_PARAMS_ANY then the API starts looking for a set of + * contiguous Parameter RAM slots from parameter RAM 64 in the case of + * DaVinci SOCs and 32 in the case of DA8xx SOCs. + * + * If id is EDMA_CONT_PARAMS_FIXED_EXACT then the API starts looking for a + * set of contiguous parameter RAM slots from the "slot" that is passed as an + * argument to the API. + * + * If id is EDMA_CONT_PARAMS_FIXED_NOT_EXACT then the API initially tries + * starts looking for a set of contiguous parameter RAMs from the "slot" + * that is passed as an argument to the API. On failure the API will try to + * find a set of contiguous Parameter RAM slots from the remaining Parameter + * RAM slots + */ +int edma_alloc_cont_slots(unsigned ctlr, unsigned int id, int slot, int count) +{ + /* + * The start slot requested should be greater than + * the number of channels and lesser than the total number + * of slots + */ + if ((id != EDMA_CONT_PARAMS_ANY) && + (slot < edma_cc[ctlr]->num_channels || + slot >= edma_cc[ctlr]->num_slots)) + return -EINVAL; + + /* + * The number of parameter RAM slots requested cannot be less than 1 + * and cannot be more than the number of slots minus the number of + * channels + */ + if (count < 1 || count > + (edma_cc[ctlr]->num_slots - edma_cc[ctlr]->num_channels)) + return -EINVAL; + + switch (id) { + case EDMA_CONT_PARAMS_ANY: + return reserve_contiguous_slots(ctlr, id, count, + edma_cc[ctlr]->num_channels); + case EDMA_CONT_PARAMS_FIXED_EXACT: + case EDMA_CONT_PARAMS_FIXED_NOT_EXACT: + return reserve_contiguous_slots(ctlr, id, count, slot); + default: + return -EINVAL; + } + +} +EXPORT_SYMBOL(edma_alloc_cont_slots); + +/** + * edma_free_cont_slots - deallocate DMA parameter RAM slots + * @slot: first parameter RAM of a set of parameter RAM slots to be freed + * @count: the number of contiguous parameter RAM slots to be freed + * + * This deallocates the parameter RAM slots allocated by + * edma_alloc_cont_slots. + * Callers/applications need to keep track of sets of contiguous + * parameter RAM slots that have been allocated using the edma_alloc_cont_slots + * API. + * Callers are responsible for ensuring the slots are inactive, and will + * not be activated. + */ +int edma_free_cont_slots(unsigned slot, int count) +{ + unsigned ctlr, slot_to_free; + int i; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_channels || + slot >= edma_cc[ctlr]->num_slots || + count < 1) + return -EINVAL; + + for (i = slot; i < slot + count; ++i) { + ctlr = EDMA_CTLR(i); + slot_to_free = EDMA_CHAN_SLOT(i); + + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot_to_free), + &dummy_paramset, PARM_SIZE); + clear_bit(slot_to_free, edma_cc[ctlr]->edma_inuse); + } + + return 0; +} +EXPORT_SYMBOL(edma_free_cont_slots); + +/*-----------------------------------------------------------------------*/ + +/* Parameter RAM operations (i) -- read/write partial slots */ + +/** + * edma_set_src - set initial DMA source address in parameter RAM slot + * @slot: parameter RAM slot being configured + * @src_port: physical address of source (memory, controller FIFO, etc) + * @addressMode: INCR, except in very rare cases + * @fifoWidth: ignored unless @addressMode is FIFO, else specifies the + * width to use when addressing the fifo (e.g. W8BIT, W32BIT) + * + * Note that the source address is modified during the DMA transfer + * according to edma_set_src_index(). + */ +void edma_set_src(unsigned slot, dma_addr_t src_port, + enum address_mode mode, enum fifo_width width) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_slots) { + unsigned int i = edma_parm_read(ctlr, PARM_OPT, slot); + + if (mode) { + /* set SAM and program FWID */ + i = (i & ~(EDMA_FWID)) | (SAM | ((width & 0x7) << 8)); + } else { + /* clear SAM */ + i &= ~SAM; + } + edma_parm_write(ctlr, PARM_OPT, slot, i); + + /* set the source port address + in source register of param structure */ + edma_parm_write(ctlr, PARM_SRC, slot, src_port); + } +} +EXPORT_SYMBOL(edma_set_src); + +/** + * edma_set_dest - set initial DMA destination address in parameter RAM slot + * @slot: parameter RAM slot being configured + * @dest_port: physical address of destination (memory, controller FIFO, etc) + * @addressMode: INCR, except in very rare cases + * @fifoWidth: ignored unless @addressMode is FIFO, else specifies the + * width to use when addressing the fifo (e.g. W8BIT, W32BIT) + * + * Note that the destination address is modified during the DMA transfer + * according to edma_set_dest_index(). + */ +void edma_set_dest(unsigned slot, dma_addr_t dest_port, + enum address_mode mode, enum fifo_width width) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_slots) { + unsigned int i = edma_parm_read(ctlr, PARM_OPT, slot); + + if (mode) { + /* set DAM and program FWID */ + i = (i & ~(EDMA_FWID)) | (DAM | ((width & 0x7) << 8)); + } else { + /* clear DAM */ + i &= ~DAM; + } + edma_parm_write(ctlr, PARM_OPT, slot, i); + /* set the destination port address + in dest register of param structure */ + edma_parm_write(ctlr, PARM_DST, slot, dest_port); + } +} +EXPORT_SYMBOL(edma_set_dest); + +/** + * edma_get_position - returns the current transfer points + * @slot: parameter RAM slot being examined + * @src: pointer to source port position + * @dst: pointer to destination port position + * + * Returns current source and destination addresses for a particular + * parameter RAM slot. Its channel should not be active when this is called. + */ +void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst) +{ + struct edmacc_param temp; + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + edma_read_slot(EDMA_CTLR_CHAN(ctlr, slot), &temp); + if (src != NULL) + *src = temp.src; + if (dst != NULL) + *dst = temp.dst; +} +EXPORT_SYMBOL(edma_get_position); + +/** + * edma_set_src_index - configure DMA source address indexing + * @slot: parameter RAM slot being configured + * @src_bidx: byte offset between source arrays in a frame + * @src_cidx: byte offset between source frames in a block + * + * Offsets are specified to support either contiguous or discontiguous + * memory transfers, or repeated access to a hardware register, as needed. + * When accessing hardware registers, both offsets are normally zero. + */ +void edma_set_src_index(unsigned slot, s16 src_bidx, s16 src_cidx) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_slots) { + edma_parm_modify(ctlr, PARM_SRC_DST_BIDX, slot, + 0xffff0000, src_bidx); + edma_parm_modify(ctlr, PARM_SRC_DST_CIDX, slot, + 0xffff0000, src_cidx); + } +} +EXPORT_SYMBOL(edma_set_src_index); + +/** + * edma_set_dest_index - configure DMA destination address indexing + * @slot: parameter RAM slot being configured + * @dest_bidx: byte offset between destination arrays in a frame + * @dest_cidx: byte offset between destination frames in a block + * + * Offsets are specified to support either contiguous or discontiguous + * memory transfers, or repeated access to a hardware register, as needed. + * When accessing hardware registers, both offsets are normally zero. + */ +void edma_set_dest_index(unsigned slot, s16 dest_bidx, s16 dest_cidx) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_slots) { + edma_parm_modify(ctlr, PARM_SRC_DST_BIDX, slot, + 0x0000ffff, dest_bidx << 16); + edma_parm_modify(ctlr, PARM_SRC_DST_CIDX, slot, + 0x0000ffff, dest_cidx << 16); + } +} +EXPORT_SYMBOL(edma_set_dest_index); + +/** + * edma_set_transfer_params - configure DMA transfer parameters + * @slot: parameter RAM slot being configured + * @acnt: how many bytes per array (at least one) + * @bcnt: how many arrays per frame (at least one) + * @ccnt: how many frames per block (at least one) + * @bcnt_rld: used only for A-Synchronized transfers; this specifies + * the value to reload into bcnt when it decrements to zero + * @sync_mode: ASYNC or ABSYNC + * + * See the EDMA3 documentation to understand how to configure and link + * transfers using the fields in PaRAM slots. If you are not doing it + * all at once with edma_write_slot(), you will use this routine + * plus two calls each for source and destination, setting the initial + * address and saying how to index that address. + * + * An example of an A-Synchronized transfer is a serial link using a + * single word shift register. In that case, @acnt would be equal to + * that word size; the serial controller issues a DMA synchronization + * event to transfer each word, and memory access by the DMA transfer + * controller will be word-at-a-time. + * + * An example of an AB-Synchronized transfer is a device using a FIFO. + * In that case, @acnt equals the FIFO width and @bcnt equals its depth. + * The controller with the FIFO issues DMA synchronization events when + * the FIFO threshold is reached, and the DMA transfer controller will + * transfer one frame to (or from) the FIFO. It will probably use + * efficient burst modes to access memory. + */ +void edma_set_transfer_params(unsigned slot, + u16 acnt, u16 bcnt, u16 ccnt, + u16 bcnt_rld, enum sync_dimension sync_mode) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_slots) { + edma_parm_modify(ctlr, PARM_LINK_BCNTRLD, slot, + 0x0000ffff, bcnt_rld << 16); + if (sync_mode == ASYNC) + edma_parm_and(ctlr, PARM_OPT, slot, ~SYNCDIM); + else + edma_parm_or(ctlr, PARM_OPT, slot, SYNCDIM); + /* Set the acount, bcount, ccount registers */ + edma_parm_write(ctlr, PARM_A_B_CNT, slot, (bcnt << 16) | acnt); + edma_parm_write(ctlr, PARM_CCNT, slot, ccnt); + } +} +EXPORT_SYMBOL(edma_set_transfer_params); + +/** + * edma_link - link one parameter RAM slot to another + * @from: parameter RAM slot originating the link + * @to: parameter RAM slot which is the link target + * + * The originating slot should not be part of any active DMA transfer. + */ +void edma_link(unsigned from, unsigned to) +{ + unsigned ctlr_from, ctlr_to; + + ctlr_from = EDMA_CTLR(from); + from = EDMA_CHAN_SLOT(from); + ctlr_to = EDMA_CTLR(to); + to = EDMA_CHAN_SLOT(to); + + if (from >= edma_cc[ctlr_from]->num_slots) + return; + if (to >= edma_cc[ctlr_to]->num_slots) + return; + edma_parm_modify(ctlr_from, PARM_LINK_BCNTRLD, from, 0xffff0000, + PARM_OFFSET(to)); +} +EXPORT_SYMBOL(edma_link); + +/** + * edma_unlink - cut link from one parameter RAM slot + * @from: parameter RAM slot originating the link + * + * The originating slot should not be part of any active DMA transfer. + * Its link is set to 0xffff. + */ +void edma_unlink(unsigned from) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(from); + from = EDMA_CHAN_SLOT(from); + + if (from >= edma_cc[ctlr]->num_slots) + return; + edma_parm_or(ctlr, PARM_LINK_BCNTRLD, from, 0xffff); +} +EXPORT_SYMBOL(edma_unlink); + +/*-----------------------------------------------------------------------*/ + +/* Parameter RAM operations (ii) -- read/write whole parameter sets */ + +/** + * edma_write_slot - write parameter RAM data for slot + * @slot: number of parameter RAM slot being modified + * @param: data to be written into parameter RAM slot + * + * Use this to assign all parameters of a transfer at once. This + * allows more efficient setup of transfers than issuing multiple + * calls to set up those parameters in small pieces, and provides + * complete control over all transfer options. + */ +void edma_write_slot(unsigned slot, const struct edmacc_param *param) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot >= edma_cc[ctlr]->num_slots) + return; + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot), param, + PARM_SIZE); +} +EXPORT_SYMBOL(edma_write_slot); + +/** + * edma_read_slot - read parameter RAM data from slot + * @slot: number of parameter RAM slot being copied + * @param: where to store copy of parameter RAM data + * + * Use this to read data from a parameter RAM slot, perhaps to + * save them as a template for later reuse. + */ +void edma_read_slot(unsigned slot, struct edmacc_param *param) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot >= edma_cc[ctlr]->num_slots) + return; + memcpy_fromio(param, edmacc_regs_base[ctlr] + PARM_OFFSET(slot), + PARM_SIZE); +} +EXPORT_SYMBOL(edma_read_slot); + +/*-----------------------------------------------------------------------*/ + +/* Various EDMA channel control operations */ + +/** + * edma_pause - pause dma on a channel + * @channel: on which edma_start() has been called + * + * This temporarily disables EDMA hardware events on the specified channel, + * preventing them from triggering new transfers on its behalf + */ +void edma_pause(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel < edma_cc[ctlr]->num_channels) { + unsigned int mask = BIT(channel & 0x1f); + + edma_shadow0_write_array(ctlr, SH_EECR, channel >> 5, mask); + } +} +EXPORT_SYMBOL(edma_pause); + +/** + * edma_resume - resumes dma on a paused channel + * @channel: on which edma_pause() has been called + * + * This re-enables EDMA hardware events on the specified channel. + */ +void edma_resume(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel < edma_cc[ctlr]->num_channels) { + unsigned int mask = BIT(channel & 0x1f); + + edma_shadow0_write_array(ctlr, SH_EESR, channel >> 5, mask); + } +} +EXPORT_SYMBOL(edma_resume); + +/** + * edma_start - start dma on a channel + * @channel: channel being activated + * + * Channels with event associations will be triggered by their hardware + * events, and channels without such associations will be triggered by + * software. (At this writing there is no interface for using software + * triggers except with channels that don't support hardware triggers.) + * + * Returns zero on success, else negative errno. + */ +int edma_start(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel < edma_cc[ctlr]->num_channels) { + int j = channel >> 5; + unsigned int mask = BIT(channel & 0x1f); + + /* EDMA channels without event association */ + if (test_bit(channel, edma_cc[ctlr]->edma_unused)) { + pr_debug("EDMA: ESR%d %08x\n", j, + edma_shadow0_read_array(ctlr, SH_ESR, j)); + edma_shadow0_write_array(ctlr, SH_ESR, j, mask); + return 0; + } + + /* EDMA channel with event association */ + pr_debug("EDMA: ER%d %08x\n", j, + edma_shadow0_read_array(ctlr, SH_ER, j)); + /* Clear any pending event or error */ + edma_write_array(ctlr, EDMA_ECR, j, mask); + edma_write_array(ctlr, EDMA_EMCR, j, mask); + /* Clear any SER */ + edma_shadow0_write_array(ctlr, SH_SECR, j, mask); + edma_shadow0_write_array(ctlr, SH_EESR, j, mask); + pr_debug("EDMA: EER%d %08x\n", j, + edma_shadow0_read_array(ctlr, SH_EER, j)); + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL(edma_start); + +/** + * edma_stop - stops dma on the channel passed + * @channel: channel being deactivated + * + * When @lch is a channel, any active transfer is paused and + * all pending hardware events are cleared. The current transfer + * may not be resumed, and the channel's Parameter RAM should be + * reinitialized before being reused. + */ +void edma_stop(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel < edma_cc[ctlr]->num_channels) { + int j = channel >> 5; + unsigned int mask = BIT(channel & 0x1f); + + edma_shadow0_write_array(ctlr, SH_EECR, j, mask); + edma_shadow0_write_array(ctlr, SH_ECR, j, mask); + edma_shadow0_write_array(ctlr, SH_SECR, j, mask); + edma_write_array(ctlr, EDMA_EMCR, j, mask); + + pr_debug("EDMA: EER%d %08x\n", j, + edma_shadow0_read_array(ctlr, SH_EER, j)); + + /* REVISIT: consider guarding against inappropriate event + * chaining by overwriting with dummy_paramset. + */ + } +} +EXPORT_SYMBOL(edma_stop); + +/****************************************************************************** + * + * It cleans ParamEntry qand bring back EDMA to initial state if media has + * been removed before EDMA has finished.It is usedful for removable media. + * Arguments: + * ch_no - channel no + * + * Return: zero on success, or corresponding error no on failure + * + * FIXME this should not be needed ... edma_stop() should suffice. + * + *****************************************************************************/ + +void edma_clean_channel(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel < edma_cc[ctlr]->num_channels) { + int j = (channel >> 5); + unsigned int mask = BIT(channel & 0x1f); + + pr_debug("EDMA: EMR%d %08x\n", j, + edma_read_array(ctlr, EDMA_EMR, j)); + edma_shadow0_write_array(ctlr, SH_ECR, j, mask); + /* Clear the corresponding EMR bits */ + edma_write_array(ctlr, EDMA_EMCR, j, mask); + /* Clear any SER */ + edma_shadow0_write_array(ctlr, SH_SECR, j, mask); + edma_write(ctlr, EDMA_CCERRCLR, BIT(16) | BIT(1) | BIT(0)); + } +} +EXPORT_SYMBOL(edma_clean_channel); + +/* + * edma_clear_event - clear an outstanding event on the DMA channel + * Arguments: + * channel - channel number + */ +void edma_clear_event(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel >= edma_cc[ctlr]->num_channels) + return; + if (channel < 32) + edma_write(ctlr, EDMA_ECR, BIT(channel)); + else + edma_write(ctlr, EDMA_ECRH, BIT(channel - 32)); +} +EXPORT_SYMBOL(edma_clear_event); + +/*-----------------------------------------------------------------------*/ + +static int __init edma_probe(struct platform_device *pdev) +{ + struct edma_soc_info **info = pdev->dev.platform_data; + const s8 (*queue_priority_mapping)[2]; + const s8 (*queue_tc_mapping)[2]; + int i, j, off, ln, found = 0; + int status = -1; + const s16 (*rsv_chans)[2]; + const s16 (*rsv_slots)[2]; + int irq[EDMA_MAX_CC] = {0, 0}; + int err_irq[EDMA_MAX_CC] = {0, 0}; + struct resource *r[EDMA_MAX_CC] = {NULL}; + resource_size_t len[EDMA_MAX_CC]; + char res_name[10]; + char irq_name[10]; + + if (!info) + return -ENODEV; + + for (j = 0; j < EDMA_MAX_CC; j++) { + sprintf(res_name, "edma_cc%d", j); + r[j] = platform_get_resource_byname(pdev, IORESOURCE_MEM, + res_name); + if (!r[j] || !info[j]) { + if (found) + break; + else + return -ENODEV; + } else { + found = 1; + } + + len[j] = resource_size(r[j]); + + r[j] = request_mem_region(r[j]->start, len[j], + dev_name(&pdev->dev)); + if (!r[j]) { + status = -EBUSY; + goto fail1; + } + + edmacc_regs_base[j] = ioremap(r[j]->start, len[j]); + if (!edmacc_regs_base[j]) { + status = -EBUSY; + goto fail1; + } + + edma_cc[j] = kzalloc(sizeof(struct edma), GFP_KERNEL); + if (!edma_cc[j]) { + status = -ENOMEM; + goto fail1; + } + + edma_cc[j]->num_channels = min_t(unsigned, info[j]->n_channel, + EDMA_MAX_DMACH); + edma_cc[j]->num_slots = min_t(unsigned, info[j]->n_slot, + EDMA_MAX_PARAMENTRY); + edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc, + EDMA_MAX_CC); + + edma_cc[j]->default_queue = info[j]->default_queue; + + dev_dbg(&pdev->dev, "DMA REG BASE ADDR=%p\n", + edmacc_regs_base[j]); + + for (i = 0; i < edma_cc[j]->num_slots; i++) + memcpy_toio(edmacc_regs_base[j] + PARM_OFFSET(i), + &dummy_paramset, PARM_SIZE); + + /* Mark all channels as unused */ + memset(edma_cc[j]->edma_unused, 0xff, + sizeof(edma_cc[j]->edma_unused)); + + if (info[j]->rsv) { + + /* Clear the reserved channels in unused list */ + rsv_chans = info[j]->rsv->rsv_chans; + if (rsv_chans) { + for (i = 0; rsv_chans[i][0] != -1; i++) { + off = rsv_chans[i][0]; + ln = rsv_chans[i][1]; + clear_bits(off, ln, + edma_cc[j]->edma_unused); + } + } + + /* Set the reserved slots in inuse list */ + rsv_slots = info[j]->rsv->rsv_slots; + if (rsv_slots) { + for (i = 0; rsv_slots[i][0] != -1; i++) { + off = rsv_slots[i][0]; + ln = rsv_slots[i][1]; + set_bits(off, ln, + edma_cc[j]->edma_inuse); + } + } + } + + sprintf(irq_name, "edma%d", j); + irq[j] = platform_get_irq_byname(pdev, irq_name); + edma_cc[j]->irq_res_start = irq[j]; + status = request_irq(irq[j], dma_irq_handler, 0, "edma", + &pdev->dev); + if (status < 0) { + dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n", + irq[j], status); + goto fail; + } + + sprintf(irq_name, "edma%d_err", j); + err_irq[j] = platform_get_irq_byname(pdev, irq_name); + edma_cc[j]->irq_res_end = err_irq[j]; + status = request_irq(err_irq[j], dma_ccerr_handler, 0, + "edma_error", &pdev->dev); + if (status < 0) { + dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n", + err_irq[j], status); + goto fail; + } + + for (i = 0; i < edma_cc[j]->num_channels; i++) + map_dmach_queue(j, i, info[j]->default_queue); + + queue_tc_mapping = info[j]->queue_tc_mapping; + queue_priority_mapping = info[j]->queue_priority_mapping; + + /* Event queue to TC mapping */ + for (i = 0; queue_tc_mapping[i][0] != -1; i++) + map_queue_tc(j, queue_tc_mapping[i][0], + queue_tc_mapping[i][1]); + + /* Event queue priority mapping */ + for (i = 0; queue_priority_mapping[i][0] != -1; i++) + assign_priority_to_queue(j, + queue_priority_mapping[i][0], + queue_priority_mapping[i][1]); + + /* Map the channel to param entry if channel mapping logic + * exist + */ + if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST) + map_dmach_param(j); + + for (i = 0; i < info[j]->n_region; i++) { + edma_write_array2(j, EDMA_DRAE, i, 0, 0x0); + edma_write_array2(j, EDMA_DRAE, i, 1, 0x0); + edma_write_array(j, EDMA_QRAE, i, 0x0); + } + arch_num_cc++; + } + + if (tc_errs_handled) { + status = request_irq(IRQ_TCERRINT0, dma_tc0err_handler, 0, + "edma_tc0", &pdev->dev); + if (status < 0) { + dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n", + IRQ_TCERRINT0, status); + return status; + } + status = request_irq(IRQ_TCERRINT, dma_tc1err_handler, 0, + "edma_tc1", &pdev->dev); + if (status < 0) { + dev_dbg(&pdev->dev, "request_irq %d --> %d\n", + IRQ_TCERRINT, status); + return status; + } + } + + return 0; + +fail: + for (i = 0; i < EDMA_MAX_CC; i++) { + if (err_irq[i]) + free_irq(err_irq[i], &pdev->dev); + if (irq[i]) + free_irq(irq[i], &pdev->dev); + } +fail1: + for (i = 0; i < EDMA_MAX_CC; i++) { + if (r[i]) + release_mem_region(r[i]->start, len[i]); + if (edmacc_regs_base[i]) + iounmap(edmacc_regs_base[i]); + kfree(edma_cc[i]); + } + return status; +} + + +static struct platform_driver edma_driver = { + .driver.name = "edma", +}; + +static int __init edma_init(void) +{ + return platform_driver_probe(&edma_driver, edma_probe); +} +arch_initcall(edma_init); + diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile index dd1ffccc75e9..63997a1128e6 100644 --- a/arch/arm/mach-davinci/Makefile +++ b/arch/arm/mach-davinci/Makefile @@ -5,7 +5,7 @@ # Common objects obj-y := time.o clock.o serial.o psc.o \ - dma.o usb.o common.o sram.o aemif.o + usb.o common.o sram.o aemif.o obj-$(CONFIG_DAVINCI_MUX) += mux.o diff --git a/arch/arm/mach-davinci/board-tnetv107x-evm.c b/arch/arm/mach-davinci/board-tnetv107x-evm.c index ba798370fc96..78ea395d2aca 100644 --- a/arch/arm/mach-davinci/board-tnetv107x-evm.c +++ b/arch/arm/mach-davinci/board-tnetv107x-evm.c @@ -26,12 +26,12 @@ #include #include #include +#include #include #include #include -#include #include #include #include diff --git a/arch/arm/mach-davinci/davinci.h b/arch/arm/mach-davinci/davinci.h index b4b7451a8434..a883043d0820 100644 --- a/arch/arm/mach-davinci/davinci.h +++ b/arch/arm/mach-davinci/davinci.h @@ -23,9 +23,9 @@ #include #include #include +#include #include #include -#include #include #include diff --git a/arch/arm/mach-davinci/devices-tnetv107x.c b/arch/arm/mach-davinci/devices-tnetv107x.c index cfb194df18ed..612a0856e9c5 100644 --- a/arch/arm/mach-davinci/devices-tnetv107x.c +++ b/arch/arm/mach-davinci/devices-tnetv107x.c @@ -18,10 +18,10 @@ #include #include #include +#include #include #include -#include #include #include "clock.h" diff --git a/arch/arm/mach-davinci/devices.c b/arch/arm/mach-davinci/devices.c index a7068a3aa9d3..90b83d00fe2b 100644 --- a/arch/arm/mach-davinci/devices.c +++ b/arch/arm/mach-davinci/devices.c @@ -19,9 +19,10 @@ #include #include #include -#include #include #include +#include + #include "davinci.h" #include "clock.h" @@ -34,6 +35,9 @@ #define DM365_MMCSD0_BASE 0x01D11000 #define DM365_MMCSD1_BASE 0x01D00000 +#define DAVINCI_DMA_MMCRXEVT 26 +#define DAVINCI_DMA_MMCTXEVT 27 + void __iomem *davinci_sysmod_base; void davinci_map_sysmod(void) diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index a11034a358f1..526cf7d06d0e 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -28,6 +27,7 @@ #include #include #include +#include #include "davinci.h" #include "clock.h" diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 40fa4fee9331..c4b741173c06 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -18,11 +18,11 @@ #include #include #include +#include #include #include -#include #include #include #include diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 4d37d3e2a193..dd156d58fe64 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -12,11 +12,11 @@ #include #include #include +#include #include #include -#include #include #include #include diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index ac7b431c4c8e..6d52a321a8cf 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -13,11 +13,11 @@ #include #include #include +#include #include #include -#include #include #include #include diff --git a/arch/arm/mach-davinci/dma.c b/arch/arm/mach-davinci/dma.c deleted file mode 100644 index 45b7c71d9cc1..000000000000 --- a/arch/arm/mach-davinci/dma.c +++ /dev/null @@ -1,1591 +0,0 @@ -/* - * EDMA3 support for DaVinci - * - * Copyright (C) 2006-2009 Texas Instruments. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#include -#include -#include -#include -#include -#include -#include - -#include - -/* Offsets matching "struct edmacc_param" */ -#define PARM_OPT 0x00 -#define PARM_SRC 0x04 -#define PARM_A_B_CNT 0x08 -#define PARM_DST 0x0c -#define PARM_SRC_DST_BIDX 0x10 -#define PARM_LINK_BCNTRLD 0x14 -#define PARM_SRC_DST_CIDX 0x18 -#define PARM_CCNT 0x1c - -#define PARM_SIZE 0x20 - -/* Offsets for EDMA CC global channel registers and their shadows */ -#define SH_ER 0x00 /* 64 bits */ -#define SH_ECR 0x08 /* 64 bits */ -#define SH_ESR 0x10 /* 64 bits */ -#define SH_CER 0x18 /* 64 bits */ -#define SH_EER 0x20 /* 64 bits */ -#define SH_EECR 0x28 /* 64 bits */ -#define SH_EESR 0x30 /* 64 bits */ -#define SH_SER 0x38 /* 64 bits */ -#define SH_SECR 0x40 /* 64 bits */ -#define SH_IER 0x50 /* 64 bits */ -#define SH_IECR 0x58 /* 64 bits */ -#define SH_IESR 0x60 /* 64 bits */ -#define SH_IPR 0x68 /* 64 bits */ -#define SH_ICR 0x70 /* 64 bits */ -#define SH_IEVAL 0x78 -#define SH_QER 0x80 -#define SH_QEER 0x84 -#define SH_QEECR 0x88 -#define SH_QEESR 0x8c -#define SH_QSER 0x90 -#define SH_QSECR 0x94 -#define SH_SIZE 0x200 - -/* Offsets for EDMA CC global registers */ -#define EDMA_REV 0x0000 -#define EDMA_CCCFG 0x0004 -#define EDMA_QCHMAP 0x0200 /* 8 registers */ -#define EDMA_DMAQNUM 0x0240 /* 8 registers (4 on OMAP-L1xx) */ -#define EDMA_QDMAQNUM 0x0260 -#define EDMA_QUETCMAP 0x0280 -#define EDMA_QUEPRI 0x0284 -#define EDMA_EMR 0x0300 /* 64 bits */ -#define EDMA_EMCR 0x0308 /* 64 bits */ -#define EDMA_QEMR 0x0310 -#define EDMA_QEMCR 0x0314 -#define EDMA_CCERR 0x0318 -#define EDMA_CCERRCLR 0x031c -#define EDMA_EEVAL 0x0320 -#define EDMA_DRAE 0x0340 /* 4 x 64 bits*/ -#define EDMA_QRAE 0x0380 /* 4 registers */ -#define EDMA_QUEEVTENTRY 0x0400 /* 2 x 16 registers */ -#define EDMA_QSTAT 0x0600 /* 2 registers */ -#define EDMA_QWMTHRA 0x0620 -#define EDMA_QWMTHRB 0x0624 -#define EDMA_CCSTAT 0x0640 - -#define EDMA_M 0x1000 /* global channel registers */ -#define EDMA_ECR 0x1008 -#define EDMA_ECRH 0x100C -#define EDMA_SHADOW0 0x2000 /* 4 regions shadowing global channels */ -#define EDMA_PARM 0x4000 /* 128 param entries */ - -#define PARM_OFFSET(param_no) (EDMA_PARM + ((param_no) << 5)) - -#define EDMA_DCHMAP 0x0100 /* 64 registers */ -#define CHMAP_EXIST BIT(24) - -#define EDMA_MAX_DMACH 64 -#define EDMA_MAX_PARAMENTRY 512 - -/*****************************************************************************/ - -static void __iomem *edmacc_regs_base[EDMA_MAX_CC]; - -static inline unsigned int edma_read(unsigned ctlr, int offset) -{ - return (unsigned int)__raw_readl(edmacc_regs_base[ctlr] + offset); -} - -static inline void edma_write(unsigned ctlr, int offset, int val) -{ - __raw_writel(val, edmacc_regs_base[ctlr] + offset); -} -static inline void edma_modify(unsigned ctlr, int offset, unsigned and, - unsigned or) -{ - unsigned val = edma_read(ctlr, offset); - val &= and; - val |= or; - edma_write(ctlr, offset, val); -} -static inline void edma_and(unsigned ctlr, int offset, unsigned and) -{ - unsigned val = edma_read(ctlr, offset); - val &= and; - edma_write(ctlr, offset, val); -} -static inline void edma_or(unsigned ctlr, int offset, unsigned or) -{ - unsigned val = edma_read(ctlr, offset); - val |= or; - edma_write(ctlr, offset, val); -} -static inline unsigned int edma_read_array(unsigned ctlr, int offset, int i) -{ - return edma_read(ctlr, offset + (i << 2)); -} -static inline void edma_write_array(unsigned ctlr, int offset, int i, - unsigned val) -{ - edma_write(ctlr, offset + (i << 2), val); -} -static inline void edma_modify_array(unsigned ctlr, int offset, int i, - unsigned and, unsigned or) -{ - edma_modify(ctlr, offset + (i << 2), and, or); -} -static inline void edma_or_array(unsigned ctlr, int offset, int i, unsigned or) -{ - edma_or(ctlr, offset + (i << 2), or); -} -static inline void edma_or_array2(unsigned ctlr, int offset, int i, int j, - unsigned or) -{ - edma_or(ctlr, offset + ((i*2 + j) << 2), or); -} -static inline void edma_write_array2(unsigned ctlr, int offset, int i, int j, - unsigned val) -{ - edma_write(ctlr, offset + ((i*2 + j) << 2), val); -} -static inline unsigned int edma_shadow0_read(unsigned ctlr, int offset) -{ - return edma_read(ctlr, EDMA_SHADOW0 + offset); -} -static inline unsigned int edma_shadow0_read_array(unsigned ctlr, int offset, - int i) -{ - return edma_read(ctlr, EDMA_SHADOW0 + offset + (i << 2)); -} -static inline void edma_shadow0_write(unsigned ctlr, int offset, unsigned val) -{ - edma_write(ctlr, EDMA_SHADOW0 + offset, val); -} -static inline void edma_shadow0_write_array(unsigned ctlr, int offset, int i, - unsigned val) -{ - edma_write(ctlr, EDMA_SHADOW0 + offset + (i << 2), val); -} -static inline unsigned int edma_parm_read(unsigned ctlr, int offset, - int param_no) -{ - return edma_read(ctlr, EDMA_PARM + offset + (param_no << 5)); -} -static inline void edma_parm_write(unsigned ctlr, int offset, int param_no, - unsigned val) -{ - edma_write(ctlr, EDMA_PARM + offset + (param_no << 5), val); -} -static inline void edma_parm_modify(unsigned ctlr, int offset, int param_no, - unsigned and, unsigned or) -{ - edma_modify(ctlr, EDMA_PARM + offset + (param_no << 5), and, or); -} -static inline void edma_parm_and(unsigned ctlr, int offset, int param_no, - unsigned and) -{ - edma_and(ctlr, EDMA_PARM + offset + (param_no << 5), and); -} -static inline void edma_parm_or(unsigned ctlr, int offset, int param_no, - unsigned or) -{ - edma_or(ctlr, EDMA_PARM + offset + (param_no << 5), or); -} - -static inline void set_bits(int offset, int len, unsigned long *p) -{ - for (; len > 0; len--) - set_bit(offset + (len - 1), p); -} - -static inline void clear_bits(int offset, int len, unsigned long *p) -{ - for (; len > 0; len--) - clear_bit(offset + (len - 1), p); -} - -/*****************************************************************************/ - -/* actual number of DMA channels and slots on this silicon */ -struct edma { - /* how many dma resources of each type */ - unsigned num_channels; - unsigned num_region; - unsigned num_slots; - unsigned num_tc; - unsigned num_cc; - enum dma_event_q default_queue; - - /* list of channels with no even trigger; terminated by "-1" */ - const s8 *noevent; - - /* The edma_inuse bit for each PaRAM slot is clear unless the - * channel is in use ... by ARM or DSP, for QDMA, or whatever. - */ - DECLARE_BITMAP(edma_inuse, EDMA_MAX_PARAMENTRY); - - /* The edma_unused bit for each channel is clear unless - * it is not being used on this platform. It uses a bit - * of SOC-specific initialization code. - */ - DECLARE_BITMAP(edma_unused, EDMA_MAX_DMACH); - - unsigned irq_res_start; - unsigned irq_res_end; - - struct dma_interrupt_data { - void (*callback)(unsigned channel, unsigned short ch_status, - void *data); - void *data; - } intr_data[EDMA_MAX_DMACH]; -}; - -static struct edma *edma_cc[EDMA_MAX_CC]; -static int arch_num_cc; - -/* dummy param set used to (re)initialize parameter RAM slots */ -static const struct edmacc_param dummy_paramset = { - .link_bcntrld = 0xffff, - .ccnt = 1, -}; - -/*****************************************************************************/ - -static void map_dmach_queue(unsigned ctlr, unsigned ch_no, - enum dma_event_q queue_no) -{ - int bit = (ch_no & 0x7) * 4; - - /* default to low priority queue */ - if (queue_no == EVENTQ_DEFAULT) - queue_no = edma_cc[ctlr]->default_queue; - - queue_no &= 7; - edma_modify_array(ctlr, EDMA_DMAQNUM, (ch_no >> 3), - ~(0x7 << bit), queue_no << bit); -} - -static void __init map_queue_tc(unsigned ctlr, int queue_no, int tc_no) -{ - int bit = queue_no * 4; - edma_modify(ctlr, EDMA_QUETCMAP, ~(0x7 << bit), ((tc_no & 0x7) << bit)); -} - -static void __init assign_priority_to_queue(unsigned ctlr, int queue_no, - int priority) -{ - int bit = queue_no * 4; - edma_modify(ctlr, EDMA_QUEPRI, ~(0x7 << bit), - ((priority & 0x7) << bit)); -} - -/** - * map_dmach_param - Maps channel number to param entry number - * - * This maps the dma channel number to param entry numberter. In - * other words using the DMA channel mapping registers a param entry - * can be mapped to any channel - * - * Callers are responsible for ensuring the channel mapping logic is - * included in that particular EDMA variant (Eg : dm646x) - * - */ -static void __init map_dmach_param(unsigned ctlr) -{ - int i; - for (i = 0; i < EDMA_MAX_DMACH; i++) - edma_write_array(ctlr, EDMA_DCHMAP , i , (i << 5)); -} - -static inline void -setup_dma_interrupt(unsigned lch, - void (*callback)(unsigned channel, u16 ch_status, void *data), - void *data) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(lch); - lch = EDMA_CHAN_SLOT(lch); - - if (!callback) - edma_shadow0_write_array(ctlr, SH_IECR, lch >> 5, - BIT(lch & 0x1f)); - - edma_cc[ctlr]->intr_data[lch].callback = callback; - edma_cc[ctlr]->intr_data[lch].data = data; - - if (callback) { - edma_shadow0_write_array(ctlr, SH_ICR, lch >> 5, - BIT(lch & 0x1f)); - edma_shadow0_write_array(ctlr, SH_IESR, lch >> 5, - BIT(lch & 0x1f)); - } -} - -static int irq2ctlr(int irq) -{ - if (irq >= edma_cc[0]->irq_res_start && irq <= edma_cc[0]->irq_res_end) - return 0; - else if (irq >= edma_cc[1]->irq_res_start && - irq <= edma_cc[1]->irq_res_end) - return 1; - - return -1; -} - -/****************************************************************************** - * - * DMA interrupt handler - * - *****************************************************************************/ -static irqreturn_t dma_irq_handler(int irq, void *data) -{ - int ctlr; - u32 sh_ier; - u32 sh_ipr; - u32 bank; - - ctlr = irq2ctlr(irq); - if (ctlr < 0) - return IRQ_NONE; - - dev_dbg(data, "dma_irq_handler\n"); - - sh_ipr = edma_shadow0_read_array(ctlr, SH_IPR, 0); - if (!sh_ipr) { - sh_ipr = edma_shadow0_read_array(ctlr, SH_IPR, 1); - if (!sh_ipr) - return IRQ_NONE; - sh_ier = edma_shadow0_read_array(ctlr, SH_IER, 1); - bank = 1; - } else { - sh_ier = edma_shadow0_read_array(ctlr, SH_IER, 0); - bank = 0; - } - - do { - u32 slot; - u32 channel; - - dev_dbg(data, "IPR%d %08x\n", bank, sh_ipr); - - slot = __ffs(sh_ipr); - sh_ipr &= ~(BIT(slot)); - - if (sh_ier & BIT(slot)) { - channel = (bank << 5) | slot; - /* Clear the corresponding IPR bits */ - edma_shadow0_write_array(ctlr, SH_ICR, bank, - BIT(slot)); - if (edma_cc[ctlr]->intr_data[channel].callback) - edma_cc[ctlr]->intr_data[channel].callback( - channel, DMA_COMPLETE, - edma_cc[ctlr]->intr_data[channel].data); - } - } while (sh_ipr); - - edma_shadow0_write(ctlr, SH_IEVAL, 1); - return IRQ_HANDLED; -} - -/****************************************************************************** - * - * DMA error interrupt handler - * - *****************************************************************************/ -static irqreturn_t dma_ccerr_handler(int irq, void *data) -{ - int i; - int ctlr; - unsigned int cnt = 0; - - ctlr = irq2ctlr(irq); - if (ctlr < 0) - return IRQ_NONE; - - dev_dbg(data, "dma_ccerr_handler\n"); - - if ((edma_read_array(ctlr, EDMA_EMR, 0) == 0) && - (edma_read_array(ctlr, EDMA_EMR, 1) == 0) && - (edma_read(ctlr, EDMA_QEMR) == 0) && - (edma_read(ctlr, EDMA_CCERR) == 0)) - return IRQ_NONE; - - while (1) { - int j = -1; - if (edma_read_array(ctlr, EDMA_EMR, 0)) - j = 0; - else if (edma_read_array(ctlr, EDMA_EMR, 1)) - j = 1; - if (j >= 0) { - dev_dbg(data, "EMR%d %08x\n", j, - edma_read_array(ctlr, EDMA_EMR, j)); - for (i = 0; i < 32; i++) { - int k = (j << 5) + i; - if (edma_read_array(ctlr, EDMA_EMR, j) & - BIT(i)) { - /* Clear the corresponding EMR bits */ - edma_write_array(ctlr, EDMA_EMCR, j, - BIT(i)); - /* Clear any SER */ - edma_shadow0_write_array(ctlr, SH_SECR, - j, BIT(i)); - if (edma_cc[ctlr]->intr_data[k]. - callback) { - edma_cc[ctlr]->intr_data[k]. - callback(k, - DMA_CC_ERROR, - edma_cc[ctlr]->intr_data - [k].data); - } - } - } - } else if (edma_read(ctlr, EDMA_QEMR)) { - dev_dbg(data, "QEMR %02x\n", - edma_read(ctlr, EDMA_QEMR)); - for (i = 0; i < 8; i++) { - if (edma_read(ctlr, EDMA_QEMR) & BIT(i)) { - /* Clear the corresponding IPR bits */ - edma_write(ctlr, EDMA_QEMCR, BIT(i)); - edma_shadow0_write(ctlr, SH_QSECR, - BIT(i)); - - /* NOTE: not reported!! */ - } - } - } else if (edma_read(ctlr, EDMA_CCERR)) { - dev_dbg(data, "CCERR %08x\n", - edma_read(ctlr, EDMA_CCERR)); - /* FIXME: CCERR.BIT(16) ignored! much better - * to just write CCERRCLR with CCERR value... - */ - for (i = 0; i < 8; i++) { - if (edma_read(ctlr, EDMA_CCERR) & BIT(i)) { - /* Clear the corresponding IPR bits */ - edma_write(ctlr, EDMA_CCERRCLR, BIT(i)); - - /* NOTE: not reported!! */ - } - } - } - if ((edma_read_array(ctlr, EDMA_EMR, 0) == 0) && - (edma_read_array(ctlr, EDMA_EMR, 1) == 0) && - (edma_read(ctlr, EDMA_QEMR) == 0) && - (edma_read(ctlr, EDMA_CCERR) == 0)) - break; - cnt++; - if (cnt > 10) - break; - } - edma_write(ctlr, EDMA_EEVAL, 1); - return IRQ_HANDLED; -} - -/****************************************************************************** - * - * Transfer controller error interrupt handlers - * - *****************************************************************************/ - -#define tc_errs_handled false /* disabled as long as they're NOPs */ - -static irqreturn_t dma_tc0err_handler(int irq, void *data) -{ - dev_dbg(data, "dma_tc0err_handler\n"); - return IRQ_HANDLED; -} - -static irqreturn_t dma_tc1err_handler(int irq, void *data) -{ - dev_dbg(data, "dma_tc1err_handler\n"); - return IRQ_HANDLED; -} - -static int reserve_contiguous_slots(int ctlr, unsigned int id, - unsigned int num_slots, - unsigned int start_slot) -{ - int i, j; - unsigned int count = num_slots; - int stop_slot = start_slot; - DECLARE_BITMAP(tmp_inuse, EDMA_MAX_PARAMENTRY); - - for (i = start_slot; i < edma_cc[ctlr]->num_slots; ++i) { - j = EDMA_CHAN_SLOT(i); - if (!test_and_set_bit(j, edma_cc[ctlr]->edma_inuse)) { - /* Record our current beginning slot */ - if (count == num_slots) - stop_slot = i; - - count--; - set_bit(j, tmp_inuse); - - if (count == 0) - break; - } else { - clear_bit(j, tmp_inuse); - - if (id == EDMA_CONT_PARAMS_FIXED_EXACT) { - stop_slot = i; - break; - } else { - count = num_slots; - } - } - } - - /* - * We have to clear any bits that we set - * if we run out parameter RAM slots, i.e we do find a set - * of contiguous parameter RAM slots but do not find the exact number - * requested as we may reach the total number of parameter RAM slots - */ - if (i == edma_cc[ctlr]->num_slots) - stop_slot = i; - - j = start_slot; - for_each_set_bit_from(j, tmp_inuse, stop_slot) - clear_bit(j, edma_cc[ctlr]->edma_inuse); - - if (count) - return -EBUSY; - - for (j = i - num_slots + 1; j <= i; ++j) - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(j), - &dummy_paramset, PARM_SIZE); - - return EDMA_CTLR_CHAN(ctlr, i - num_slots + 1); -} - -static int prepare_unused_channel_list(struct device *dev, void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - int i, ctlr; - - for (i = 0; i < pdev->num_resources; i++) { - if ((pdev->resource[i].flags & IORESOURCE_DMA) && - (int)pdev->resource[i].start >= 0) { - ctlr = EDMA_CTLR(pdev->resource[i].start); - clear_bit(EDMA_CHAN_SLOT(pdev->resource[i].start), - edma_cc[ctlr]->edma_unused); - } - } - - return 0; -} - -/*-----------------------------------------------------------------------*/ - -static bool unused_chan_list_done; - -/* Resource alloc/free: dma channels, parameter RAM slots */ - -/** - * edma_alloc_channel - allocate DMA channel and paired parameter RAM - * @channel: specific channel to allocate; negative for "any unmapped channel" - * @callback: optional; to be issued on DMA completion or errors - * @data: passed to callback - * @eventq_no: an EVENTQ_* constant, used to choose which Transfer - * Controller (TC) executes requests using this channel. Use - * EVENTQ_DEFAULT unless you really need a high priority queue. - * - * This allocates a DMA channel and its associated parameter RAM slot. - * The parameter RAM is initialized to hold a dummy transfer. - * - * Normal use is to pass a specific channel number as @channel, to make - * use of hardware events mapped to that channel. When the channel will - * be used only for software triggering or event chaining, channels not - * mapped to hardware events (or mapped to unused events) are preferable. - * - * DMA transfers start from a channel using edma_start(), or by - * chaining. When the transfer described in that channel's parameter RAM - * slot completes, that slot's data may be reloaded through a link. - * - * DMA errors are only reported to the @callback associated with the - * channel driving that transfer, but transfer completion callbacks can - * be sent to another channel under control of the TCC field in - * the option word of the transfer's parameter RAM set. Drivers must not - * use DMA transfer completion callbacks for channels they did not allocate. - * (The same applies to TCC codes used in transfer chaining.) - * - * Returns the number of the channel, else negative errno. - */ -int edma_alloc_channel(int channel, - void (*callback)(unsigned channel, u16 ch_status, void *data), - void *data, - enum dma_event_q eventq_no) -{ - unsigned i, done = 0, ctlr = 0; - int ret = 0; - - if (!unused_chan_list_done) { - /* - * Scan all the platform devices to find out the EDMA channels - * used and clear them in the unused list, making the rest - * available for ARM usage. - */ - ret = bus_for_each_dev(&platform_bus_type, NULL, NULL, - prepare_unused_channel_list); - if (ret < 0) - return ret; - - unused_chan_list_done = true; - } - - if (channel >= 0) { - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - } - - if (channel < 0) { - for (i = 0; i < arch_num_cc; i++) { - channel = 0; - for (;;) { - channel = find_next_bit(edma_cc[i]->edma_unused, - edma_cc[i]->num_channels, - channel); - if (channel == edma_cc[i]->num_channels) - break; - if (!test_and_set_bit(channel, - edma_cc[i]->edma_inuse)) { - done = 1; - ctlr = i; - break; - } - channel++; - } - if (done) - break; - } - if (!done) - return -ENOMEM; - } else if (channel >= edma_cc[ctlr]->num_channels) { - return -EINVAL; - } else if (test_and_set_bit(channel, edma_cc[ctlr]->edma_inuse)) { - return -EBUSY; - } - - /* ensure access through shadow region 0 */ - edma_or_array2(ctlr, EDMA_DRAE, 0, channel >> 5, BIT(channel & 0x1f)); - - /* ensure no events are pending */ - edma_stop(EDMA_CTLR_CHAN(ctlr, channel)); - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(channel), - &dummy_paramset, PARM_SIZE); - - if (callback) - setup_dma_interrupt(EDMA_CTLR_CHAN(ctlr, channel), - callback, data); - - map_dmach_queue(ctlr, channel, eventq_no); - - return EDMA_CTLR_CHAN(ctlr, channel); -} -EXPORT_SYMBOL(edma_alloc_channel); - - -/** - * edma_free_channel - deallocate DMA channel - * @channel: dma channel returned from edma_alloc_channel() - * - * This deallocates the DMA channel and associated parameter RAM slot - * allocated by edma_alloc_channel(). - * - * Callers are responsible for ensuring the channel is inactive, and - * will not be reactivated by linking, chaining, or software calls to - * edma_start(). - */ -void edma_free_channel(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel >= edma_cc[ctlr]->num_channels) - return; - - setup_dma_interrupt(channel, NULL, NULL); - /* REVISIT should probably take out of shadow region 0 */ - - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(channel), - &dummy_paramset, PARM_SIZE); - clear_bit(channel, edma_cc[ctlr]->edma_inuse); -} -EXPORT_SYMBOL(edma_free_channel); - -/** - * edma_alloc_slot - allocate DMA parameter RAM - * @slot: specific slot to allocate; negative for "any unused slot" - * - * This allocates a parameter RAM slot, initializing it to hold a - * dummy transfer. Slots allocated using this routine have not been - * mapped to a hardware DMA channel, and will normally be used by - * linking to them from a slot associated with a DMA channel. - * - * Normal use is to pass EDMA_SLOT_ANY as the @slot, but specific - * slots may be allocated on behalf of DSP firmware. - * - * Returns the number of the slot, else negative errno. - */ -int edma_alloc_slot(unsigned ctlr, int slot) -{ - if (!edma_cc[ctlr]) - return -EINVAL; - - if (slot >= 0) - slot = EDMA_CHAN_SLOT(slot); - - if (slot < 0) { - slot = edma_cc[ctlr]->num_channels; - for (;;) { - slot = find_next_zero_bit(edma_cc[ctlr]->edma_inuse, - edma_cc[ctlr]->num_slots, slot); - if (slot == edma_cc[ctlr]->num_slots) - return -ENOMEM; - if (!test_and_set_bit(slot, edma_cc[ctlr]->edma_inuse)) - break; - } - } else if (slot < edma_cc[ctlr]->num_channels || - slot >= edma_cc[ctlr]->num_slots) { - return -EINVAL; - } else if (test_and_set_bit(slot, edma_cc[ctlr]->edma_inuse)) { - return -EBUSY; - } - - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot), - &dummy_paramset, PARM_SIZE); - - return EDMA_CTLR_CHAN(ctlr, slot); -} -EXPORT_SYMBOL(edma_alloc_slot); - -/** - * edma_free_slot - deallocate DMA parameter RAM - * @slot: parameter RAM slot returned from edma_alloc_slot() - * - * This deallocates the parameter RAM slot allocated by edma_alloc_slot(). - * Callers are responsible for ensuring the slot is inactive, and will - * not be activated. - */ -void edma_free_slot(unsigned slot) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_channels || - slot >= edma_cc[ctlr]->num_slots) - return; - - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot), - &dummy_paramset, PARM_SIZE); - clear_bit(slot, edma_cc[ctlr]->edma_inuse); -} -EXPORT_SYMBOL(edma_free_slot); - - -/** - * edma_alloc_cont_slots- alloc contiguous parameter RAM slots - * The API will return the starting point of a set of - * contiguous parameter RAM slots that have been requested - * - * @id: can only be EDMA_CONT_PARAMS_ANY or EDMA_CONT_PARAMS_FIXED_EXACT - * or EDMA_CONT_PARAMS_FIXED_NOT_EXACT - * @count: number of contiguous Paramter RAM slots - * @slot - the start value of Parameter RAM slot that should be passed if id - * is EDMA_CONT_PARAMS_FIXED_EXACT or EDMA_CONT_PARAMS_FIXED_NOT_EXACT - * - * If id is EDMA_CONT_PARAMS_ANY then the API starts looking for a set of - * contiguous Parameter RAM slots from parameter RAM 64 in the case of - * DaVinci SOCs and 32 in the case of DA8xx SOCs. - * - * If id is EDMA_CONT_PARAMS_FIXED_EXACT then the API starts looking for a - * set of contiguous parameter RAM slots from the "slot" that is passed as an - * argument to the API. - * - * If id is EDMA_CONT_PARAMS_FIXED_NOT_EXACT then the API initially tries - * starts looking for a set of contiguous parameter RAMs from the "slot" - * that is passed as an argument to the API. On failure the API will try to - * find a set of contiguous Parameter RAM slots from the remaining Parameter - * RAM slots - */ -int edma_alloc_cont_slots(unsigned ctlr, unsigned int id, int slot, int count) -{ - /* - * The start slot requested should be greater than - * the number of channels and lesser than the total number - * of slots - */ - if ((id != EDMA_CONT_PARAMS_ANY) && - (slot < edma_cc[ctlr]->num_channels || - slot >= edma_cc[ctlr]->num_slots)) - return -EINVAL; - - /* - * The number of parameter RAM slots requested cannot be less than 1 - * and cannot be more than the number of slots minus the number of - * channels - */ - if (count < 1 || count > - (edma_cc[ctlr]->num_slots - edma_cc[ctlr]->num_channels)) - return -EINVAL; - - switch (id) { - case EDMA_CONT_PARAMS_ANY: - return reserve_contiguous_slots(ctlr, id, count, - edma_cc[ctlr]->num_channels); - case EDMA_CONT_PARAMS_FIXED_EXACT: - case EDMA_CONT_PARAMS_FIXED_NOT_EXACT: - return reserve_contiguous_slots(ctlr, id, count, slot); - default: - return -EINVAL; - } - -} -EXPORT_SYMBOL(edma_alloc_cont_slots); - -/** - * edma_free_cont_slots - deallocate DMA parameter RAM slots - * @slot: first parameter RAM of a set of parameter RAM slots to be freed - * @count: the number of contiguous parameter RAM slots to be freed - * - * This deallocates the parameter RAM slots allocated by - * edma_alloc_cont_slots. - * Callers/applications need to keep track of sets of contiguous - * parameter RAM slots that have been allocated using the edma_alloc_cont_slots - * API. - * Callers are responsible for ensuring the slots are inactive, and will - * not be activated. - */ -int edma_free_cont_slots(unsigned slot, int count) -{ - unsigned ctlr, slot_to_free; - int i; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_channels || - slot >= edma_cc[ctlr]->num_slots || - count < 1) - return -EINVAL; - - for (i = slot; i < slot + count; ++i) { - ctlr = EDMA_CTLR(i); - slot_to_free = EDMA_CHAN_SLOT(i); - - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot_to_free), - &dummy_paramset, PARM_SIZE); - clear_bit(slot_to_free, edma_cc[ctlr]->edma_inuse); - } - - return 0; -} -EXPORT_SYMBOL(edma_free_cont_slots); - -/*-----------------------------------------------------------------------*/ - -/* Parameter RAM operations (i) -- read/write partial slots */ - -/** - * edma_set_src - set initial DMA source address in parameter RAM slot - * @slot: parameter RAM slot being configured - * @src_port: physical address of source (memory, controller FIFO, etc) - * @addressMode: INCR, except in very rare cases - * @fifoWidth: ignored unless @addressMode is FIFO, else specifies the - * width to use when addressing the fifo (e.g. W8BIT, W32BIT) - * - * Note that the source address is modified during the DMA transfer - * according to edma_set_src_index(). - */ -void edma_set_src(unsigned slot, dma_addr_t src_port, - enum address_mode mode, enum fifo_width width) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_slots) { - unsigned int i = edma_parm_read(ctlr, PARM_OPT, slot); - - if (mode) { - /* set SAM and program FWID */ - i = (i & ~(EDMA_FWID)) | (SAM | ((width & 0x7) << 8)); - } else { - /* clear SAM */ - i &= ~SAM; - } - edma_parm_write(ctlr, PARM_OPT, slot, i); - - /* set the source port address - in source register of param structure */ - edma_parm_write(ctlr, PARM_SRC, slot, src_port); - } -} -EXPORT_SYMBOL(edma_set_src); - -/** - * edma_set_dest - set initial DMA destination address in parameter RAM slot - * @slot: parameter RAM slot being configured - * @dest_port: physical address of destination (memory, controller FIFO, etc) - * @addressMode: INCR, except in very rare cases - * @fifoWidth: ignored unless @addressMode is FIFO, else specifies the - * width to use when addressing the fifo (e.g. W8BIT, W32BIT) - * - * Note that the destination address is modified during the DMA transfer - * according to edma_set_dest_index(). - */ -void edma_set_dest(unsigned slot, dma_addr_t dest_port, - enum address_mode mode, enum fifo_width width) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_slots) { - unsigned int i = edma_parm_read(ctlr, PARM_OPT, slot); - - if (mode) { - /* set DAM and program FWID */ - i = (i & ~(EDMA_FWID)) | (DAM | ((width & 0x7) << 8)); - } else { - /* clear DAM */ - i &= ~DAM; - } - edma_parm_write(ctlr, PARM_OPT, slot, i); - /* set the destination port address - in dest register of param structure */ - edma_parm_write(ctlr, PARM_DST, slot, dest_port); - } -} -EXPORT_SYMBOL(edma_set_dest); - -/** - * edma_get_position - returns the current transfer points - * @slot: parameter RAM slot being examined - * @src: pointer to source port position - * @dst: pointer to destination port position - * - * Returns current source and destination addresses for a particular - * parameter RAM slot. Its channel should not be active when this is called. - */ -void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst) -{ - struct edmacc_param temp; - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - edma_read_slot(EDMA_CTLR_CHAN(ctlr, slot), &temp); - if (src != NULL) - *src = temp.src; - if (dst != NULL) - *dst = temp.dst; -} -EXPORT_SYMBOL(edma_get_position); - -/** - * edma_set_src_index - configure DMA source address indexing - * @slot: parameter RAM slot being configured - * @src_bidx: byte offset between source arrays in a frame - * @src_cidx: byte offset between source frames in a block - * - * Offsets are specified to support either contiguous or discontiguous - * memory transfers, or repeated access to a hardware register, as needed. - * When accessing hardware registers, both offsets are normally zero. - */ -void edma_set_src_index(unsigned slot, s16 src_bidx, s16 src_cidx) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_slots) { - edma_parm_modify(ctlr, PARM_SRC_DST_BIDX, slot, - 0xffff0000, src_bidx); - edma_parm_modify(ctlr, PARM_SRC_DST_CIDX, slot, - 0xffff0000, src_cidx); - } -} -EXPORT_SYMBOL(edma_set_src_index); - -/** - * edma_set_dest_index - configure DMA destination address indexing - * @slot: parameter RAM slot being configured - * @dest_bidx: byte offset between destination arrays in a frame - * @dest_cidx: byte offset between destination frames in a block - * - * Offsets are specified to support either contiguous or discontiguous - * memory transfers, or repeated access to a hardware register, as needed. - * When accessing hardware registers, both offsets are normally zero. - */ -void edma_set_dest_index(unsigned slot, s16 dest_bidx, s16 dest_cidx) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_slots) { - edma_parm_modify(ctlr, PARM_SRC_DST_BIDX, slot, - 0x0000ffff, dest_bidx << 16); - edma_parm_modify(ctlr, PARM_SRC_DST_CIDX, slot, - 0x0000ffff, dest_cidx << 16); - } -} -EXPORT_SYMBOL(edma_set_dest_index); - -/** - * edma_set_transfer_params - configure DMA transfer parameters - * @slot: parameter RAM slot being configured - * @acnt: how many bytes per array (at least one) - * @bcnt: how many arrays per frame (at least one) - * @ccnt: how many frames per block (at least one) - * @bcnt_rld: used only for A-Synchronized transfers; this specifies - * the value to reload into bcnt when it decrements to zero - * @sync_mode: ASYNC or ABSYNC - * - * See the EDMA3 documentation to understand how to configure and link - * transfers using the fields in PaRAM slots. If you are not doing it - * all at once with edma_write_slot(), you will use this routine - * plus two calls each for source and destination, setting the initial - * address and saying how to index that address. - * - * An example of an A-Synchronized transfer is a serial link using a - * single word shift register. In that case, @acnt would be equal to - * that word size; the serial controller issues a DMA synchronization - * event to transfer each word, and memory access by the DMA transfer - * controller will be word-at-a-time. - * - * An example of an AB-Synchronized transfer is a device using a FIFO. - * In that case, @acnt equals the FIFO width and @bcnt equals its depth. - * The controller with the FIFO issues DMA synchronization events when - * the FIFO threshold is reached, and the DMA transfer controller will - * transfer one frame to (or from) the FIFO. It will probably use - * efficient burst modes to access memory. - */ -void edma_set_transfer_params(unsigned slot, - u16 acnt, u16 bcnt, u16 ccnt, - u16 bcnt_rld, enum sync_dimension sync_mode) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_slots) { - edma_parm_modify(ctlr, PARM_LINK_BCNTRLD, slot, - 0x0000ffff, bcnt_rld << 16); - if (sync_mode == ASYNC) - edma_parm_and(ctlr, PARM_OPT, slot, ~SYNCDIM); - else - edma_parm_or(ctlr, PARM_OPT, slot, SYNCDIM); - /* Set the acount, bcount, ccount registers */ - edma_parm_write(ctlr, PARM_A_B_CNT, slot, (bcnt << 16) | acnt); - edma_parm_write(ctlr, PARM_CCNT, slot, ccnt); - } -} -EXPORT_SYMBOL(edma_set_transfer_params); - -/** - * edma_link - link one parameter RAM slot to another - * @from: parameter RAM slot originating the link - * @to: parameter RAM slot which is the link target - * - * The originating slot should not be part of any active DMA transfer. - */ -void edma_link(unsigned from, unsigned to) -{ - unsigned ctlr_from, ctlr_to; - - ctlr_from = EDMA_CTLR(from); - from = EDMA_CHAN_SLOT(from); - ctlr_to = EDMA_CTLR(to); - to = EDMA_CHAN_SLOT(to); - - if (from >= edma_cc[ctlr_from]->num_slots) - return; - if (to >= edma_cc[ctlr_to]->num_slots) - return; - edma_parm_modify(ctlr_from, PARM_LINK_BCNTRLD, from, 0xffff0000, - PARM_OFFSET(to)); -} -EXPORT_SYMBOL(edma_link); - -/** - * edma_unlink - cut link from one parameter RAM slot - * @from: parameter RAM slot originating the link - * - * The originating slot should not be part of any active DMA transfer. - * Its link is set to 0xffff. - */ -void edma_unlink(unsigned from) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(from); - from = EDMA_CHAN_SLOT(from); - - if (from >= edma_cc[ctlr]->num_slots) - return; - edma_parm_or(ctlr, PARM_LINK_BCNTRLD, from, 0xffff); -} -EXPORT_SYMBOL(edma_unlink); - -/*-----------------------------------------------------------------------*/ - -/* Parameter RAM operations (ii) -- read/write whole parameter sets */ - -/** - * edma_write_slot - write parameter RAM data for slot - * @slot: number of parameter RAM slot being modified - * @param: data to be written into parameter RAM slot - * - * Use this to assign all parameters of a transfer at once. This - * allows more efficient setup of transfers than issuing multiple - * calls to set up those parameters in small pieces, and provides - * complete control over all transfer options. - */ -void edma_write_slot(unsigned slot, const struct edmacc_param *param) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot >= edma_cc[ctlr]->num_slots) - return; - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot), param, - PARM_SIZE); -} -EXPORT_SYMBOL(edma_write_slot); - -/** - * edma_read_slot - read parameter RAM data from slot - * @slot: number of parameter RAM slot being copied - * @param: where to store copy of parameter RAM data - * - * Use this to read data from a parameter RAM slot, perhaps to - * save them as a template for later reuse. - */ -void edma_read_slot(unsigned slot, struct edmacc_param *param) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot >= edma_cc[ctlr]->num_slots) - return; - memcpy_fromio(param, edmacc_regs_base[ctlr] + PARM_OFFSET(slot), - PARM_SIZE); -} -EXPORT_SYMBOL(edma_read_slot); - -/*-----------------------------------------------------------------------*/ - -/* Various EDMA channel control operations */ - -/** - * edma_pause - pause dma on a channel - * @channel: on which edma_start() has been called - * - * This temporarily disables EDMA hardware events on the specified channel, - * preventing them from triggering new transfers on its behalf - */ -void edma_pause(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel < edma_cc[ctlr]->num_channels) { - unsigned int mask = BIT(channel & 0x1f); - - edma_shadow0_write_array(ctlr, SH_EECR, channel >> 5, mask); - } -} -EXPORT_SYMBOL(edma_pause); - -/** - * edma_resume - resumes dma on a paused channel - * @channel: on which edma_pause() has been called - * - * This re-enables EDMA hardware events on the specified channel. - */ -void edma_resume(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel < edma_cc[ctlr]->num_channels) { - unsigned int mask = BIT(channel & 0x1f); - - edma_shadow0_write_array(ctlr, SH_EESR, channel >> 5, mask); - } -} -EXPORT_SYMBOL(edma_resume); - -/** - * edma_start - start dma on a channel - * @channel: channel being activated - * - * Channels with event associations will be triggered by their hardware - * events, and channels without such associations will be triggered by - * software. (At this writing there is no interface for using software - * triggers except with channels that don't support hardware triggers.) - * - * Returns zero on success, else negative errno. - */ -int edma_start(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel < edma_cc[ctlr]->num_channels) { - int j = channel >> 5; - unsigned int mask = BIT(channel & 0x1f); - - /* EDMA channels without event association */ - if (test_bit(channel, edma_cc[ctlr]->edma_unused)) { - pr_debug("EDMA: ESR%d %08x\n", j, - edma_shadow0_read_array(ctlr, SH_ESR, j)); - edma_shadow0_write_array(ctlr, SH_ESR, j, mask); - return 0; - } - - /* EDMA channel with event association */ - pr_debug("EDMA: ER%d %08x\n", j, - edma_shadow0_read_array(ctlr, SH_ER, j)); - /* Clear any pending event or error */ - edma_write_array(ctlr, EDMA_ECR, j, mask); - edma_write_array(ctlr, EDMA_EMCR, j, mask); - /* Clear any SER */ - edma_shadow0_write_array(ctlr, SH_SECR, j, mask); - edma_shadow0_write_array(ctlr, SH_EESR, j, mask); - pr_debug("EDMA: EER%d %08x\n", j, - edma_shadow0_read_array(ctlr, SH_EER, j)); - return 0; - } - - return -EINVAL; -} -EXPORT_SYMBOL(edma_start); - -/** - * edma_stop - stops dma on the channel passed - * @channel: channel being deactivated - * - * When @lch is a channel, any active transfer is paused and - * all pending hardware events are cleared. The current transfer - * may not be resumed, and the channel's Parameter RAM should be - * reinitialized before being reused. - */ -void edma_stop(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel < edma_cc[ctlr]->num_channels) { - int j = channel >> 5; - unsigned int mask = BIT(channel & 0x1f); - - edma_shadow0_write_array(ctlr, SH_EECR, j, mask); - edma_shadow0_write_array(ctlr, SH_ECR, j, mask); - edma_shadow0_write_array(ctlr, SH_SECR, j, mask); - edma_write_array(ctlr, EDMA_EMCR, j, mask); - - pr_debug("EDMA: EER%d %08x\n", j, - edma_shadow0_read_array(ctlr, SH_EER, j)); - - /* REVISIT: consider guarding against inappropriate event - * chaining by overwriting with dummy_paramset. - */ - } -} -EXPORT_SYMBOL(edma_stop); - -/****************************************************************************** - * - * It cleans ParamEntry qand bring back EDMA to initial state if media has - * been removed before EDMA has finished.It is usedful for removable media. - * Arguments: - * ch_no - channel no - * - * Return: zero on success, or corresponding error no on failure - * - * FIXME this should not be needed ... edma_stop() should suffice. - * - *****************************************************************************/ - -void edma_clean_channel(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel < edma_cc[ctlr]->num_channels) { - int j = (channel >> 5); - unsigned int mask = BIT(channel & 0x1f); - - pr_debug("EDMA: EMR%d %08x\n", j, - edma_read_array(ctlr, EDMA_EMR, j)); - edma_shadow0_write_array(ctlr, SH_ECR, j, mask); - /* Clear the corresponding EMR bits */ - edma_write_array(ctlr, EDMA_EMCR, j, mask); - /* Clear any SER */ - edma_shadow0_write_array(ctlr, SH_SECR, j, mask); - edma_write(ctlr, EDMA_CCERRCLR, BIT(16) | BIT(1) | BIT(0)); - } -} -EXPORT_SYMBOL(edma_clean_channel); - -/* - * edma_clear_event - clear an outstanding event on the DMA channel - * Arguments: - * channel - channel number - */ -void edma_clear_event(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel >= edma_cc[ctlr]->num_channels) - return; - if (channel < 32) - edma_write(ctlr, EDMA_ECR, BIT(channel)); - else - edma_write(ctlr, EDMA_ECRH, BIT(channel - 32)); -} -EXPORT_SYMBOL(edma_clear_event); - -/*-----------------------------------------------------------------------*/ - -static int __init edma_probe(struct platform_device *pdev) -{ - struct edma_soc_info **info = pdev->dev.platform_data; - const s8 (*queue_priority_mapping)[2]; - const s8 (*queue_tc_mapping)[2]; - int i, j, off, ln, found = 0; - int status = -1; - const s16 (*rsv_chans)[2]; - const s16 (*rsv_slots)[2]; - int irq[EDMA_MAX_CC] = {0, 0}; - int err_irq[EDMA_MAX_CC] = {0, 0}; - struct resource *r[EDMA_MAX_CC] = {NULL}; - resource_size_t len[EDMA_MAX_CC]; - char res_name[10]; - char irq_name[10]; - - if (!info) - return -ENODEV; - - for (j = 0; j < EDMA_MAX_CC; j++) { - sprintf(res_name, "edma_cc%d", j); - r[j] = platform_get_resource_byname(pdev, IORESOURCE_MEM, - res_name); - if (!r[j] || !info[j]) { - if (found) - break; - else - return -ENODEV; - } else { - found = 1; - } - - len[j] = resource_size(r[j]); - - r[j] = request_mem_region(r[j]->start, len[j], - dev_name(&pdev->dev)); - if (!r[j]) { - status = -EBUSY; - goto fail1; - } - - edmacc_regs_base[j] = ioremap(r[j]->start, len[j]); - if (!edmacc_regs_base[j]) { - status = -EBUSY; - goto fail1; - } - - edma_cc[j] = kzalloc(sizeof(struct edma), GFP_KERNEL); - if (!edma_cc[j]) { - status = -ENOMEM; - goto fail1; - } - - edma_cc[j]->num_channels = min_t(unsigned, info[j]->n_channel, - EDMA_MAX_DMACH); - edma_cc[j]->num_slots = min_t(unsigned, info[j]->n_slot, - EDMA_MAX_PARAMENTRY); - edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc, - EDMA_MAX_CC); - - edma_cc[j]->default_queue = info[j]->default_queue; - - dev_dbg(&pdev->dev, "DMA REG BASE ADDR=%p\n", - edmacc_regs_base[j]); - - for (i = 0; i < edma_cc[j]->num_slots; i++) - memcpy_toio(edmacc_regs_base[j] + PARM_OFFSET(i), - &dummy_paramset, PARM_SIZE); - - /* Mark all channels as unused */ - memset(edma_cc[j]->edma_unused, 0xff, - sizeof(edma_cc[j]->edma_unused)); - - if (info[j]->rsv) { - - /* Clear the reserved channels in unused list */ - rsv_chans = info[j]->rsv->rsv_chans; - if (rsv_chans) { - for (i = 0; rsv_chans[i][0] != -1; i++) { - off = rsv_chans[i][0]; - ln = rsv_chans[i][1]; - clear_bits(off, ln, - edma_cc[j]->edma_unused); - } - } - - /* Set the reserved slots in inuse list */ - rsv_slots = info[j]->rsv->rsv_slots; - if (rsv_slots) { - for (i = 0; rsv_slots[i][0] != -1; i++) { - off = rsv_slots[i][0]; - ln = rsv_slots[i][1]; - set_bits(off, ln, - edma_cc[j]->edma_inuse); - } - } - } - - sprintf(irq_name, "edma%d", j); - irq[j] = platform_get_irq_byname(pdev, irq_name); - edma_cc[j]->irq_res_start = irq[j]; - status = request_irq(irq[j], dma_irq_handler, 0, "edma", - &pdev->dev); - if (status < 0) { - dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n", - irq[j], status); - goto fail; - } - - sprintf(irq_name, "edma%d_err", j); - err_irq[j] = platform_get_irq_byname(pdev, irq_name); - edma_cc[j]->irq_res_end = err_irq[j]; - status = request_irq(err_irq[j], dma_ccerr_handler, 0, - "edma_error", &pdev->dev); - if (status < 0) { - dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n", - err_irq[j], status); - goto fail; - } - - for (i = 0; i < edma_cc[j]->num_channels; i++) - map_dmach_queue(j, i, info[j]->default_queue); - - queue_tc_mapping = info[j]->queue_tc_mapping; - queue_priority_mapping = info[j]->queue_priority_mapping; - - /* Event queue to TC mapping */ - for (i = 0; queue_tc_mapping[i][0] != -1; i++) - map_queue_tc(j, queue_tc_mapping[i][0], - queue_tc_mapping[i][1]); - - /* Event queue priority mapping */ - for (i = 0; queue_priority_mapping[i][0] != -1; i++) - assign_priority_to_queue(j, - queue_priority_mapping[i][0], - queue_priority_mapping[i][1]); - - /* Map the channel to param entry if channel mapping logic - * exist - */ - if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST) - map_dmach_param(j); - - for (i = 0; i < info[j]->n_region; i++) { - edma_write_array2(j, EDMA_DRAE, i, 0, 0x0); - edma_write_array2(j, EDMA_DRAE, i, 1, 0x0); - edma_write_array(j, EDMA_QRAE, i, 0x0); - } - arch_num_cc++; - } - - if (tc_errs_handled) { - status = request_irq(IRQ_TCERRINT0, dma_tc0err_handler, 0, - "edma_tc0", &pdev->dev); - if (status < 0) { - dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n", - IRQ_TCERRINT0, status); - return status; - } - status = request_irq(IRQ_TCERRINT, dma_tc1err_handler, 0, - "edma_tc1", &pdev->dev); - if (status < 0) { - dev_dbg(&pdev->dev, "request_irq %d --> %d\n", - IRQ_TCERRINT, status); - return status; - } - } - - return 0; - -fail: - for (i = 0; i < EDMA_MAX_CC; i++) { - if (err_irq[i]) - free_irq(err_irq[i], &pdev->dev); - if (irq[i]) - free_irq(irq[i], &pdev->dev); - } -fail1: - for (i = 0; i < EDMA_MAX_CC; i++) { - if (r[i]) - release_mem_region(r[i]->start, len[i]); - if (edmacc_regs_base[i]) - iounmap(edmacc_regs_base[i]); - kfree(edma_cc[i]); - } - return status; -} - - -static struct platform_driver edma_driver = { - .driver.name = "edma", -}; - -static int __init edma_init(void) -{ - return platform_driver_probe(&edma_driver, edma_probe); -} -arch_initcall(edma_init); - diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index faec783b0dc8..3c797e2272f8 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -20,8 +20,8 @@ #include #include -#include #include +#include #include #include #include diff --git a/arch/arm/mach-davinci/include/mach/edma.h b/arch/arm/mach-davinci/include/mach/edma.h deleted file mode 100644 index 7e84c906ceff..000000000000 --- a/arch/arm/mach-davinci/include/mach/edma.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * TI DAVINCI dma definitions - * - * Copyright (C) 2006-2009 Texas Instruments. - * - * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -/* - * This EDMA3 programming framework exposes two basic kinds of resource: - * - * Channel Triggers transfers, usually from a hardware event but - * also manually or by "chaining" from DMA completions. - * Each channel is coupled to a Parameter RAM (PaRAM) slot. - * - * Slot Each PaRAM slot holds a DMA transfer descriptor (PaRAM - * "set"), source and destination addresses, a link to a - * next PaRAM slot (if any), options for the transfer, and - * instructions for updating those addresses. There are - * more than twice as many slots as event channels. - * - * Each PaRAM set describes a sequence of transfers, either for one large - * buffer or for several discontiguous smaller buffers. An EDMA transfer - * is driven only from a channel, which performs the transfers specified - * in its PaRAM slot until there are no more transfers. When that last - * transfer completes, the "link" field may be used to reload the channel's - * PaRAM slot with a new transfer descriptor. - * - * The EDMA Channel Controller (CC) maps requests from channels into physical - * Transfer Controller (TC) requests when the channel triggers (by hardware - * or software events, or by chaining). The two physical DMA channels provided - * by the TCs are thus shared by many logical channels. - * - * DaVinci hardware also has a "QDMA" mechanism which is not currently - * supported through this interface. (DSP firmware uses it though.) - */ - -#ifndef EDMA_H_ -#define EDMA_H_ - -/* PaRAM slots are laid out like this */ -struct edmacc_param { - unsigned int opt; - unsigned int src; - unsigned int a_b_cnt; - unsigned int dst; - unsigned int src_dst_bidx; - unsigned int link_bcntrld; - unsigned int src_dst_cidx; - unsigned int ccnt; -}; - -#define CCINT0_INTERRUPT 16 -#define CCERRINT_INTERRUPT 17 -#define TCERRINT0_INTERRUPT 18 -#define TCERRINT1_INTERRUPT 19 - -/* fields in edmacc_param.opt */ -#define SAM BIT(0) -#define DAM BIT(1) -#define SYNCDIM BIT(2) -#define STATIC BIT(3) -#define EDMA_FWID (0x07 << 8) -#define TCCMODE BIT(11) -#define EDMA_TCC(t) ((t) << 12) -#define TCINTEN BIT(20) -#define ITCINTEN BIT(21) -#define TCCHEN BIT(22) -#define ITCCHEN BIT(23) - -#define TRWORD (0x7<<2) -#define PAENTRY (0x1ff<<5) - -/* Drivers should avoid using these symbolic names for dm644x - * channels, and use platform_device IORESOURCE_DMA resources - * instead. (Other DaVinci chips have different peripherals - * and thus have different DMA channel mappings.) - */ -#define DAVINCI_DMA_MCBSP_TX 2 -#define DAVINCI_DMA_MCBSP_RX 3 -#define DAVINCI_DMA_VPSS_HIST 4 -#define DAVINCI_DMA_VPSS_H3A 5 -#define DAVINCI_DMA_VPSS_PRVU 6 -#define DAVINCI_DMA_VPSS_RSZ 7 -#define DAVINCI_DMA_IMCOP_IMXINT 8 -#define DAVINCI_DMA_IMCOP_VLCDINT 9 -#define DAVINCI_DMA_IMCO_PASQINT 10 -#define DAVINCI_DMA_IMCOP_DSQINT 11 -#define DAVINCI_DMA_SPI_SPIX 16 -#define DAVINCI_DMA_SPI_SPIR 17 -#define DAVINCI_DMA_UART0_URXEVT0 18 -#define DAVINCI_DMA_UART0_UTXEVT0 19 -#define DAVINCI_DMA_UART1_URXEVT1 20 -#define DAVINCI_DMA_UART1_UTXEVT1 21 -#define DAVINCI_DMA_UART2_URXEVT2 22 -#define DAVINCI_DMA_UART2_UTXEVT2 23 -#define DAVINCI_DMA_MEMSTK_MSEVT 24 -#define DAVINCI_DMA_MMCRXEVT 26 -#define DAVINCI_DMA_MMCTXEVT 27 -#define DAVINCI_DMA_I2C_ICREVT 28 -#define DAVINCI_DMA_I2C_ICXEVT 29 -#define DAVINCI_DMA_GPIO_GPINT0 32 -#define DAVINCI_DMA_GPIO_GPINT1 33 -#define DAVINCI_DMA_GPIO_GPINT2 34 -#define DAVINCI_DMA_GPIO_GPINT3 35 -#define DAVINCI_DMA_GPIO_GPINT4 36 -#define DAVINCI_DMA_GPIO_GPINT5 37 -#define DAVINCI_DMA_GPIO_GPINT6 38 -#define DAVINCI_DMA_GPIO_GPINT7 39 -#define DAVINCI_DMA_GPIO_GPBNKINT0 40 -#define DAVINCI_DMA_GPIO_GPBNKINT1 41 -#define DAVINCI_DMA_GPIO_GPBNKINT2 42 -#define DAVINCI_DMA_GPIO_GPBNKINT3 43 -#define DAVINCI_DMA_GPIO_GPBNKINT4 44 -#define DAVINCI_DMA_TIMER0_TINT0 48 -#define DAVINCI_DMA_TIMER1_TINT1 49 -#define DAVINCI_DMA_TIMER2_TINT2 50 -#define DAVINCI_DMA_TIMER3_TINT3 51 -#define DAVINCI_DMA_PWM0 52 -#define DAVINCI_DMA_PWM1 53 -#define DAVINCI_DMA_PWM2 54 - -/* DA830 specific EDMA3 information */ -#define EDMA_DA830_NUM_DMACH 32 -#define EDMA_DA830_NUM_TCC 32 -#define EDMA_DA830_NUM_PARAMENTRY 128 -#define EDMA_DA830_NUM_EVQUE 2 -#define EDMA_DA830_NUM_TC 2 -#define EDMA_DA830_CHMAP_EXIST 0 -#define EDMA_DA830_NUM_REGIONS 4 -#define DA830_DMACH2EVENT_MAP0 0x000FC03Fu -#define DA830_DMACH2EVENT_MAP1 0x00000000u -#define DA830_EDMA_ARM_OWN 0x30FFCCFFu - -/*ch_status paramater of callback function possible values*/ -#define DMA_COMPLETE 1 -#define DMA_CC_ERROR 2 -#define DMA_TC1_ERROR 3 -#define DMA_TC2_ERROR 4 - -enum address_mode { - INCR = 0, - FIFO = 1 -}; - -enum fifo_width { - W8BIT = 0, - W16BIT = 1, - W32BIT = 2, - W64BIT = 3, - W128BIT = 4, - W256BIT = 5 -}; - -enum dma_event_q { - EVENTQ_0 = 0, - EVENTQ_1 = 1, - EVENTQ_2 = 2, - EVENTQ_3 = 3, - EVENTQ_DEFAULT = -1 -}; - -enum sync_dimension { - ASYNC = 0, - ABSYNC = 1 -}; - -#define EDMA_CTLR_CHAN(ctlr, chan) (((ctlr) << 16) | (chan)) -#define EDMA_CTLR(i) ((i) >> 16) -#define EDMA_CHAN_SLOT(i) ((i) & 0xffff) - -#define EDMA_CHANNEL_ANY -1 /* for edma_alloc_channel() */ -#define EDMA_SLOT_ANY -1 /* for edma_alloc_slot() */ -#define EDMA_CONT_PARAMS_ANY 1001 -#define EDMA_CONT_PARAMS_FIXED_EXACT 1002 -#define EDMA_CONT_PARAMS_FIXED_NOT_EXACT 1003 - -#define EDMA_MAX_CC 2 - -/* alloc/free DMA channels and their dedicated parameter RAM slots */ -int edma_alloc_channel(int channel, - void (*callback)(unsigned channel, u16 ch_status, void *data), - void *data, enum dma_event_q); -void edma_free_channel(unsigned channel); - -/* alloc/free parameter RAM slots */ -int edma_alloc_slot(unsigned ctlr, int slot); -void edma_free_slot(unsigned slot); - -/* alloc/free a set of contiguous parameter RAM slots */ -int edma_alloc_cont_slots(unsigned ctlr, unsigned int id, int slot, int count); -int edma_free_cont_slots(unsigned slot, int count); - -/* calls that operate on part of a parameter RAM slot */ -void edma_set_src(unsigned slot, dma_addr_t src_port, - enum address_mode mode, enum fifo_width); -void edma_set_dest(unsigned slot, dma_addr_t dest_port, - enum address_mode mode, enum fifo_width); -void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst); -void edma_set_src_index(unsigned slot, s16 src_bidx, s16 src_cidx); -void edma_set_dest_index(unsigned slot, s16 dest_bidx, s16 dest_cidx); -void edma_set_transfer_params(unsigned slot, u16 acnt, u16 bcnt, u16 ccnt, - u16 bcnt_rld, enum sync_dimension sync_mode); -void edma_link(unsigned from, unsigned to); -void edma_unlink(unsigned from); - -/* calls that operate on an entire parameter RAM slot */ -void edma_write_slot(unsigned slot, const struct edmacc_param *params); -void edma_read_slot(unsigned slot, struct edmacc_param *params); - -/* channel control operations */ -int edma_start(unsigned channel); -void edma_stop(unsigned channel); -void edma_clean_channel(unsigned channel); -void edma_clear_event(unsigned channel); -void edma_pause(unsigned channel); -void edma_resume(unsigned channel); - -struct edma_rsv_info { - - const s16 (*rsv_chans)[2]; - const s16 (*rsv_slots)[2]; -}; - -/* platform_data for EDMA driver */ -struct edma_soc_info { - - /* how many dma resources of each type */ - unsigned n_channel; - unsigned n_region; - unsigned n_slot; - unsigned n_tc; - unsigned n_cc; - /* - * Default queue is expected to be a low-priority queue. - * This way, long transfers on the default queue started - * by the codec engine will not cause audio defects. - */ - enum dma_event_q default_queue; - - /* Resource reservation for other cores */ - struct edma_rsv_info *rsv; - - const s8 (*queue_tc_mapping)[2]; - const s8 (*queue_priority_mapping)[2]; -}; - -#endif diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index cd7e3280fadd..5f3e532436ee 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -24,7 +24,7 @@ #include #include -#include +#include #include "dmaengine.h" #include "virt-dma.h" diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 3946a0eb3a03..5dfb70c669dc 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -37,6 +37,7 @@ #include #include +#include #include /* diff --git a/include/linux/mfd/davinci_voicecodec.h b/include/linux/mfd/davinci_voicecodec.h index 0ab61320ffa8..7dd6524d2aac 100644 --- a/include/linux/mfd/davinci_voicecodec.h +++ b/include/linux/mfd/davinci_voicecodec.h @@ -26,8 +26,7 @@ #include #include #include - -#include +#include /* * Register values. diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h new file mode 100644 index 000000000000..2344ea2675ad --- /dev/null +++ b/include/linux/platform_data/edma.h @@ -0,0 +1,182 @@ +/* + * TI EDMA definitions + * + * Copyright (C) 2006-2013 Texas Instruments. + * + * 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 EDMA3 programming framework exposes two basic kinds of resource: + * + * Channel Triggers transfers, usually from a hardware event but + * also manually or by "chaining" from DMA completions. + * Each channel is coupled to a Parameter RAM (PaRAM) slot. + * + * Slot Each PaRAM slot holds a DMA transfer descriptor (PaRAM + * "set"), source and destination addresses, a link to a + * next PaRAM slot (if any), options for the transfer, and + * instructions for updating those addresses. There are + * more than twice as many slots as event channels. + * + * Each PaRAM set describes a sequence of transfers, either for one large + * buffer or for several discontiguous smaller buffers. An EDMA transfer + * is driven only from a channel, which performs the transfers specified + * in its PaRAM slot until there are no more transfers. When that last + * transfer completes, the "link" field may be used to reload the channel's + * PaRAM slot with a new transfer descriptor. + * + * The EDMA Channel Controller (CC) maps requests from channels into physical + * Transfer Controller (TC) requests when the channel triggers (by hardware + * or software events, or by chaining). The two physical DMA channels provided + * by the TCs are thus shared by many logical channels. + * + * DaVinci hardware also has a "QDMA" mechanism which is not currently + * supported through this interface. (DSP firmware uses it though.) + */ + +#ifndef EDMA_H_ +#define EDMA_H_ + +/* PaRAM slots are laid out like this */ +struct edmacc_param { + unsigned int opt; + unsigned int src; + unsigned int a_b_cnt; + unsigned int dst; + unsigned int src_dst_bidx; + unsigned int link_bcntrld; + unsigned int src_dst_cidx; + unsigned int ccnt; +}; + +/* fields in edmacc_param.opt */ +#define SAM BIT(0) +#define DAM BIT(1) +#define SYNCDIM BIT(2) +#define STATIC BIT(3) +#define EDMA_FWID (0x07 << 8) +#define TCCMODE BIT(11) +#define EDMA_TCC(t) ((t) << 12) +#define TCINTEN BIT(20) +#define ITCINTEN BIT(21) +#define TCCHEN BIT(22) +#define ITCCHEN BIT(23) + +/*ch_status paramater of callback function possible values*/ +#define DMA_COMPLETE 1 +#define DMA_CC_ERROR 2 +#define DMA_TC1_ERROR 3 +#define DMA_TC2_ERROR 4 + +enum address_mode { + INCR = 0, + FIFO = 1 +}; + +enum fifo_width { + W8BIT = 0, + W16BIT = 1, + W32BIT = 2, + W64BIT = 3, + W128BIT = 4, + W256BIT = 5 +}; + +enum dma_event_q { + EVENTQ_0 = 0, + EVENTQ_1 = 1, + EVENTQ_2 = 2, + EVENTQ_3 = 3, + EVENTQ_DEFAULT = -1 +}; + +enum sync_dimension { + ASYNC = 0, + ABSYNC = 1 +}; + +#define EDMA_CTLR_CHAN(ctlr, chan) (((ctlr) << 16) | (chan)) +#define EDMA_CTLR(i) ((i) >> 16) +#define EDMA_CHAN_SLOT(i) ((i) & 0xffff) + +#define EDMA_CHANNEL_ANY -1 /* for edma_alloc_channel() */ +#define EDMA_SLOT_ANY -1 /* for edma_alloc_slot() */ +#define EDMA_CONT_PARAMS_ANY 1001 +#define EDMA_CONT_PARAMS_FIXED_EXACT 1002 +#define EDMA_CONT_PARAMS_FIXED_NOT_EXACT 1003 + +#define EDMA_MAX_CC 2 + +/* alloc/free DMA channels and their dedicated parameter RAM slots */ +int edma_alloc_channel(int channel, + void (*callback)(unsigned channel, u16 ch_status, void *data), + void *data, enum dma_event_q); +void edma_free_channel(unsigned channel); + +/* alloc/free parameter RAM slots */ +int edma_alloc_slot(unsigned ctlr, int slot); +void edma_free_slot(unsigned slot); + +/* alloc/free a set of contiguous parameter RAM slots */ +int edma_alloc_cont_slots(unsigned ctlr, unsigned int id, int slot, int count); +int edma_free_cont_slots(unsigned slot, int count); + +/* calls that operate on part of a parameter RAM slot */ +void edma_set_src(unsigned slot, dma_addr_t src_port, + enum address_mode mode, enum fifo_width); +void edma_set_dest(unsigned slot, dma_addr_t dest_port, + enum address_mode mode, enum fifo_width); +void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst); +void edma_set_src_index(unsigned slot, s16 src_bidx, s16 src_cidx); +void edma_set_dest_index(unsigned slot, s16 dest_bidx, s16 dest_cidx); +void edma_set_transfer_params(unsigned slot, u16 acnt, u16 bcnt, u16 ccnt, + u16 bcnt_rld, enum sync_dimension sync_mode); +void edma_link(unsigned from, unsigned to); +void edma_unlink(unsigned from); + +/* calls that operate on an entire parameter RAM slot */ +void edma_write_slot(unsigned slot, const struct edmacc_param *params); +void edma_read_slot(unsigned slot, struct edmacc_param *params); + +/* channel control operations */ +int edma_start(unsigned channel); +void edma_stop(unsigned channel); +void edma_clean_channel(unsigned channel); +void edma_clear_event(unsigned channel); +void edma_pause(unsigned channel); +void edma_resume(unsigned channel); + +struct edma_rsv_info { + + const s16 (*rsv_chans)[2]; + const s16 (*rsv_slots)[2]; +}; + +/* platform_data for EDMA driver */ +struct edma_soc_info { + + /* how many dma resources of each type */ + unsigned n_channel; + unsigned n_region; + unsigned n_slot; + unsigned n_tc; + unsigned n_cc; + /* + * Default queue is expected to be a low-priority queue. + * This way, long transfers on the default queue started + * by the codec engine will not cause audio defects. + */ + enum dma_event_q default_queue; + + /* Resource reservation for other cores */ + struct edma_rsv_info *rsv; + + const s8 (*queue_tc_mapping)[2]; + const s8 (*queue_priority_mapping)[2]; +}; + +#endif diff --git a/include/linux/platform_data/spi-davinci.h b/include/linux/platform_data/spi-davinci.h index 7af305b37868..8dc2fa47a2aa 100644 --- a/include/linux/platform_data/spi-davinci.h +++ b/include/linux/platform_data/spi-davinci.h @@ -19,7 +19,7 @@ #ifndef __ARCH_ARM_DAVINCI_SPI_H #define __ARCH_ARM_DAVINCI_SPI_H -#include +#include #define SPI_INTERN_CS 0xFF diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 484b22c5df5d..fd7c45b9ed5a 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index b2f27c2e5fdc..8460edce1c3b 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index b6ef7039dd09..fbb710c76c08 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h @@ -14,7 +14,7 @@ #include #include -#include +#include struct davinci_pcm_dma_params { int channel; /* sync dma channel ID */ -- cgit v1.3-18-gd494 From 0a085a9482fa51efb58c9d351ea98e83c5df93fc Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Sun, 5 May 2013 20:24:58 -0700 Subject: Input: pxa27x-keypad - use matrix_keymap for matrix keys pxa27x-keypad includes matrix keys. Make use of matrix_keymap for the matrix keys. Signed-off-by: Chao Xie Signed-off-by: Dmitry Torokhov --- arch/arm/mach-mmp/aspenite.c | 10 +++-- arch/arm/mach-mmp/teton_bga.c | 8 +++- arch/arm/mach-pxa/em-x270.c | 20 +++++++--- arch/arm/mach-pxa/ezx.c | 60 ++++++++++++++++++++--------- arch/arm/mach-pxa/littleton.c | 10 +++-- arch/arm/mach-pxa/mainstone.c | 10 +++-- arch/arm/mach-pxa/mioa701.c | 11 ++++-- arch/arm/mach-pxa/palmld.c | 10 +++-- arch/arm/mach-pxa/palmt5.c | 10 +++-- arch/arm/mach-pxa/palmtreo.c | 23 +++++++---- arch/arm/mach-pxa/palmtx.c | 10 +++-- arch/arm/mach-pxa/palmz72.c | 10 +++-- arch/arm/mach-pxa/tavorevb.c | 10 +++-- arch/arm/mach-pxa/z2.c | 10 +++-- arch/arm/mach-pxa/zylonite.c | 10 +++-- drivers/input/keyboard/Kconfig | 1 + drivers/input/keyboard/pxa27x_keypad.c | 35 +++++++++++------ include/linux/platform_data/keypad-pxa27x.h | 3 +- 18 files changed, 180 insertions(+), 81 deletions(-) (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c index 9f64d5632e07..1e233467fffa 100644 --- a/arch/arm/mach-mmp/aspenite.c +++ b/arch/arm/mach-mmp/aspenite.c @@ -205,7 +205,7 @@ struct pxa168fb_mach_info aspenite_lcd_info = { .invert_pixclock = 0, }; -static unsigned int aspenite_matrix_key_map[] = { +static const unsigned int aspenite_matrix_key_map[] = { KEY(0, 6, KEY_UP), /* SW 4 */ KEY(0, 7, KEY_DOWN), /* SW 5 */ KEY(1, 6, KEY_LEFT), /* SW 6 */ @@ -214,11 +214,15 @@ static unsigned int aspenite_matrix_key_map[] = { KEY(4, 7, KEY_ESC), /* SW 9 */ }; +static struct matrix_keymap_data aspenite_matrix_keymap_data = { + .keymap = aspenite_matrix_key_map, + .keymap_size = ARRAY_SIZE(aspenite_matrix_key_map), +}; + static struct pxa27x_keypad_platform_data aspenite_keypad_info __initdata = { .matrix_key_rows = 5, .matrix_key_cols = 8, - .matrix_key_map = aspenite_matrix_key_map, - .matrix_key_map_size = ARRAY_SIZE(aspenite_matrix_key_map), + .matrix_keymap_data = &aspenite_matrix_keymap_data, .debounce_interval = 30, }; diff --git a/arch/arm/mach-mmp/teton_bga.c b/arch/arm/mach-mmp/teton_bga.c index 8609967975ed..d8967fa48374 100644 --- a/arch/arm/mach-mmp/teton_bga.c +++ b/arch/arm/mach-mmp/teton_bga.c @@ -56,11 +56,15 @@ static unsigned int teton_bga_matrix_key_map[] = { KEY(1, 7, KEY_RIGHT), }; +static struct matrix_keymap_data teton_bga_matrix_keymap_data = { + .keymap = teton_bga_matrix_key_map, + .keymap_size = ARRAY_SIZE(teton_bga_matrix_key_map), +}; + static struct pxa27x_keypad_platform_data teton_bga_keypad_info __initdata = { .matrix_key_rows = 2, .matrix_key_cols = 8, - .matrix_key_map = teton_bga_matrix_key_map, - .matrix_key_map_size = ARRAY_SIZE(teton_bga_matrix_key_map), + .matrix_keymap_data = &teton_bga_matrix_keymap_data, .debounce_interval = 30, }; diff --git a/arch/arm/mach-pxa/em-x270.c b/arch/arm/mach-pxa/em-x270.c index 446563a7d1ad..f6726bb4eb95 100644 --- a/arch/arm/mach-pxa/em-x270.c +++ b/arch/arm/mach-pxa/em-x270.c @@ -833,21 +833,25 @@ static inline void em_x270_init_ac97(void) {} #endif #if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE) -static unsigned int em_x270_module_matrix_keys[] = { +static const unsigned int em_x270_module_matrix_keys[] = { KEY(0, 0, KEY_A), KEY(1, 0, KEY_UP), KEY(2, 1, KEY_B), KEY(0, 2, KEY_LEFT), KEY(1, 1, KEY_ENTER), KEY(2, 0, KEY_RIGHT), KEY(0, 1, KEY_C), KEY(1, 2, KEY_DOWN), KEY(2, 2, KEY_D), }; +static struct matrix_keymap_data em_x270_matrix_keymap_data = { + .keymap = em_x270_module_matrix_keys, + .keymap_size = ARRAY_SIZE(em_x270_module_matrix_keys), +}; + struct pxa27x_keypad_platform_data em_x270_module_keypad_info = { /* code map for the matrix keys */ .matrix_key_rows = 3, .matrix_key_cols = 3, - .matrix_key_map = em_x270_module_matrix_keys, - .matrix_key_map_size = ARRAY_SIZE(em_x270_module_matrix_keys), + .matrix_keymap_data = &em_x270_matrix_keymap_data, }; -static unsigned int em_x270_exeda_matrix_keys[] = { +static const unsigned int em_x270_exeda_matrix_keys[] = { KEY(0, 0, KEY_RIGHTSHIFT), KEY(0, 1, KEY_RIGHTCTRL), KEY(0, 2, KEY_RIGHTALT), KEY(0, 3, KEY_SPACE), KEY(0, 4, KEY_LEFTALT), KEY(0, 5, KEY_LEFTCTRL), @@ -889,12 +893,16 @@ static unsigned int em_x270_exeda_matrix_keys[] = { KEY(7, 6, 0), KEY(7, 7, 0), }; +static struct matrix_keymap_data em_x270_exeda_matrix_keymap_data = { + .keymap = em_x270_exeda_matrix_keys, + .keymap_size = ARRAY_SIZE(em_x270_exeda_matrix_keys), +}; + struct pxa27x_keypad_platform_data em_x270_exeda_keypad_info = { /* code map for the matrix keys */ .matrix_key_rows = 8, .matrix_key_cols = 8, - .matrix_key_map = em_x270_exeda_matrix_keys, - .matrix_key_map_size = ARRAY_SIZE(em_x270_exeda_matrix_keys), + .matrix_keymap_data = &em_x270_exeda_matrix_keymap_data, }; static void __init em_x270_init_keypad(void) diff --git a/arch/arm/mach-pxa/ezx.c b/arch/arm/mach-pxa/ezx.c index dca10709be8f..fe2eb8394dff 100644 --- a/arch/arm/mach-pxa/ezx.c +++ b/arch/arm/mach-pxa/ezx.c @@ -392,7 +392,7 @@ static unsigned long e6_pin_config[] __initdata = { /* KEYPAD */ #ifdef CONFIG_MACH_EZX_A780 -static unsigned int a780_key_map[] = { +static const unsigned int a780_key_map[] = { KEY(0, 0, KEY_SEND), KEY(0, 1, KEY_BACK), KEY(0, 2, KEY_END), @@ -424,11 +424,15 @@ static unsigned int a780_key_map[] = { KEY(4, 4, KEY_DOWN), }; +static struct matrix_keymap_data a780_matrix_keymap_data = { + .keymap = a780_key_map, + .keymap_size = ARRAY_SIZE(a780_key_map), +}; + static struct pxa27x_keypad_platform_data a780_keypad_platform_data = { .matrix_key_rows = 5, .matrix_key_cols = 5, - .matrix_key_map = a780_key_map, - .matrix_key_map_size = ARRAY_SIZE(a780_key_map), + .matrix_keymap_data = &a780_matrix_keymap_data, .direct_key_map = { KEY_CAMERA }, .direct_key_num = 1, @@ -438,7 +442,7 @@ static struct pxa27x_keypad_platform_data a780_keypad_platform_data = { #endif /* CONFIG_MACH_EZX_A780 */ #ifdef CONFIG_MACH_EZX_E680 -static unsigned int e680_key_map[] = { +static const unsigned int e680_key_map[] = { KEY(0, 0, KEY_UP), KEY(0, 1, KEY_RIGHT), KEY(0, 2, KEY_RESERVED), @@ -455,11 +459,15 @@ static unsigned int e680_key_map[] = { KEY(2, 3, KEY_KPENTER), }; +static struct matrix_keymap_data e680_matrix_keymap_data = { + .keymap = e680_key_map, + .keymap_size = ARRAY_SIZE(e680_key_map), +}; + static struct pxa27x_keypad_platform_data e680_keypad_platform_data = { .matrix_key_rows = 3, .matrix_key_cols = 4, - .matrix_key_map = e680_key_map, - .matrix_key_map_size = ARRAY_SIZE(e680_key_map), + .matrix_keymap_data = &e680_matrix_keymap_data, .direct_key_map = { KEY_CAMERA, @@ -476,7 +484,7 @@ static struct pxa27x_keypad_platform_data e680_keypad_platform_data = { #endif /* CONFIG_MACH_EZX_E680 */ #ifdef CONFIG_MACH_EZX_A1200 -static unsigned int a1200_key_map[] = { +static const unsigned int a1200_key_map[] = { KEY(0, 0, KEY_RESERVED), KEY(0, 1, KEY_RIGHT), KEY(0, 2, KEY_PAGEDOWN), @@ -513,18 +521,22 @@ static unsigned int a1200_key_map[] = { KEY(4, 5, KEY_RESERVED), }; +static struct matrix_keymap_data a1200_matrix_keymap_data = { + .keymap = a1200_key_map, + .keymap_size = ARRAY_SIZE(a1200_key_map), +}; + static struct pxa27x_keypad_platform_data a1200_keypad_platform_data = { .matrix_key_rows = 5, .matrix_key_cols = 6, - .matrix_key_map = a1200_key_map, - .matrix_key_map_size = ARRAY_SIZE(a1200_key_map), + .matrix_keymap_data = &a1200_matrix_keymap_data, .debounce_interval = 30, }; #endif /* CONFIG_MACH_EZX_A1200 */ #ifdef CONFIG_MACH_EZX_E6 -static unsigned int e6_key_map[] = { +static const unsigned int e6_key_map[] = { KEY(0, 0, KEY_RESERVED), KEY(0, 1, KEY_RIGHT), KEY(0, 2, KEY_PAGEDOWN), @@ -561,18 +573,22 @@ static unsigned int e6_key_map[] = { KEY(4, 5, KEY_PREVIOUSSONG), }; +static struct matrix_keymap_data e6_keymap_data = { + .keymap = e6_key_map, + .keymap_size = ARRAY_SIZE(e6_key_map), +}; + static struct pxa27x_keypad_platform_data e6_keypad_platform_data = { .matrix_key_rows = 5, .matrix_key_cols = 6, - .matrix_key_map = e6_key_map, - .matrix_key_map_size = ARRAY_SIZE(e6_key_map), + .matrix_keymap_data = &e6_keymap_data, .debounce_interval = 30, }; #endif /* CONFIG_MACH_EZX_E6 */ #ifdef CONFIG_MACH_EZX_A910 -static unsigned int a910_key_map[] = { +static const unsigned int a910_key_map[] = { KEY(0, 0, KEY_NUMERIC_6), KEY(0, 1, KEY_RIGHT), KEY(0, 2, KEY_PAGEDOWN), @@ -609,18 +625,22 @@ static unsigned int a910_key_map[] = { KEY(4, 5, KEY_RESERVED), }; +static struct matrix_keymap_data a910_matrix_keymap_data = { + .keymap = a910_key_map, + .keymap_size = ARRAY_SIZE(a910_key_map), +}; + static struct pxa27x_keypad_platform_data a910_keypad_platform_data = { .matrix_key_rows = 5, .matrix_key_cols = 6, - .matrix_key_map = a910_key_map, - .matrix_key_map_size = ARRAY_SIZE(a910_key_map), + .matrix_keymap_data = &a910_matrix_keymap_data, .debounce_interval = 30, }; #endif /* CONFIG_MACH_EZX_A910 */ #ifdef CONFIG_MACH_EZX_E2 -static unsigned int e2_key_map[] = { +static const unsigned int e2_key_map[] = { KEY(0, 0, KEY_NUMERIC_6), KEY(0, 1, KEY_RIGHT), KEY(0, 2, KEY_NUMERIC_9), @@ -657,11 +677,15 @@ static unsigned int e2_key_map[] = { KEY(4, 5, KEY_RESERVED), }; +static struct matrix_keymap_data e2_matrix_keymap_data = { + .keymap = e2_key_map, + .keymap_size = ARRAY_SIZE(e2_key_map), +}; + static struct pxa27x_keypad_platform_data e2_keypad_platform_data = { .matrix_key_rows = 5, .matrix_key_cols = 6, - .matrix_key_map = e2_key_map, - .matrix_key_map_size = ARRAY_SIZE(e2_key_map), + .matrix_keymap_data = &e2_matrix_keymap_data, .debounce_interval = 30, }; diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c index e848c4607baf..5d665588c7eb 100644 --- a/arch/arm/mach-pxa/littleton.c +++ b/arch/arm/mach-pxa/littleton.c @@ -222,7 +222,7 @@ static inline void littleton_init_spi(void) {} #endif #if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE) -static unsigned int littleton_matrix_key_map[] = { +static const unsigned int littleton_matrix_key_map[] = { /* KEY(row, col, key_code) */ KEY(1, 3, KEY_0), KEY(0, 0, KEY_1), KEY(1, 0, KEY_2), KEY(2, 0, KEY_3), KEY(0, 1, KEY_4), KEY(1, 1, KEY_5), KEY(2, 1, KEY_6), KEY(0, 2, KEY_7), @@ -249,11 +249,15 @@ static unsigned int littleton_matrix_key_map[] = { KEY(3, 1, KEY_F23), /* soft2 */ }; +static struct matrix_keymap_data littleton_matrix_keymap_data = { + .keymap = littleton_matrix_key_map, + .keymap_size = ARRAY_SIZE(littleton_matrix_key_map), +}; + static struct pxa27x_keypad_platform_data littleton_keypad_info = { .matrix_key_rows = 6, .matrix_key_cols = 5, - .matrix_key_map = littleton_matrix_key_map, - .matrix_key_map_size = ARRAY_SIZE(littleton_matrix_key_map), + .matrix_keymap_data = &littleton_matrix_keymap_data, .enable_rotary0 = 1, .rotary0_up_key = KEY_UP, diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c index 7a12c1ba90ff..d2c652318376 100644 --- a/arch/arm/mach-pxa/mainstone.c +++ b/arch/arm/mach-pxa/mainstone.c @@ -498,7 +498,7 @@ static struct pxaohci_platform_data mainstone_ohci_platform_data = { }; #if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE) -static unsigned int mainstone_matrix_keys[] = { +static const unsigned int mainstone_matrix_keys[] = { KEY(0, 0, KEY_A), KEY(1, 0, KEY_B), KEY(2, 0, KEY_C), KEY(3, 0, KEY_D), KEY(4, 0, KEY_E), KEY(5, 0, KEY_F), KEY(0, 1, KEY_G), KEY(1, 1, KEY_H), KEY(2, 1, KEY_I), @@ -527,11 +527,15 @@ static unsigned int mainstone_matrix_keys[] = { KEY(4, 6, KEY_SELECT), }; +static struct matrix_keymap_data mainstone_matrix_keymap_data = { + .keymap = mainstone_matrix_keys, + .keymap_size = ARRAY_SIZE(mainstone_matrix_keys), +}; + struct pxa27x_keypad_platform_data mainstone_keypad_info = { .matrix_key_rows = 6, .matrix_key_cols = 7, - .matrix_key_map = mainstone_matrix_keys, - .matrix_key_map_size = ARRAY_SIZE(mainstone_matrix_keys), + .matrix_keymap_data = &mainstone_matrix_keymap_data, .enable_rotary0 = 1, .rotary0_up_key = KEY_UP, diff --git a/arch/arm/mach-pxa/mioa701.c b/arch/arm/mach-pxa/mioa701.c index f8979b943cbf..654b0ac84dea 100644 --- a/arch/arm/mach-pxa/mioa701.c +++ b/arch/arm/mach-pxa/mioa701.c @@ -222,7 +222,7 @@ static struct pxafb_mach_info mioa701_pxafb_info = { /* * Keyboard configuration */ -static unsigned int mioa701_matrix_keys[] = { +static const unsigned int mioa701_matrix_keys[] = { KEY(0, 0, KEY_UP), KEY(0, 1, KEY_RIGHT), KEY(0, 2, KEY_MEDIA), @@ -233,11 +233,16 @@ static unsigned int mioa701_matrix_keys[] = { KEY(2, 1, KEY_PHONE), /* Phone Green key */ KEY(2, 2, KEY_CAMERA) /* Camera key */ }; + +static struct matrix_keymap_data mioa701_matrix_keymap_data = { + .keymap = mioa701_matrix_keys, + .keymap_size = ARRAY_SIZE(mioa701_matrix_keys), +}; + static struct pxa27x_keypad_platform_data mioa701_keypad_info = { .matrix_key_rows = 3, .matrix_key_cols = 3, - .matrix_key_map = mioa701_matrix_keys, - .matrix_key_map_size = ARRAY_SIZE(mioa701_matrix_keys), + .matrix_keymap_data = &mioa701_matrix_keymap_data, }; /* diff --git a/arch/arm/mach-pxa/palmld.c b/arch/arm/mach-pxa/palmld.c index 909b713e5789..cf210b11ffcc 100644 --- a/arch/arm/mach-pxa/palmld.c +++ b/arch/arm/mach-pxa/palmld.c @@ -173,7 +173,7 @@ static inline void palmld_nor_init(void) {} * GPIO keyboard ******************************************************************************/ #if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE) -static unsigned int palmld_matrix_keys[] = { +static const unsigned int palmld_matrix_keys[] = { KEY(0, 1, KEY_F2), KEY(0, 2, KEY_UP), @@ -190,11 +190,15 @@ static unsigned int palmld_matrix_keys[] = { KEY(3, 2, KEY_LEFT), }; +static struct matrix_keymap_data palmld_matrix_keymap_data = { + .keymap = palmld_matrix_keys, + .keymap_size = ARRAY_SIZE(palmld_matrix_keys), +}; + static struct pxa27x_keypad_platform_data palmld_keypad_platform_data = { .matrix_key_rows = 4, .matrix_key_cols = 3, - .matrix_key_map = palmld_matrix_keys, - .matrix_key_map_size = ARRAY_SIZE(palmld_matrix_keys), + .matrix_keymap_data = &palmld_matrix_keymap_data, .debounce_interval = 30, }; diff --git a/arch/arm/mach-pxa/palmt5.c b/arch/arm/mach-pxa/palmt5.c index 5033fd07968f..3ed9b029428b 100644 --- a/arch/arm/mach-pxa/palmt5.c +++ b/arch/arm/mach-pxa/palmt5.c @@ -108,7 +108,7 @@ static unsigned long palmt5_pin_config[] __initdata = { * GPIO keyboard ******************************************************************************/ #if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE) -static unsigned int palmt5_matrix_keys[] = { +static const unsigned int palmt5_matrix_keys[] = { KEY(0, 0, KEY_POWER), KEY(0, 1, KEY_F1), KEY(0, 2, KEY_ENTER), @@ -124,11 +124,15 @@ static unsigned int palmt5_matrix_keys[] = { KEY(3, 2, KEY_LEFT), }; +static struct matrix_keymap_data palmt5_matrix_keymap_data = { + .keymap = palmt5_matrix_keys, + .keymap_size = ARRAY_SIZE(palmt5_matrix_keys), +}; + static struct pxa27x_keypad_platform_data palmt5_keypad_platform_data = { .matrix_key_rows = 4, .matrix_key_cols = 3, - .matrix_key_map = palmt5_matrix_keys, - .matrix_key_map_size = ARRAY_SIZE(palmt5_matrix_keys), + .matrix_keymap_data = &palmt5_matrix_keymap_data, .debounce_interval = 30, }; diff --git a/arch/arm/mach-pxa/palmtreo.c b/arch/arm/mach-pxa/palmtreo.c index d82a50b4a803..d8b937c870de 100644 --- a/arch/arm/mach-pxa/palmtreo.c +++ b/arch/arm/mach-pxa/palmtreo.c @@ -168,7 +168,7 @@ static unsigned long centro685_pin_config[] __initdata = { * GPIO keyboard ******************************************************************************/ #if IS_ENABLED(CONFIG_KEYBOARD_PXA27x) -static unsigned int treo680_matrix_keys[] = { +static const unsigned int treo680_matrix_keys[] = { KEY(0, 0, KEY_F8), /* Red/Off/Power */ KEY(0, 1, KEY_LEFT), KEY(0, 2, KEY_LEFTCTRL), /* Alternate */ @@ -227,7 +227,7 @@ static unsigned int treo680_matrix_keys[] = { KEY(7, 5, KEY_I), }; -static unsigned int centro_matrix_keys[] = { +static const unsigned int centro_matrix_keys[] = { KEY(0, 0, KEY_F9), /* Home */ KEY(0, 1, KEY_LEFT), KEY(0, 2, KEY_LEFTCTRL), /* Alternate */ @@ -286,11 +286,20 @@ static unsigned int centro_matrix_keys[] = { KEY(7, 5, KEY_I), }; +static struct matrix_keymap_data treo680_matrix_keymap_data = { + .keymap = treo680_matrix_keys, + .keymap_size = ARRAY_SIZE(treo680_matrix_keys), +}; + +static struct matrix_keymap_data centro_matrix_keymap_data = { + .keymap = centro_matrix_keys, + .keymap_size = ARRAY_SIZE(centro_matrix_keys), +}; + static struct pxa27x_keypad_platform_data treo680_keypad_pdata = { .matrix_key_rows = 8, .matrix_key_cols = 7, - .matrix_key_map = treo680_matrix_keys, - .matrix_key_map_size = ARRAY_SIZE(treo680_matrix_keys), + .matrix_keymap_data = &treo680_matrix_keymap_data, .direct_key_map = { KEY_CONNECT }, .direct_key_num = 1, @@ -301,10 +310,8 @@ static void __init palmtreo_kpc_init(void) { static struct pxa27x_keypad_platform_data *data = &treo680_keypad_pdata; - if (machine_is_centro()) { - data->matrix_key_map = centro_matrix_keys; - data->matrix_key_map_size = ARRAY_SIZE(centro_matrix_keys); - } + if (machine_is_centro()) + data->matrix_keymap_data = ¢ro_matrix_keymap_data; pxa_set_keypad_info(&treo680_keypad_pdata); } diff --git a/arch/arm/mach-pxa/palmtx.c b/arch/arm/mach-pxa/palmtx.c index 627c93a7364c..83f830dd8ad8 100644 --- a/arch/arm/mach-pxa/palmtx.c +++ b/arch/arm/mach-pxa/palmtx.c @@ -176,7 +176,7 @@ static inline void palmtx_nor_init(void) {} * GPIO keyboard ******************************************************************************/ #if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE) -static unsigned int palmtx_matrix_keys[] = { +static const unsigned int palmtx_matrix_keys[] = { KEY(0, 0, KEY_POWER), KEY(0, 1, KEY_F1), KEY(0, 2, KEY_ENTER), @@ -192,11 +192,15 @@ static unsigned int palmtx_matrix_keys[] = { KEY(3, 2, KEY_LEFT), }; +static struct matrix_keymap_data palmtx_matrix_keymap_data = { + .keymap = palmtx_matrix_keys, + .keymap_size = ARRAY_SIZE(palmtx_matrix_keys), +}; + static struct pxa27x_keypad_platform_data palmtx_keypad_platform_data = { .matrix_key_rows = 4, .matrix_key_cols = 3, - .matrix_key_map = palmtx_matrix_keys, - .matrix_key_map_size = ARRAY_SIZE(palmtx_matrix_keys), + .matrix_keymap_data = &palmtx_matrix_keymap_data, .debounce_interval = 30, }; diff --git a/arch/arm/mach-pxa/palmz72.c b/arch/arm/mach-pxa/palmz72.c index 18b7fcd98592..1a35ddf218da 100644 --- a/arch/arm/mach-pxa/palmz72.c +++ b/arch/arm/mach-pxa/palmz72.c @@ -140,7 +140,7 @@ static unsigned long palmz72_pin_config[] __initdata = { * GPIO keyboard ******************************************************************************/ #if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE) -static unsigned int palmz72_matrix_keys[] = { +static const unsigned int palmz72_matrix_keys[] = { KEY(0, 0, KEY_POWER), KEY(0, 1, KEY_F1), KEY(0, 2, KEY_ENTER), @@ -156,11 +156,15 @@ static unsigned int palmz72_matrix_keys[] = { KEY(3, 2, KEY_LEFT), }; +static struct matrix_keymap_data almz72_matrix_keymap_data = { + .keymap = palmz72_matrix_keys, + .keymap_size = ARRAY_SIZE(palmz72_matrix_keys), +}; + static struct pxa27x_keypad_platform_data palmz72_keypad_platform_data = { .matrix_key_rows = 4, .matrix_key_cols = 3, - .matrix_key_map = palmz72_matrix_keys, - .matrix_key_map_size = ARRAY_SIZE(palmz72_matrix_keys), + .matrix_keymap_data = &almz72_matrix_keymap_data, .debounce_interval = 30, }; diff --git a/arch/arm/mach-pxa/tavorevb.c b/arch/arm/mach-pxa/tavorevb.c index f55979c09a5f..4680efe55345 100644 --- a/arch/arm/mach-pxa/tavorevb.c +++ b/arch/arm/mach-pxa/tavorevb.c @@ -106,7 +106,7 @@ static struct platform_device smc91x_device = { }; #if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE) -static unsigned int tavorevb_matrix_key_map[] = { +static const unsigned int tavorevb_matrix_key_map[] = { /* KEY(row, col, key_code) */ KEY(0, 4, KEY_A), KEY(0, 5, KEY_B), KEY(0, 6, KEY_C), KEY(1, 4, KEY_E), KEY(1, 5, KEY_F), KEY(1, 6, KEY_G), @@ -147,11 +147,15 @@ static unsigned int tavorevb_matrix_key_map[] = { KEY(3, 3, KEY_F23), /* soft2 */ }; +static struct matrix_keymap_data tavorevb_matrix_keymap_data = { + .keymap = tavorevb_matrix_key_map, + .keymap_size = ARRAY_SIZE(tavorevb_matrix_key_map), +}; + static struct pxa27x_keypad_platform_data tavorevb_keypad_info = { .matrix_key_rows = 7, .matrix_key_cols = 7, - .matrix_key_map = tavorevb_matrix_key_map, - .matrix_key_map_size = ARRAY_SIZE(tavorevb_matrix_key_map), + .matrix_keymap_data = &tavorevb_matrix_keymap_data, .debounce_interval = 30, }; diff --git a/arch/arm/mach-pxa/z2.c b/arch/arm/mach-pxa/z2.c index 989903a7e467..2513d8f4931f 100644 --- a/arch/arm/mach-pxa/z2.c +++ b/arch/arm/mach-pxa/z2.c @@ -345,7 +345,7 @@ static inline void z2_leds_init(void) {} * GPIO keyboard ******************************************************************************/ #if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE) -static unsigned int z2_matrix_keys[] = { +static const unsigned int z2_matrix_keys[] = { KEY(0, 0, KEY_OPTION), KEY(1, 0, KEY_UP), KEY(2, 0, KEY_DOWN), @@ -405,11 +405,15 @@ static unsigned int z2_matrix_keys[] = { KEY(5, 7, KEY_DOT), }; +static struct matrix_keymap_data z2_matrix_keymap_data = { + .keymap = z2_matrix_keys, + .keymap_size = ARRAY_SIZE(z2_matrix_keys), +}; + static struct pxa27x_keypad_platform_data z2_keypad_platform_data = { .matrix_key_rows = 7, .matrix_key_cols = 8, - .matrix_key_map = z2_matrix_keys, - .matrix_key_map_size = ARRAY_SIZE(z2_matrix_keys), + .matrix_keymap_data = &z2_matrix_keymap_data, .debounce_interval = 30, }; diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c index 1f00d650ac27..36cf7cf95ec1 100644 --- a/arch/arm/mach-pxa/zylonite.c +++ b/arch/arm/mach-pxa/zylonite.c @@ -263,7 +263,7 @@ static inline void zylonite_init_mmc(void) {} #endif #if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE) -static unsigned int zylonite_matrix_key_map[] = { +static const unsigned int zylonite_matrix_key_map[] = { /* KEY(row, col, key_code) */ KEY(0, 0, KEY_A), KEY(0, 1, KEY_B), KEY(0, 2, KEY_C), KEY(0, 5, KEY_D), KEY(1, 0, KEY_E), KEY(1, 1, KEY_F), KEY(1, 2, KEY_G), KEY(1, 5, KEY_H), @@ -306,11 +306,15 @@ static unsigned int zylonite_matrix_key_map[] = { KEY(0, 3, KEY_AUX), /* contact */ }; +static struct matrix_keymap_data zylonite_matrix_keymap_data = { + .keymap = zylonite_matrix_key_map, + .keymap_size = ARRAY_SIZE(zylonite_matrix_key_map), +}; + static struct pxa27x_keypad_platform_data zylonite_keypad_info = { .matrix_key_rows = 8, .matrix_key_cols = 8, - .matrix_key_map = zylonite_matrix_key_map, - .matrix_key_map_size = ARRAY_SIZE(zylonite_matrix_key_map), + .matrix_keymap_data = &zylonite_matrix_keymap_data, .enable_rotary0 = 1, .rotary0_up_key = KEY_UP, diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 37c366623fc0..706e11bb6a32 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -451,6 +451,7 @@ config KEYBOARD_OPENCORES config KEYBOARD_PXA27x tristate "PXA27x/PXA3xx keypad support" depends on PXA27x || PXA3xx || ARCH_MMP + select INPUT_MATRIXKMAP help Enable support for PXA27x/PXA3xx keypad controller. diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index b674e7aca404..5b2d8764dd37 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -118,25 +118,30 @@ struct pxa27x_keypad { unsigned int direct_key_mask; }; -static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) +static int pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) { struct pxa27x_keypad_platform_data *pdata = keypad->pdata; struct input_dev *input_dev = keypad->input_dev; + const struct matrix_keymap_data *keymap_data = + pdata ? pdata->matrix_keymap_data : NULL; unsigned short keycode; int i; + int error; - for (i = 0; i < pdata->matrix_key_map_size; i++) { - unsigned int key = pdata->matrix_key_map[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned int scancode = MATRIX_SCAN_CODE(row, col, - MATRIX_ROW_SHIFT); + error = matrix_keypad_build_keymap(keymap_data, NULL, + pdata->matrix_key_rows, + pdata->matrix_key_cols, + keypad->keycodes, input_dev); + if (error) + return error; - keycode = KEY_VAL(key); - keypad->keycodes[scancode] = keycode; - __set_bit(keycode, input_dev->keybit); - } + /* + * The keycodes may not only include matrix keys but also the direct + * or rotary keys. + */ + input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); + /* For direct keys. */ for (i = 0; i < pdata->direct_key_num; i++) { keycode = pdata->direct_key_map[i]; keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = keycode; @@ -178,6 +183,8 @@ static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) } __clear_bit(KEY_RESERVED, input_dev->keybit); + + return 0; } static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) @@ -555,7 +562,11 @@ static int pxa27x_keypad_probe(struct platform_device *pdev) input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); input_set_capability(input_dev, EV_MSC, MSC_SCAN); - pxa27x_keypad_build_keycode(keypad); + error = pxa27x_keypad_build_keycode(keypad); + if (error) { + dev_err(&pdev->dev, "failed to build keycode\n"); + goto failed_put_clk; + } if ((pdata->enable_rotary0 && keypad->rotary_rel_code[0] != -1) || (pdata->enable_rotary1 && keypad->rotary_rel_code[1] != -1)) { diff --git a/include/linux/platform_data/keypad-pxa27x.h b/include/linux/platform_data/keypad-pxa27x.h index 5ce8d5e6ea51..24625569d16d 100644 --- a/include/linux/platform_data/keypad-pxa27x.h +++ b/include/linux/platform_data/keypad-pxa27x.h @@ -36,10 +36,9 @@ struct pxa27x_keypad_platform_data { /* code map for the matrix keys */ + const struct matrix_keymap_data *matrix_keymap_data; unsigned int matrix_key_rows; unsigned int matrix_key_cols; - unsigned int *matrix_key_map; - int matrix_key_map_size; /* direct keys */ int direct_key_num; -- cgit v1.3-18-gd494 From 99b82abb0a35b07310ea6334257829af168c8e08 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 13 Jun 2013 18:54:44 +0200 Subject: pwm: Add Renesas TPU PWM driver The Timer Pulse Unit (TPU) is a 4-channels 16-bit timer used to generate waveforms. This driver exposes PWM functions through the PWM API for other drivers to use. The code is loosely based on the leds-renesas-tpu driver by Magnus Damm and the TPU PWM driver shipped in the Armadillo EVA 800 kernel sources. Signed-off-by: Laurent Pinchart Signed-off-by: Axel Lin Tested-by: Simon Horman Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 10 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-renesas-tpu.c | 475 ++++++++++++++++++++++++++ include/linux/platform_data/pwm-renesas-tpu.h | 16 + 4 files changed, 502 insertions(+) create mode 100644 drivers/pwm/pwm-renesas-tpu.c create mode 100644 include/linux/platform_data/pwm-renesas-tpu.h (limited to 'include/linux/platform_data') diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 406a4d94ddb9..75840b5cea6d 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -128,6 +128,16 @@ config PWM_PXA To compile this driver as a module, choose M here: the module will be called pwm-pxa. +config PWM_RENESAS_TPU + tristate "Renesas TPU PWM support" + depends on ARCH_SHMOBILE + help + This driver exposes the Timer Pulse Unit (TPU) PWM controller found + in Renesas chips through the PWM API. + + To compile this driver as a module, choose M here: the module + will be called pwm-renesas-tpu. + config PWM_SAMSUNG tristate "Samsung PWM support" depends on PLAT_SAMSUNG diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 859692224044..77a8c185c5b2 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o +obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c new file mode 100644 index 000000000000..96e0cc488a4e --- /dev/null +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -0,0 +1,475 @@ +/* + * R-Mobile TPU PWM driver + * + * Copyright (C) 2012 Renesas Solutions Corp. + * + * 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TPU_TSTR 0x00 /* Timer start register (shared) */ + +#define TPU_TCRn 0x00 /* Timer control register */ +#define TPU_TCR_CCLR_NONE (0 << 5) +#define TPU_TCR_CCLR_TGRA (1 << 5) +#define TPU_TCR_CCLR_TGRB (2 << 5) +#define TPU_TCR_CCLR_TGRC (5 << 5) +#define TPU_TCR_CCLR_TGRD (6 << 5) +#define TPU_TCR_CKEG_RISING (0 << 3) +#define TPU_TCR_CKEG_FALLING (1 << 3) +#define TPU_TCR_CKEG_BOTH (2 << 3) +#define TPU_TMDRn 0x04 /* Timer mode register */ +#define TPU_TMDR_BFWT (1 << 6) +#define TPU_TMDR_BFB (1 << 5) +#define TPU_TMDR_BFA (1 << 4) +#define TPU_TMDR_MD_NORMAL (0 << 0) +#define TPU_TMDR_MD_PWM (2 << 0) +#define TPU_TIORn 0x08 /* Timer I/O control register */ +#define TPU_TIOR_IOA_0 (0 << 0) +#define TPU_TIOR_IOA_0_CLR (1 << 0) +#define TPU_TIOR_IOA_0_SET (2 << 0) +#define TPU_TIOR_IOA_0_TOGGLE (3 << 0) +#define TPU_TIOR_IOA_1 (4 << 0) +#define TPU_TIOR_IOA_1_CLR (5 << 0) +#define TPU_TIOR_IOA_1_SET (6 << 0) +#define TPU_TIOR_IOA_1_TOGGLE (7 << 0) +#define TPU_TIERn 0x0c /* Timer interrupt enable register */ +#define TPU_TSRn 0x10 /* Timer status register */ +#define TPU_TCNTn 0x14 /* Timer counter */ +#define TPU_TGRAn 0x18 /* Timer general register A */ +#define TPU_TGRBn 0x1c /* Timer general register B */ +#define TPU_TGRCn 0x20 /* Timer general register C */ +#define TPU_TGRDn 0x24 /* Timer general register D */ + +#define TPU_CHANNEL_OFFSET 0x10 +#define TPU_CHANNEL_SIZE 0x40 + +enum tpu_pin_state { + TPU_PIN_INACTIVE, /* Pin is driven inactive */ + TPU_PIN_PWM, /* Pin is driven by PWM */ + TPU_PIN_ACTIVE, /* Pin is driven active */ +}; + +struct tpu_device; + +struct tpu_pwm_device { + bool timer_on; /* Whether the timer is running */ + + struct tpu_device *tpu; + unsigned int channel; /* Channel number in the TPU */ + + enum pwm_polarity polarity; + unsigned int prescaler; + u16 period; + u16 duty; +}; + +struct tpu_device { + struct platform_device *pdev; + struct tpu_pwm_platform_data *pdata; + struct pwm_chip chip; + spinlock_t lock; + + void __iomem *base; + struct clk *clk; +}; + +#define to_tpu_device(c) container_of(c, struct tpu_device, chip) + +static void tpu_pwm_write(struct tpu_pwm_device *pwm, int reg_nr, u16 value) +{ + void __iomem *base = pwm->tpu->base + TPU_CHANNEL_OFFSET + + pwm->channel * TPU_CHANNEL_SIZE; + + iowrite16(value, base + reg_nr); +} + +static void tpu_pwm_set_pin(struct tpu_pwm_device *pwm, + enum tpu_pin_state state) +{ + static const char * const states[] = { "inactive", "PWM", "active" }; + + dev_dbg(&pwm->tpu->pdev->dev, "%u: configuring pin as %s\n", + pwm->channel, states[state]); + + switch (state) { + case TPU_PIN_INACTIVE: + tpu_pwm_write(pwm, TPU_TIORn, + pwm->polarity == PWM_POLARITY_INVERSED ? + TPU_TIOR_IOA_1 : TPU_TIOR_IOA_0); + break; + case TPU_PIN_PWM: + tpu_pwm_write(pwm, TPU_TIORn, + pwm->polarity == PWM_POLARITY_INVERSED ? + TPU_TIOR_IOA_0_SET : TPU_TIOR_IOA_1_CLR); + break; + case TPU_PIN_ACTIVE: + tpu_pwm_write(pwm, TPU_TIORn, + pwm->polarity == PWM_POLARITY_INVERSED ? + TPU_TIOR_IOA_0 : TPU_TIOR_IOA_1); + break; + } +} + +static void tpu_pwm_start_stop(struct tpu_pwm_device *pwm, int start) +{ + unsigned long flags; + u16 value; + + spin_lock_irqsave(&pwm->tpu->lock, flags); + value = ioread16(pwm->tpu->base + TPU_TSTR); + + if (start) + value |= 1 << pwm->channel; + else + value &= ~(1 << pwm->channel); + + iowrite16(value, pwm->tpu->base + TPU_TSTR); + spin_unlock_irqrestore(&pwm->tpu->lock, flags); +} + +static int tpu_pwm_timer_start(struct tpu_pwm_device *pwm) +{ + int ret; + + if (!pwm->timer_on) { + /* Wake up device and enable clock. */ + pm_runtime_get_sync(&pwm->tpu->pdev->dev); + ret = clk_prepare_enable(pwm->tpu->clk); + if (ret) { + dev_err(&pwm->tpu->pdev->dev, "cannot enable clock\n"); + return ret; + } + pwm->timer_on = true; + } + + /* + * Make sure the channel is stopped, as we need to reconfigure it + * completely. First drive the pin to the inactive state to avoid + * glitches. + */ + tpu_pwm_set_pin(pwm, TPU_PIN_INACTIVE); + tpu_pwm_start_stop(pwm, false); + + /* + * - Clear TCNT on TGRB match + * - Count on rising edge + * - Set prescaler + * - Output 0 until TGRA, output 1 until TGRB (active low polarity) + * - Output 1 until TGRA, output 0 until TGRB (active high polarity + * - PWM mode + */ + tpu_pwm_write(pwm, TPU_TCRn, TPU_TCR_CCLR_TGRB | TPU_TCR_CKEG_RISING | + pwm->prescaler); + tpu_pwm_write(pwm, TPU_TMDRn, TPU_TMDR_MD_PWM); + tpu_pwm_set_pin(pwm, TPU_PIN_PWM); + tpu_pwm_write(pwm, TPU_TGRAn, pwm->duty); + tpu_pwm_write(pwm, TPU_TGRBn, pwm->period); + + dev_dbg(&pwm->tpu->pdev->dev, "%u: TGRA 0x%04x TGRB 0x%04x\n", + pwm->channel, pwm->duty, pwm->period); + + /* Start the channel. */ + tpu_pwm_start_stop(pwm, true); + + return 0; +} + +static void tpu_pwm_timer_stop(struct tpu_pwm_device *pwm) +{ + if (!pwm->timer_on) + return; + + /* Disable channel. */ + tpu_pwm_start_stop(pwm, false); + + /* Stop clock and mark device as idle. */ + clk_disable_unprepare(pwm->tpu->clk); + pm_runtime_put(&pwm->tpu->pdev->dev); + + pwm->timer_on = false; +} + +/* ----------------------------------------------------------------------------- + * PWM API + */ + +static int tpu_pwm_request(struct pwm_chip *chip, struct pwm_device *_pwm) +{ + struct tpu_device *tpu = to_tpu_device(chip); + struct tpu_pwm_device *pwm; + + if (_pwm->hwpwm >= TPU_CHANNEL_MAX) + return -EINVAL; + + pwm = kzalloc(sizeof(*pwm), GFP_KERNEL); + if (pwm == NULL) + return -ENOMEM; + + pwm->tpu = tpu; + pwm->channel = _pwm->hwpwm; + pwm->polarity = tpu->pdata ? tpu->pdata->channels[pwm->channel].polarity + : PWM_POLARITY_NORMAL; + pwm->prescaler = 0; + pwm->period = 0; + pwm->duty = 0; + + pwm->timer_on = false; + + pwm_set_chip_data(_pwm, pwm); + + return 0; +} + +static void tpu_pwm_free(struct pwm_chip *chip, struct pwm_device *_pwm) +{ + struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); + + tpu_pwm_timer_stop(pwm); + kfree(pwm); +} + +static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *_pwm, + int duty_ns, int period_ns) +{ + static const unsigned int prescalers[] = { 1, 4, 16, 64 }; + struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); + struct tpu_device *tpu = to_tpu_device(chip); + unsigned int prescaler; + bool duty_only = false; + u32 clk_rate; + u32 period; + u32 duty; + int ret; + + /* + * Pick a prescaler to avoid overflowing the counter. + * TODO: Pick the highest acceptable prescaler. + */ + clk_rate = clk_get_rate(tpu->clk); + + for (prescaler = 0; prescaler < ARRAY_SIZE(prescalers); ++prescaler) { + period = clk_rate / prescalers[prescaler] + / (NSEC_PER_SEC / period_ns); + if (period <= 0xffff) + break; + } + + if (prescaler == ARRAY_SIZE(prescalers) || period == 0) { + dev_err(&tpu->pdev->dev, "clock rate mismatch\n"); + return -ENOTSUPP; + } + + if (duty_ns) { + duty = clk_rate / prescalers[prescaler] + / (NSEC_PER_SEC / duty_ns); + if (duty > period) + return -EINVAL; + } else { + duty = 0; + } + + dev_dbg(&tpu->pdev->dev, + "rate %u, prescaler %u, period %u, duty %u\n", + clk_rate, prescalers[prescaler], period, duty); + + if (pwm->prescaler == prescaler && pwm->period == period) + duty_only = true; + + pwm->prescaler = prescaler; + pwm->period = period; + pwm->duty = duty; + + /* If the channel is disabled we're done. */ + if (!test_bit(PWMF_ENABLED, &_pwm->flags)) + return 0; + + if (duty_only && pwm->timer_on) { + /* + * If only the duty cycle changed and the timer is already + * running, there's no need to reconfigure it completely, Just + * modify the duty cycle. + */ + tpu_pwm_write(pwm, TPU_TGRAn, pwm->duty); + dev_dbg(&tpu->pdev->dev, "%u: TGRA 0x%04x\n", pwm->channel, + pwm->duty); + } else { + /* Otherwise perform a full reconfiguration. */ + ret = tpu_pwm_timer_start(pwm); + if (ret < 0) + return ret; + } + + if (duty == 0 || duty == period) { + /* + * To avoid running the timer when not strictly required, handle + * 0% and 100% duty cycles as fixed levels and stop the timer. + */ + tpu_pwm_set_pin(pwm, duty ? TPU_PIN_ACTIVE : TPU_PIN_INACTIVE); + tpu_pwm_timer_stop(pwm); + } + + return 0; +} + +static int tpu_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *_pwm, + enum pwm_polarity polarity) +{ + struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); + + pwm->polarity = polarity; + + return 0; +} + +static int tpu_pwm_enable(struct pwm_chip *chip, struct pwm_device *_pwm) +{ + struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); + int ret; + + ret = tpu_pwm_timer_start(pwm); + if (ret < 0) + return ret; + + /* + * To avoid running the timer when not strictly required, handle 0% and + * 100% duty cycles as fixed levels and stop the timer. + */ + if (pwm->duty == 0 || pwm->duty == pwm->period) { + tpu_pwm_set_pin(pwm, pwm->duty ? + TPU_PIN_ACTIVE : TPU_PIN_INACTIVE); + tpu_pwm_timer_stop(pwm); + } + + return 0; +} + +static void tpu_pwm_disable(struct pwm_chip *chip, struct pwm_device *_pwm) +{ + struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); + + /* The timer must be running to modify the pin output configuration. */ + tpu_pwm_timer_start(pwm); + tpu_pwm_set_pin(pwm, TPU_PIN_INACTIVE); + tpu_pwm_timer_stop(pwm); +} + +static const struct pwm_ops tpu_pwm_ops = { + .request = tpu_pwm_request, + .free = tpu_pwm_free, + .config = tpu_pwm_config, + .set_polarity = tpu_pwm_set_polarity, + .enable = tpu_pwm_enable, + .disable = tpu_pwm_disable, + .owner = THIS_MODULE, +}; + +/* ----------------------------------------------------------------------------- + * Probe and remove + */ + +static int tpu_probe(struct platform_device *pdev) +{ + struct tpu_device *tpu; + struct resource *res; + int ret; + + tpu = devm_kzalloc(&pdev->dev, sizeof(*tpu), GFP_KERNEL); + if (tpu == NULL) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + return -ENOMEM; + } + + tpu->pdata = pdev->dev.platform_data; + + /* Map memory, get clock and pin control. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + return -ENXIO; + } + + tpu->base = devm_ioremap_resource(&pdev->dev, res); + if (tpu->base == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + return -ENXIO; + } + + tpu->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(tpu->clk)) { + dev_err(&pdev->dev, "cannot get clock\n"); + return PTR_ERR(tpu->clk); + } + + /* Initialize and register the device. */ + platform_set_drvdata(pdev, tpu); + + spin_lock_init(&tpu->lock); + tpu->pdev = pdev; + + tpu->chip.dev = &pdev->dev; + tpu->chip.ops = &tpu_pwm_ops; + tpu->chip.base = -1; + tpu->chip.npwm = TPU_CHANNEL_MAX; + + ret = pwmchip_add(&tpu->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register PWM chip\n"); + return ret; + } + + dev_info(&pdev->dev, "TPU PWM %d registered\n", tpu->pdev->id); + + pm_runtime_enable(&pdev->dev); + + return 0; +} + +static int tpu_remove(struct platform_device *pdev) +{ + struct tpu_device *tpu = platform_get_drvdata(pdev); + int ret; + + ret = pwmchip_remove(&tpu->chip); + if (ret) + return ret; + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver tpu_driver = { + .probe = tpu_probe, + .remove = tpu_remove, + .driver = { + .name = "renesas-tpu-pwm", + .owner = THIS_MODULE, + } +}; + +module_platform_driver(tpu_driver); + +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("Renesas TPU PWM Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/pwm-renesas-tpu.h b/include/linux/platform_data/pwm-renesas-tpu.h new file mode 100644 index 000000000000..a7220b10ddab --- /dev/null +++ b/include/linux/platform_data/pwm-renesas-tpu.h @@ -0,0 +1,16 @@ +#ifndef __PWM_RENESAS_TPU_H__ +#define __PWM_RENESAS_TPU_H__ + +#include + +#define TPU_CHANNEL_MAX 4 + +struct tpu_pwm_channel_data { + enum pwm_polarity polarity; +}; + +struct tpu_pwm_platform_data { + struct tpu_pwm_channel_data channels[TPU_CHANNEL_MAX]; +}; + +#endif /* __PWM_RENESAS_TPU_H__ */ -- cgit v1.3-18-gd494 From 6cba4355066bda19f14d4da66b8abbca0ffdfd59 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 20 Jun 2013 16:06:38 -0500 Subject: ARM: edma: Add DT and runtime PM support to the private EDMA API Adds support for parsing the TI EDMA DT data into the required EDMA private API platform data. Enables runtime PM support to initialize the EDMA hwmod. Enables build on OMAP. Changes by Joel: * Setup default one-to-one mapping for queue_priority and queue_tc mapping as discussed in [1]. * Split out xbar stuff to separate patch. [1] * Dropped unused DT helper to convert to array * Fixed dangling pointer issue with Sekhar's changes [1] https://patchwork.kernel.org/patch/2226761/ Signed-off-by: Matt Porter [nsekhar@ti.com: fix checkpatch errors, build breakages. Introduce edma_setup_info_from_dt() as part of that effort] Signed-off-by: Joel A Fernandes Acked-by: Arnd Bergmann Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 186 +++++++++++++++++++++++++++--- arch/arm/mach-davinci/devices-da8xx.c | 8 +- arch/arm/mach-davinci/devices-tnetv107x.c | 4 +- arch/arm/mach-davinci/dm355.c | 4 +- arch/arm/mach-davinci/dm365.c | 4 +- arch/arm/mach-davinci/dm644x.c | 4 +- arch/arm/mach-davinci/dm646x.c | 4 +- include/linux/platform_data/edma.h | 4 +- 8 files changed, 189 insertions(+), 29 deletions(-) (limited to 'include/linux/platform_data') diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 7658874cc3d5..5183a310657c 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -25,6 +25,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include @@ -1369,13 +1376,110 @@ void edma_clear_event(unsigned channel) } EXPORT_SYMBOL(edma_clear_event); -/*-----------------------------------------------------------------------*/ +#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DMADEVICES) + +static int edma_of_parse_dt(struct device *dev, + struct device_node *node, + struct edma_soc_info *pdata) +{ + int ret = 0, i; + u32 value; + struct edma_rsv_info *rsv_info; + s8 (*queue_tc_map)[2], (*queue_priority_map)[2]; + + memset(pdata, 0, sizeof(struct edma_soc_info)); + + ret = of_property_read_u32(node, "dma-channels", &value); + if (ret < 0) + return ret; + pdata->n_channel = value; + + ret = of_property_read_u32(node, "ti,edma-regions", &value); + if (ret < 0) + return ret; + pdata->n_region = value; + + ret = of_property_read_u32(node, "ti,edma-slots", &value); + if (ret < 0) + return ret; + pdata->n_slot = value; + + pdata->n_cc = 1; + + rsv_info = devm_kzalloc(dev, sizeof(struct edma_rsv_info), GFP_KERNEL); + if (!rsv_info) + return -ENOMEM; + pdata->rsv = rsv_info; + + queue_tc_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL); + if (!queue_tc_map) + return -ENOMEM; + + for (i = 0; i < 3; i++) { + queue_tc_map[i][0] = i; + queue_tc_map[i][1] = i; + } + queue_tc_map[i][0] = -1; + queue_tc_map[i][1] = -1; + + pdata->queue_tc_mapping = queue_tc_map; + + queue_priority_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL); + if (!queue_priority_map) + return -ENOMEM; + + for (i = 0; i < 3; i++) { + queue_priority_map[i][0] = i; + queue_priority_map[i][1] = i; + } + queue_priority_map[i][0] = -1; + queue_priority_map[i][1] = -1; + + pdata->queue_priority_mapping = queue_priority_map; + + pdata->default_queue = 0; -static int __init edma_probe(struct platform_device *pdev) + return ret; +} + +static struct of_dma_filter_info edma_filter_info = { + .filter_fn = edma_filter_fn, +}; + +static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev, + struct device_node *node) +{ + struct edma_soc_info *info; + int ret; + + info = devm_kzalloc(dev, sizeof(struct edma_soc_info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + ret = edma_of_parse_dt(dev, node, info); + if (ret) + return ERR_PTR(ret); + + dma_cap_set(DMA_SLAVE, edma_filter_info.dma_cap); + of_dma_controller_register(dev->of_node, of_dma_simple_xlate, + &edma_filter_info); + + return info; +} +#else +static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev, + struct device_node *node) +{ + return ERR_PTR(-ENOSYS); +} +#endif + +static int edma_probe(struct platform_device *pdev) { struct edma_soc_info **info = pdev->dev.platform_data; - const s8 (*queue_priority_mapping)[2]; - const s8 (*queue_tc_mapping)[2]; + struct edma_soc_info *ninfo[EDMA_MAX_CC] = {NULL}; + s8 (*queue_priority_mapping)[2]; + s8 (*queue_tc_mapping)[2]; int i, j, off, ln, found = 0; int status = -1; const s16 (*rsv_chans)[2]; @@ -1383,17 +1487,56 @@ static int __init edma_probe(struct platform_device *pdev) int irq[EDMA_MAX_CC] = {0, 0}; int err_irq[EDMA_MAX_CC] = {0, 0}; struct resource *r[EDMA_MAX_CC] = {NULL}; + struct resource res[EDMA_MAX_CC]; char res_name[10]; char irq_name[10]; + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + int ret; + + if (node) { + /* Check if this is a second instance registered */ + if (arch_num_cc) { + dev_err(dev, "only one EDMA instance is supported via DT\n"); + return -ENODEV; + } + + ninfo[0] = edma_setup_info_from_dt(dev, node); + if (IS_ERR(ninfo[0])) { + dev_err(dev, "failed to get DT data\n"); + return PTR_ERR(ninfo[0]); + } + + info = ninfo; + } if (!info) return -ENODEV; + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync() failed\n"); + return ret; + } + for (j = 0; j < EDMA_MAX_CC; j++) { - sprintf(res_name, "edma_cc%d", j); - r[j] = platform_get_resource_byname(pdev, IORESOURCE_MEM, + if (!info[j]) { + if (!found) + return -ENODEV; + break; + } + if (node) { + ret = of_address_to_resource(node, j, &res[j]); + if (!ret) + r[j] = &res[j]; + } else { + sprintf(res_name, "edma_cc%d", j); + r[j] = platform_get_resource_byname(pdev, + IORESOURCE_MEM, res_name); - if (!r[j] || !info[j]) { + } + if (!r[j]) { if (found) break; else @@ -1440,7 +1583,7 @@ static int __init edma_probe(struct platform_device *pdev) off = rsv_chans[i][0]; ln = rsv_chans[i][1]; clear_bits(off, ln, - edma_cc[j]->edma_unused); + edma_cc[j]->edma_unused); } } @@ -1456,8 +1599,13 @@ static int __init edma_probe(struct platform_device *pdev) } } - sprintf(irq_name, "edma%d", j); - irq[j] = platform_get_irq_byname(pdev, irq_name); + + if (node) { + irq[j] = irq_of_parse_and_map(node, 0); + } else { + sprintf(irq_name, "edma%d", j); + irq[j] = platform_get_irq_byname(pdev, irq_name); + } edma_cc[j]->irq_res_start = irq[j]; status = devm_request_irq(&pdev->dev, irq[j], dma_irq_handler, 0, "edma", @@ -1469,8 +1617,12 @@ static int __init edma_probe(struct platform_device *pdev) return status; } - sprintf(irq_name, "edma%d_err", j); - err_irq[j] = platform_get_irq_byname(pdev, irq_name); + if (node) { + err_irq[j] = irq_of_parse_and_map(node, 2); + } else { + sprintf(irq_name, "edma%d_err", j); + err_irq[j] = platform_get_irq_byname(pdev, irq_name); + } edma_cc[j]->irq_res_end = err_irq[j]; status = devm_request_irq(&pdev->dev, err_irq[j], dma_ccerr_handler, 0, @@ -1516,9 +1668,17 @@ static int __init edma_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id edma_of_ids[] = { + { .compatible = "ti,edma3", }, + {} +}; static struct platform_driver edma_driver = { - .driver.name = "edma", + .driver = { + .name = "edma", + .of_match_table = edma_of_ids, + }, + .probe = edma_probe, }; static int __init edma_init(void) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index bf572525175d..eb254fe861ac 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -105,27 +105,27 @@ struct platform_device da8xx_serial_device = { }, }; -static const s8 da8xx_queue_tc_mapping[][2] = { +static s8 da8xx_queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, {1, 1}, {-1, -1} }; -static const s8 da8xx_queue_priority_mapping[][2] = { +static s8 da8xx_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, {1, 7}, {-1, -1} }; -static const s8 da850_queue_tc_mapping[][2] = { +static s8 da850_queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, {-1, -1} }; -static const s8 da850_queue_priority_mapping[][2] = { +static s8 da850_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, {-1, -1} diff --git a/arch/arm/mach-davinci/devices-tnetv107x.c b/arch/arm/mach-davinci/devices-tnetv107x.c index 612a0856e9c5..128cb9ae80f4 100644 --- a/arch/arm/mach-davinci/devices-tnetv107x.c +++ b/arch/arm/mach-davinci/devices-tnetv107x.c @@ -58,14 +58,14 @@ #define TNETV107X_DMACH_SDIO1_RX 28 #define TNETV107X_DMACH_SDIO1_TX 29 -static const s8 edma_tc_mapping[][2] = { +static s8 edma_tc_mapping[][2] = { /* event queue no TC no */ { 0, 0 }, { 1, 1 }, { -1, -1 } }; -static const s8 edma_priority_mapping[][2] = { +static s8 edma_priority_mapping[][2] = { /* event queue no Prio */ { 0, 3 }, { 1, 7 }, diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 526cf7d06d0e..42ef53f62c6c 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -569,7 +569,7 @@ static u8 dm355_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ -static const s8 +static s8 queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, @@ -577,7 +577,7 @@ queue_tc_mapping[][2] = { {-1, -1}, }; -static const s8 +static s8 queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index c4b741173c06..fa7af5eda52d 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -826,7 +826,7 @@ static u8 dm365_default_priorities[DAVINCI_N_AINTC_IRQ] = { }; /* Four Transfer Controllers on DM365 */ -static const s8 +static s8 dm365_queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, @@ -836,7 +836,7 @@ dm365_queue_tc_mapping[][2] = { {-1, -1}, }; -static const s8 +static s8 dm365_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 7}, diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index dd156d58fe64..a49d18246fe9 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -497,7 +497,7 @@ static u8 dm644x_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ -static const s8 +static s8 queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, @@ -505,7 +505,7 @@ queue_tc_mapping[][2] = { {-1, -1}, }; -static const s8 +static s8 queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 6d52a321a8cf..d1259e80141b 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -531,7 +531,7 @@ static u8 dm646x_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ /* Four Transfer Controllers on DM646x */ -static const s8 +static s8 dm646x_queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, @@ -541,7 +541,7 @@ dm646x_queue_tc_mapping[][2] = { {-1, -1}, }; -static const s8 +static s8 dm646x_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 4}, diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 2344ea2675ad..317f2beeaece 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -175,8 +175,8 @@ struct edma_soc_info { /* Resource reservation for other cores */ struct edma_rsv_info *rsv; - const s8 (*queue_tc_mapping)[2]; - const s8 (*queue_priority_mapping)[2]; + s8 (*queue_tc_mapping)[2]; + s8 (*queue_priority_mapping)[2]; }; #endif -- cgit v1.3-18-gd494 From 2646a0e52b65a5ea3d108794611f95df1a6cb409 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 20 Jun 2013 16:06:39 -0500 Subject: ARM: edma: Add EDMA crossbar event mux support EDMA supports a cross bar which provides ability to mux additional events into physical channels present in the channel controller. This is required when the number of events present in the system are more than number of available physical channels. Changes by Joel: * Split EDMA xbar support out of original EDMA DT parsing patch to keep it easier for review. * Rewrite shift and offset calculation. Suggested-by: Sekhar Nori Suggested by: Andy Shevchenko Signed-off-by: Joel A Fernandes Acked-by: Arnd Bergmann [nsekhar@ti.com: fix checkpatch errors and a minor coding improvement] Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 78 ++++++++++++++++++++++++++++++++++++++ include/linux/platform_data/edma.h | 1 + 2 files changed, 79 insertions(+) (limited to 'include/linux/platform_data') diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 5183a310657c..a432e6c1dac1 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1378,12 +1378,76 @@ EXPORT_SYMBOL(edma_clear_event); #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DMADEVICES) +static int edma_of_read_u32_to_s16_array(const struct device_node *np, + const char *propname, s16 *out_values, + size_t sz) +{ + int ret; + + ret = of_property_read_u16_array(np, propname, out_values, sz); + if (ret) + return ret; + + /* Terminate it */ + *out_values++ = -1; + *out_values++ = -1; + + return 0; +} + +static int edma_xbar_event_map(struct device *dev, + struct device_node *node, + struct edma_soc_info *pdata, int len) +{ + int ret, i; + struct resource res; + void __iomem *xbar; + const s16 (*xbar_chans)[2]; + u32 shift, offset, mux; + + xbar_chans = devm_kzalloc(dev, + len/sizeof(s16) + 2*sizeof(s16), + GFP_KERNEL); + if (!xbar_chans) + return -ENOMEM; + + ret = of_address_to_resource(node, 1, &res); + if (ret) + return -EIO; + + xbar = devm_ioremap(dev, res.start, resource_size(&res)); + if (!xbar) + return -ENOMEM; + + ret = edma_of_read_u32_to_s16_array(node, + "ti,edma-xbar-event-map", + (s16 *)xbar_chans, + len/sizeof(u32)); + if (ret) + return -EIO; + + for (i = 0; xbar_chans[i][0] != -1; i++) { + shift = (xbar_chans[i][1] & 0x03) << 3; + offset = xbar_chans[i][1] & 0xfffffffc; + mux = readl(xbar + offset); + mux &= ~(0xff << shift); + mux |= xbar_chans[i][0] << shift; + writel(mux, (xbar + offset)); + } + + pdata->xbar_chans = xbar_chans; + + return 0; +} + static int edma_of_parse_dt(struct device *dev, struct device_node *node, struct edma_soc_info *pdata) { int ret = 0, i; u32 value; + struct property *prop; + size_t sz; struct edma_rsv_info *rsv_info; s8 (*queue_tc_map)[2], (*queue_priority_map)[2]; @@ -1439,6 +1503,10 @@ static int edma_of_parse_dt(struct device *dev, pdata->default_queue = 0; + prop = of_find_property(node, "ti,edma-xbar-event-map", &sz); + if (prop) + ret = edma_xbar_event_map(dev, node, pdata, sz); + return ret; } @@ -1484,6 +1552,7 @@ static int edma_probe(struct platform_device *pdev) int status = -1; const s16 (*rsv_chans)[2]; const s16 (*rsv_slots)[2]; + const s16 (*xbar_chans)[2]; int irq[EDMA_MAX_CC] = {0, 0}; int err_irq[EDMA_MAX_CC] = {0, 0}; struct resource *r[EDMA_MAX_CC] = {NULL}; @@ -1599,6 +1668,15 @@ static int edma_probe(struct platform_device *pdev) } } + /* Clear the xbar mapped channels in unused list */ + xbar_chans = info[j]->xbar_chans; + if (xbar_chans) { + for (i = 0; xbar_chans[i][1] != -1; i++) { + off = xbar_chans[i][1]; + clear_bits(off, 1, + edma_cc[j]->edma_unused); + } + } if (node) { irq[j] = irq_of_parse_and_map(node, 0); diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 317f2beeaece..57300fd7cc03 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -177,6 +177,7 @@ struct edma_soc_info { s8 (*queue_tc_mapping)[2]; s8 (*queue_priority_mapping)[2]; + const s16 (*xbar_chans)[2]; }; #endif -- cgit v1.3-18-gd494 From 4bf8e1962f91eed5dbee168d2348983dda0a518f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 19 Jun 2013 13:54:11 +0200 Subject: drm: Renesas R-Car Display Unit DRM driver The R-Car Display Unit (DU) DRM driver supports both superposition processors and all eight planes in RGB and YUV formats with alpha blending. Only VGA and LVDS encoders and connectors are currently supported. Signed-off-by: Laurent Pinchart Signed-off-by: Dave Airlie --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/rcar-du/Kconfig | 9 + drivers/gpu/drm/rcar-du/Makefile | 8 + drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 595 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 50 +++ drivers/gpu/drm/rcar-du/rcar_du_drv.c | 325 +++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_drv.h | 66 ++++ drivers/gpu/drm/rcar-du/rcar_du_kms.c | 245 +++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_kms.h | 59 ++++ drivers/gpu/drm/rcar-du/rcar_du_lvds.c | 216 ++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_lvds.h | 24 ++ drivers/gpu/drm/rcar-du/rcar_du_plane.c | 507 +++++++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_plane.h | 67 ++++ drivers/gpu/drm/rcar-du/rcar_du_regs.h | 445 ++++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_vga.c | 149 ++++++++ drivers/gpu/drm/rcar-du/rcar_du_vga.h | 24 ++ include/linux/platform_data/rcar-du.h | 54 +++ 18 files changed, 2846 insertions(+) create mode 100644 drivers/gpu/drm/rcar-du/Kconfig create mode 100644 drivers/gpu/drm/rcar-du/Makefile create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_crtc.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_crtc.h create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_drv.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_drv.h create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_kms.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_kms.h create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvds.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvds.h create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_plane.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_plane.h create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_regs.h create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vga.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vga.h create mode 100644 include/linux/platform_data/rcar-du.h (limited to 'include/linux/platform_data') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index b16c50ee769c..71ca63b79a4f 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -213,6 +213,8 @@ source "drivers/gpu/drm/mgag200/Kconfig" source "drivers/gpu/drm/cirrus/Kconfig" +source "drivers/gpu/drm/rcar-du/Kconfig" + source "drivers/gpu/drm/shmobile/Kconfig" source "drivers/gpu/drm/omapdrm/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 1ecbe5b7312d..801bcafa3028 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/ obj-$(CONFIG_DRM_GMA500) += gma500/ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ +obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-$(CONFIG_DRM_TILCDC) += tilcdc/ diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig new file mode 100644 index 000000000000..72887df8dd76 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/Kconfig @@ -0,0 +1,9 @@ +config DRM_RCAR_DU + tristate "DRM Support for R-Car Display Unit" + depends on DRM && ARM + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + help + Choose this option if you have an R-Car chipset. + If M is selected the module will be called rcar-du-drm. diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile new file mode 100644 index 000000000000..7333c0094015 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/Makefile @@ -0,0 +1,8 @@ +rcar-du-drm-y := rcar_du_crtc.o \ + rcar_du_drv.o \ + rcar_du_kms.o \ + rcar_du_lvds.o \ + rcar_du_plane.o \ + rcar_du_vga.o + +obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c new file mode 100644 index 000000000000..24183fb93592 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -0,0 +1,595 @@ +/* + * rcar_du_crtc.c -- R-Car Display Unit CRTCs + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "rcar_du_crtc.h" +#include "rcar_du_drv.h" +#include "rcar_du_kms.h" +#include "rcar_du_lvds.h" +#include "rcar_du_plane.h" +#include "rcar_du_regs.h" +#include "rcar_du_vga.h" + +#define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) + +static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg) +{ + struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + + return rcar_du_read(rcdu, rcrtc->mmio_offset + reg); +} + +static void rcar_du_crtc_write(struct rcar_du_crtc *rcrtc, u32 reg, u32 data) +{ + struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + + rcar_du_write(rcdu, rcrtc->mmio_offset + reg, data); +} + +static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr) +{ + struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + + rcar_du_write(rcdu, rcrtc->mmio_offset + reg, + rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr); +} + +static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set) +{ + struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + + rcar_du_write(rcdu, rcrtc->mmio_offset + reg, + rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set); +} + +static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg, + u32 clr, u32 set) +{ + struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + u32 value = rcar_du_read(rcdu, rcrtc->mmio_offset + reg); + + rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set); +} + +static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) +{ + struct drm_crtc *crtc = &rcrtc->crtc; + struct rcar_du_device *rcdu = crtc->dev->dev_private; + const struct drm_display_mode *mode = &crtc->mode; + unsigned long clk; + u32 value; + u32 div; + + /* Dot clock */ + clk = clk_get_rate(rcdu->clock); + div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000); + div = clamp(div, 1U, 64U) - 1; + + rcar_du_write(rcdu, rcrtc->index ? ESCR2 : ESCR, + ESCR_DCLKSEL_CLKS | div); + rcar_du_write(rcdu, rcrtc->index ? OTAR2 : OTAR, 0); + + /* Signal polarities */ + value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL) + | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL) + | DSMR_DIPM_DE; + rcar_du_crtc_write(rcrtc, DSMR, value); + + /* Display timings */ + rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19); + rcar_du_crtc_write(rcrtc, HDER, mode->htotal - mode->hsync_start + + mode->hdisplay - 19); + rcar_du_crtc_write(rcrtc, HSWR, mode->hsync_end - + mode->hsync_start - 1); + rcar_du_crtc_write(rcrtc, HCR, mode->htotal - 1); + + rcar_du_crtc_write(rcrtc, VDSR, mode->vtotal - mode->vsync_end - 2); + rcar_du_crtc_write(rcrtc, VDER, mode->vtotal - mode->vsync_end + + mode->vdisplay - 2); + rcar_du_crtc_write(rcrtc, VSPR, mode->vtotal - mode->vsync_end + + mode->vsync_start - 1); + rcar_du_crtc_write(rcrtc, VCR, mode->vtotal - 1); + + rcar_du_crtc_write(rcrtc, DESR, mode->htotal - mode->hsync_start); + rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay); +} + +static void rcar_du_crtc_set_routing(struct rcar_du_crtc *rcrtc) +{ + struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + u32 dorcr = rcar_du_read(rcdu, DORCR); + + dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK); + + /* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and + * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by + * default. + */ + if (rcrtc->outputs & (1 << 1) && rcrtc->index == 0) + dorcr |= DORCR_PG2D_DS1; + else + dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2; + + rcar_du_write(rcdu, DORCR, dorcr); +} + +static void __rcar_du_start_stop(struct rcar_du_device *rcdu, bool start) +{ + rcar_du_write(rcdu, DSYSR, + (rcar_du_read(rcdu, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) | + (start ? DSYSR_DEN : DSYSR_DRES)); +} + +static void rcar_du_start_stop(struct rcar_du_device *rcdu, bool start) +{ + /* Many of the configuration bits are only updated when the display + * reset (DRES) bit in DSYSR is set to 1, disabling *both* CRTCs. Some + * of those bits could be pre-configured, but others (especially the + * bits related to plane assignment to display timing controllers) need + * to be modified at runtime. + * + * Restart the display controller if a start is requested. Sorry for the + * flicker. It should be possible to move most of the "DRES-update" bits + * setup to driver initialization time and minimize the number of cases + * when the display controller will have to be restarted. + */ + if (start) { + if (rcdu->used_crtcs++ != 0) + __rcar_du_start_stop(rcdu, false); + __rcar_du_start_stop(rcdu, true); + } else { + if (--rcdu->used_crtcs == 0) + __rcar_du_start_stop(rcdu, false); + } +} + +void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output) +{ + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + + /* Store the route from the CRTC output to the DU output. The DU will be + * configured when starting the CRTC. + */ + rcrtc->outputs |= 1 << output; +} + +void rcar_du_crtc_update_planes(struct drm_crtc *crtc) +{ + struct rcar_du_device *rcdu = crtc->dev->dev_private; + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES]; + unsigned int num_planes = 0; + unsigned int prio = 0; + unsigned int i; + u32 dptsr = 0; + u32 dspr = 0; + + for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) { + struct rcar_du_plane *plane = &rcdu->planes.planes[i]; + unsigned int j; + + if (plane->crtc != &rcrtc->crtc || !plane->enabled) + continue; + + /* Insert the plane in the sorted planes array. */ + for (j = num_planes++; j > 0; --j) { + if (planes[j-1]->zpos <= plane->zpos) + break; + planes[j] = planes[j-1]; + } + + planes[j] = plane; + prio += plane->format->planes * 4; + } + + for (i = 0; i < num_planes; ++i) { + struct rcar_du_plane *plane = planes[i]; + unsigned int index = plane->hwindex; + + prio -= 4; + dspr |= (index + 1) << prio; + dptsr |= DPTSR_PnDK(index) | DPTSR_PnTS(index); + + if (plane->format->planes == 2) { + index = (index + 1) % 8; + + prio -= 4; + dspr |= (index + 1) << prio; + dptsr |= DPTSR_PnDK(index) | DPTSR_PnTS(index); + } + } + + /* Select display timing and dot clock generator 2 for planes associated + * with superposition controller 2. + */ + if (rcrtc->index) { + u32 value = rcar_du_read(rcdu, DPTSR); + + /* The DPTSR register is updated when the display controller is + * stopped. We thus need to restart the DU. Once again, sorry + * for the flicker. One way to mitigate the issue would be to + * pre-associate planes with CRTCs (either with a fixed 4/4 + * split, or through a module parameter). Flicker would then + * occur only if we need to break the pre-association. + */ + if (value != dptsr) { + rcar_du_write(rcdu, DPTSR, dptsr); + if (rcdu->used_crtcs) { + __rcar_du_start_stop(rcdu, false); + __rcar_du_start_stop(rcdu, true); + } + } + } + + rcar_du_write(rcdu, rcrtc->index ? DS2PR : DS1PR, dspr); +} + +static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) +{ + struct drm_crtc *crtc = &rcrtc->crtc; + struct rcar_du_device *rcdu = crtc->dev->dev_private; + unsigned int i; + + if (rcrtc->started) + return; + + if (WARN_ON(rcrtc->plane->format == NULL)) + return; + + /* Set display off and background to black */ + rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0)); + rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0)); + + /* Configure display timings and output routing */ + rcar_du_crtc_set_display_timing(rcrtc); + rcar_du_crtc_set_routing(rcrtc); + + mutex_lock(&rcdu->planes.lock); + rcrtc->plane->enabled = true; + rcar_du_crtc_update_planes(crtc); + mutex_unlock(&rcdu->planes.lock); + + /* Setup planes. */ + for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) { + struct rcar_du_plane *plane = &rcdu->planes.planes[i]; + + if (plane->crtc != crtc || !plane->enabled) + continue; + + rcar_du_plane_setup(plane); + } + + /* Select master sync mode. This enables display operation in master + * sync mode (with the HSYNC and VSYNC signals configured as outputs and + * actively driven). + */ + rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_MASTER); + + rcar_du_start_stop(rcdu, true); + + rcrtc->started = true; +} + +static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) +{ + struct drm_crtc *crtc = &rcrtc->crtc; + struct rcar_du_device *rcdu = crtc->dev->dev_private; + + if (!rcrtc->started) + return; + + mutex_lock(&rcdu->planes.lock); + rcrtc->plane->enabled = false; + rcar_du_crtc_update_planes(crtc); + mutex_unlock(&rcdu->planes.lock); + + /* Select switch sync mode. This stops display operation and configures + * the HSYNC and VSYNC signals as inputs. + */ + rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH); + + rcar_du_start_stop(rcdu, false); + + rcrtc->started = false; +} + +void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc) +{ + struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + + rcar_du_crtc_stop(rcrtc); + rcar_du_put(rcdu); +} + +void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) +{ + struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + + if (rcrtc->dpms != DRM_MODE_DPMS_ON) + return; + + rcar_du_get(rcdu); + rcar_du_crtc_start(rcrtc); +} + +static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc) +{ + struct drm_crtc *crtc = &rcrtc->crtc; + + rcar_du_plane_compute_base(rcrtc->plane, crtc->fb); + rcar_du_plane_update_base(rcrtc->plane); +} + +static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct rcar_du_device *rcdu = crtc->dev->dev_private; + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + + if (rcrtc->dpms == mode) + return; + + if (mode == DRM_MODE_DPMS_ON) { + rcar_du_get(rcdu); + rcar_du_crtc_start(rcrtc); + } else { + rcar_du_crtc_stop(rcrtc); + rcar_du_put(rcdu); + } + + rcrtc->dpms = mode; +} + +static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* TODO Fixup modes */ + return true; +} + +static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc) +{ + struct rcar_du_device *rcdu = crtc->dev->dev_private; + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + + /* We need to access the hardware during mode set, acquire a reference + * to the DU. + */ + rcar_du_get(rcdu); + + /* Stop the CRTC and release the plane. Force the DPMS mode to off as a + * result. + */ + rcar_du_crtc_stop(rcrtc); + rcar_du_plane_release(rcrtc->plane); + + rcrtc->dpms = DRM_MODE_DPMS_OFF; +} + +static int rcar_du_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + struct rcar_du_device *rcdu = crtc->dev->dev_private; + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + const struct rcar_du_format_info *format; + int ret; + + format = rcar_du_format_info(crtc->fb->pixel_format); + if (format == NULL) { + dev_dbg(rcdu->dev, "mode_set: unsupported format %08x\n", + crtc->fb->pixel_format); + ret = -EINVAL; + goto error; + } + + ret = rcar_du_plane_reserve(rcrtc->plane, format); + if (ret < 0) + goto error; + + rcrtc->plane->format = format; + rcrtc->plane->pitch = crtc->fb->pitches[0]; + + rcrtc->plane->src_x = x; + rcrtc->plane->src_y = y; + rcrtc->plane->width = mode->hdisplay; + rcrtc->plane->height = mode->vdisplay; + + rcar_du_plane_compute_base(rcrtc->plane, crtc->fb); + + rcrtc->outputs = 0; + + return 0; + +error: + /* There's no rollback/abort operation to clean up in case of error. We + * thus need to release the reference to the DU acquired in prepare() + * here. + */ + rcar_du_put(rcdu); + return ret; +} + +static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc) +{ + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + + /* We're done, restart the CRTC and set the DPMS mode to on. The + * reference to the DU acquired at prepare() time will thus be released + * by the DPMS handler (possibly called by the disable() handler). + */ + rcar_du_crtc_start(rcrtc); + rcrtc->dpms = DRM_MODE_DPMS_ON; +} + +static int rcar_du_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + + rcrtc->plane->src_x = x; + rcrtc->plane->src_y = y; + + rcar_du_crtc_update_base(to_rcar_crtc(crtc)); + + return 0; +} + +static void rcar_du_crtc_disable(struct drm_crtc *crtc) +{ + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + + rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + rcar_du_plane_release(rcrtc->plane); +} + +static const struct drm_crtc_helper_funcs crtc_helper_funcs = { + .dpms = rcar_du_crtc_dpms, + .mode_fixup = rcar_du_crtc_mode_fixup, + .prepare = rcar_du_crtc_mode_prepare, + .commit = rcar_du_crtc_mode_commit, + .mode_set = rcar_du_crtc_mode_set, + .mode_set_base = rcar_du_crtc_mode_set_base, + .disable = rcar_du_crtc_disable, +}; + +void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, + struct drm_file *file) +{ + struct drm_pending_vblank_event *event; + struct drm_device *dev = rcrtc->crtc.dev; + unsigned long flags; + + /* Destroy the pending vertical blanking event associated with the + * pending page flip, if any, and disable vertical blanking interrupts. + */ + spin_lock_irqsave(&dev->event_lock, flags); + event = rcrtc->event; + if (event && event->base.file_priv == file) { + rcrtc->event = NULL; + event->base.destroy(&event->base); + drm_vblank_put(dev, rcrtc->index); + } + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc) +{ + struct drm_pending_vblank_event *event; + struct drm_device *dev = rcrtc->crtc.dev; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + event = rcrtc->event; + rcrtc->event = NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); + + if (event == NULL) + return; + + spin_lock_irqsave(&dev->event_lock, flags); + drm_send_vblank_event(dev, rcrtc->index, event); + spin_unlock_irqrestore(&dev->event_lock, flags); + + drm_vblank_put(dev, rcrtc->index); +} + +static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event) +{ + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct drm_device *dev = rcrtc->crtc.dev; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + if (rcrtc->event != NULL) { + spin_unlock_irqrestore(&dev->event_lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&dev->event_lock, flags); + + crtc->fb = fb; + rcar_du_crtc_update_base(rcrtc); + + if (event) { + event->pipe = rcrtc->index; + drm_vblank_get(dev, rcrtc->index); + spin_lock_irqsave(&dev->event_lock, flags); + rcrtc->event = event; + spin_unlock_irqrestore(&dev->event_lock, flags); + } + + return 0; +} + +static const struct drm_crtc_funcs crtc_funcs = { + .destroy = drm_crtc_cleanup, + .set_config = drm_crtc_helper_set_config, + .page_flip = rcar_du_crtc_page_flip, +}; + +int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index) +{ + struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index]; + struct drm_crtc *crtc = &rcrtc->crtc; + int ret; + + rcrtc->mmio_offset = index ? DISP2_REG_OFFSET : 0; + rcrtc->index = index; + rcrtc->dpms = DRM_MODE_DPMS_OFF; + rcrtc->plane = &rcdu->planes.planes[index]; + + rcrtc->plane->crtc = crtc; + + ret = drm_crtc_init(rcdu->ddev, crtc, &crtc_funcs); + if (ret < 0) + return ret; + + drm_crtc_helper_add(crtc, &crtc_helper_funcs); + + return 0; +} + +void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable) +{ + if (enable) { + rcar_du_crtc_write(rcrtc, DSRCR, DSRCR_VBCL); + rcar_du_crtc_set(rcrtc, DIER, DIER_VBE); + } else { + rcar_du_crtc_clr(rcrtc, DIER, DIER_VBE); + } +} + +void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc) +{ + u32 status; + + status = rcar_du_crtc_read(rcrtc, DSSR); + rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK); + + if (status & DSSR_VBK) { + drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index); + rcar_du_crtc_finish_page_flip(rcrtc); + } +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h new file mode 100644 index 000000000000..2a0365bcbd14 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -0,0 +1,50 @@ +/* + * rcar_du_crtc.h -- R-Car Display Unit CRTCs + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_CRTC_H__ +#define __RCAR_DU_CRTC_H__ + +#include + +#include +#include + +struct rcar_du_device; +struct rcar_du_plane; + +struct rcar_du_crtc { + struct drm_crtc crtc; + + unsigned int mmio_offset; + unsigned int index; + bool started; + + struct drm_pending_vblank_event *event; + unsigned int outputs; + int dpms; + + struct rcar_du_plane *plane; +}; + +int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index); +void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable); +void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc); +void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, + struct drm_file *file); +void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc); +void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc); + +void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output); +void rcar_du_crtc_update_planes(struct drm_crtc *crtc); + +#endif /* __RCAR_DU_CRTC_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c new file mode 100644 index 000000000000..003b34ee38e3 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -0,0 +1,325 @@ +/* + * rcar_du_drv.c -- R-Car Display Unit DRM driver + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "rcar_du_crtc.h" +#include "rcar_du_drv.h" +#include "rcar_du_kms.h" +#include "rcar_du_regs.h" + +/* ----------------------------------------------------------------------------- + * Core device operations + */ + +/* + * rcar_du_get - Acquire a reference to the DU + * + * Acquiring a reference enables the device clock and setup core registers. A + * reference must be held before accessing any hardware registers. + * + * This function must be called with the DRM mode_config lock held. + * + * Return 0 in case of success or a negative error code otherwise. + */ +int rcar_du_get(struct rcar_du_device *rcdu) +{ + int ret; + + if (rcdu->use_count) + goto done; + + /* Enable clocks before accessing the hardware. */ + ret = clk_prepare_enable(rcdu->clock); + if (ret < 0) + return ret; + + /* Enable extended features */ + rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE); + rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G); + rcar_du_write(rcdu, DEFR3, DEFR3_CODE | DEFR3_DEFE3); + rcar_du_write(rcdu, DEFR4, DEFR4_CODE); + rcar_du_write(rcdu, DEFR5, DEFR5_CODE | DEFR5_DEFE5); + + /* Use DS1PR and DS2PR to configure planes priorities and connects the + * superposition 0 to DU0 pins. DU1 pins will be configured dynamically. + */ + rcar_du_write(rcdu, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS); + +done: + rcdu->use_count++; + return 0; +} + +/* + * rcar_du_put - Release a reference to the DU + * + * Releasing the last reference disables the device clock. + * + * This function must be called with the DRM mode_config lock held. + */ +void rcar_du_put(struct rcar_du_device *rcdu) +{ + if (--rcdu->use_count) + return; + + clk_disable_unprepare(rcdu->clock); +} + +/* ----------------------------------------------------------------------------- + * DRM operations + */ + +static int rcar_du_unload(struct drm_device *dev) +{ + drm_kms_helper_poll_fini(dev); + drm_mode_config_cleanup(dev); + drm_vblank_cleanup(dev); + drm_irq_uninstall(dev); + + dev->dev_private = NULL; + + return 0; +} + +static int rcar_du_load(struct drm_device *dev, unsigned long flags) +{ + struct platform_device *pdev = dev->platformdev; + struct rcar_du_platform_data *pdata = pdev->dev.platform_data; + struct rcar_du_device *rcdu; + struct resource *ioarea; + struct resource *mem; + int ret; + + if (pdata == NULL) { + dev_err(dev->dev, "no platform data\n"); + return -ENODEV; + } + + rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL); + if (rcdu == NULL) { + dev_err(dev->dev, "failed to allocate private data\n"); + return -ENOMEM; + } + + rcdu->dev = &pdev->dev; + rcdu->pdata = pdata; + rcdu->ddev = dev; + dev->dev_private = rcdu; + + /* I/O resources and clocks */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (mem == NULL) { + dev_err(&pdev->dev, "failed to get memory resource\n"); + return -EINVAL; + } + + ioarea = devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), pdev->name); + if (ioarea == NULL) { + dev_err(&pdev->dev, "failed to request memory region\n"); + return -EBUSY; + } + + rcdu->mmio = devm_ioremap_nocache(&pdev->dev, ioarea->start, + resource_size(ioarea)); + if (rcdu->mmio == NULL) { + dev_err(&pdev->dev, "failed to remap memory resource\n"); + return -ENOMEM; + } + + rcdu->clock = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(rcdu->clock)) { + dev_err(&pdev->dev, "failed to get clock\n"); + return -ENOENT; + } + + /* DRM/KMS objects */ + ret = rcar_du_modeset_init(rcdu); + if (ret < 0) { + dev_err(&pdev->dev, "failed to initialize DRM/KMS\n"); + goto done; + } + + /* IRQ and vblank handling */ + ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1); + if (ret < 0) { + dev_err(&pdev->dev, "failed to initialize vblank\n"); + goto done; + } + + ret = drm_irq_install(dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to install IRQ handler\n"); + goto done; + } + + platform_set_drvdata(pdev, rcdu); + +done: + if (ret) + rcar_du_unload(dev); + + return ret; +} + +static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file) +{ + struct rcar_du_device *rcdu = dev->dev_private; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) + rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file); +} + +static irqreturn_t rcar_du_irq(int irq, void *arg) +{ + struct drm_device *dev = arg; + struct rcar_du_device *rcdu = dev->dev_private; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) + rcar_du_crtc_irq(&rcdu->crtcs[i]); + + return IRQ_HANDLED; +} + +static int rcar_du_enable_vblank(struct drm_device *dev, int crtc) +{ + struct rcar_du_device *rcdu = dev->dev_private; + + rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], true); + + return 0; +} + +static void rcar_du_disable_vblank(struct drm_device *dev, int crtc) +{ + struct rcar_du_device *rcdu = dev->dev_private; + + rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], false); +} + +static const struct file_operations rcar_du_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .poll = drm_poll, + .read = drm_read, + .fasync = drm_fasync, + .llseek = no_llseek, + .mmap = drm_gem_cma_mmap, +}; + +static struct drm_driver rcar_du_driver = { + .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET + | DRIVER_PRIME, + .load = rcar_du_load, + .unload = rcar_du_unload, + .preclose = rcar_du_preclose, + .irq_handler = rcar_du_irq, + .get_vblank_counter = drm_vblank_count, + .enable_vblank = rcar_du_enable_vblank, + .disable_vblank = rcar_du_disable_vblank, + .gem_free_object = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_cma_dmabuf_import, + .gem_prime_export = drm_gem_cma_dmabuf_export, + .dumb_create = drm_gem_cma_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_cma_dumb_destroy, + .fops = &rcar_du_fops, + .name = "rcar-du", + .desc = "Renesas R-Car Display Unit", + .date = "20130110", + .major = 1, + .minor = 0, +}; + +/* ----------------------------------------------------------------------------- + * Power management + */ + +#if CONFIG_PM_SLEEP +static int rcar_du_pm_suspend(struct device *dev) +{ + struct rcar_du_device *rcdu = dev_get_drvdata(dev); + + drm_kms_helper_poll_disable(rcdu->ddev); + /* TODO Suspend the CRTC */ + + return 0; +} + +static int rcar_du_pm_resume(struct device *dev) +{ + struct rcar_du_device *rcdu = dev_get_drvdata(dev); + + /* TODO Resume the CRTC */ + + drm_kms_helper_poll_enable(rcdu->ddev); + return 0; +} +#endif + +static const struct dev_pm_ops rcar_du_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume) +}; + +/* ----------------------------------------------------------------------------- + * Platform driver + */ + +static int rcar_du_probe(struct platform_device *pdev) +{ + return drm_platform_init(&rcar_du_driver, pdev); +} + +static int rcar_du_remove(struct platform_device *pdev) +{ + drm_platform_exit(&rcar_du_driver, pdev); + + return 0; +} + +static struct platform_driver rcar_du_platform_driver = { + .probe = rcar_du_probe, + .remove = rcar_du_remove, + .driver = { + .owner = THIS_MODULE, + .name = "rcar-du", + .pm = &rcar_du_pm_ops, + }, +}; + +module_platform_driver(rcar_du_platform_driver); + +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h new file mode 100644 index 000000000000..193cc59d495c --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -0,0 +1,66 @@ +/* + * rcar_du_drv.h -- R-Car Display Unit DRM driver + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_DRV_H__ +#define __RCAR_DU_DRV_H__ + +#include +#include +#include + +#include "rcar_du_crtc.h" +#include "rcar_du_plane.h" + +struct clk; +struct device; +struct drm_device; + +struct rcar_du_device { + struct device *dev; + const struct rcar_du_platform_data *pdata; + + void __iomem *mmio; + struct clk *clock; + unsigned int use_count; + + struct drm_device *ddev; + + struct rcar_du_crtc crtcs[2]; + unsigned int used_crtcs; + unsigned int num_crtcs; + + struct { + struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES]; + unsigned int free; + struct mutex lock; + + struct drm_property *alpha; + struct drm_property *colorkey; + struct drm_property *zpos; + } planes; +}; + +int rcar_du_get(struct rcar_du_device *rcdu); +void rcar_du_put(struct rcar_du_device *rcdu); + +static inline u32 rcar_du_read(struct rcar_du_device *rcdu, u32 reg) +{ + return ioread32(rcdu->mmio + reg); +} + +static inline void rcar_du_write(struct rcar_du_device *rcdu, u32 reg, u32 data) +{ + iowrite32(data, rcdu->mmio + reg); +} + +#endif /* __RCAR_DU_DRV_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c new file mode 100644 index 000000000000..9c63f39658de --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -0,0 +1,245 @@ +/* + * rcar_du_kms.c -- R-Car Display Unit Mode Setting + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include "rcar_du_crtc.h" +#include "rcar_du_drv.h" +#include "rcar_du_kms.h" +#include "rcar_du_lvds.h" +#include "rcar_du_regs.h" +#include "rcar_du_vga.h" + +/* ----------------------------------------------------------------------------- + * Format helpers + */ + +static const struct rcar_du_format_info rcar_du_format_infos[] = { + { + .fourcc = DRM_FORMAT_RGB565, + .bpp = 16, + .planes = 1, + .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP, + .edf = PnDDCR4_EDF_NONE, + }, { + .fourcc = DRM_FORMAT_ARGB1555, + .bpp = 16, + .planes = 1, + .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB, + .edf = PnDDCR4_EDF_NONE, + }, { + .fourcc = DRM_FORMAT_XRGB1555, + .bpp = 16, + .planes = 1, + .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB, + .edf = PnDDCR4_EDF_NONE, + }, { + .fourcc = DRM_FORMAT_XRGB8888, + .bpp = 32, + .planes = 1, + .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP, + .edf = PnDDCR4_EDF_RGB888, + }, { + .fourcc = DRM_FORMAT_ARGB8888, + .bpp = 32, + .planes = 1, + .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP, + .edf = PnDDCR4_EDF_ARGB8888, + }, { + .fourcc = DRM_FORMAT_UYVY, + .bpp = 16, + .planes = 1, + .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC, + .edf = PnDDCR4_EDF_NONE, + }, { + .fourcc = DRM_FORMAT_YUYV, + .bpp = 16, + .planes = 1, + .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC, + .edf = PnDDCR4_EDF_NONE, + }, { + .fourcc = DRM_FORMAT_NV12, + .bpp = 12, + .planes = 2, + .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC, + .edf = PnDDCR4_EDF_NONE, + }, { + .fourcc = DRM_FORMAT_NV21, + .bpp = 12, + .planes = 2, + .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC, + .edf = PnDDCR4_EDF_NONE, + }, { + /* In YUV 4:2:2, only NV16 is supported (NV61 isn't) */ + .fourcc = DRM_FORMAT_NV16, + .bpp = 16, + .planes = 2, + .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC, + .edf = PnDDCR4_EDF_NONE, + }, +}; + +const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rcar_du_format_infos); ++i) { + if (rcar_du_format_infos[i].fourcc == fourcc) + return &rcar_du_format_infos[i]; + } + + return NULL; +} + +/* ----------------------------------------------------------------------------- + * Common connector and encoder functions + */ + +struct drm_encoder * +rcar_du_connector_best_encoder(struct drm_connector *connector) +{ + struct rcar_du_connector *rcon = to_rcar_connector(connector); + + return &rcon->encoder->encoder; +} + +void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder) +{ +} + +void rcar_du_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + + rcar_du_crtc_route_output(encoder->crtc, renc->output); +} + +void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) +{ +} + +/* ----------------------------------------------------------------------------- + * Frame buffer + */ + +static struct drm_framebuffer * +rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + const struct rcar_du_format_info *format; + + format = rcar_du_format_info(mode_cmd->pixel_format); + if (format == NULL) { + dev_dbg(dev->dev, "unsupported pixel format %08x\n", + mode_cmd->pixel_format); + return ERR_PTR(-EINVAL); + } + + if (mode_cmd->pitches[0] & 15 || mode_cmd->pitches[0] >= 8192) { + dev_dbg(dev->dev, "invalid pitch value %u\n", + mode_cmd->pitches[0]); + return ERR_PTR(-EINVAL); + } + + if (format->planes == 2) { + if (mode_cmd->pitches[1] != mode_cmd->pitches[0]) { + dev_dbg(dev->dev, + "luma and chroma pitches do not match\n"); + return ERR_PTR(-EINVAL); + } + } + + return drm_fb_cma_create(dev, file_priv, mode_cmd); +} + +static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = { + .fb_create = rcar_du_fb_create, +}; + +int rcar_du_modeset_init(struct rcar_du_device *rcdu) +{ + struct drm_device *dev = rcdu->ddev; + struct drm_encoder *encoder; + unsigned int i; + int ret; + + drm_mode_config_init(rcdu->ddev); + + rcdu->ddev->mode_config.min_width = 0; + rcdu->ddev->mode_config.min_height = 0; + rcdu->ddev->mode_config.max_width = 4095; + rcdu->ddev->mode_config.max_height = 2047; + rcdu->ddev->mode_config.funcs = &rcar_du_mode_config_funcs; + + ret = rcar_du_plane_init(rcdu); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) + rcar_du_crtc_create(rcdu, i); + + rcdu->used_crtcs = 0; + rcdu->num_crtcs = i; + + for (i = 0; i < rcdu->pdata->num_encoders; ++i) { + const struct rcar_du_encoder_data *pdata = + &rcdu->pdata->encoders[i]; + + if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) { + dev_warn(rcdu->dev, + "encoder %u references unexisting output %u, skipping\n", + i, pdata->output); + continue; + } + + switch (pdata->encoder) { + case RCAR_DU_ENCODER_VGA: + rcar_du_vga_init(rcdu, &pdata->u.vga, pdata->output); + break; + + case RCAR_DU_ENCODER_LVDS: + rcar_du_lvds_init(rcdu, &pdata->u.lvds, pdata->output); + break; + + default: + break; + } + } + + /* Set the possible CRTCs and possible clones. All encoders can be + * driven by the CRTC associated with the output they're connected to, + * as well as by CRTC 0. + */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + + encoder->possible_crtcs = (1 << 0) | (1 << renc->output); + encoder->possible_clones = 1 << 0; + } + + ret = rcar_du_plane_register(rcdu); + if (ret < 0) + return ret; + + drm_kms_helper_poll_init(rcdu->ddev); + + drm_helper_disable_unused_functions(rcdu->ddev); + + return 0; +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h new file mode 100644 index 000000000000..e4d8db069a06 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h @@ -0,0 +1,59 @@ +/* + * rcar_du_kms.h -- R-Car Display Unit Mode Setting + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_KMS_H__ +#define __RCAR_DU_KMS_H__ + +#include + +#include + +struct rcar_du_device; + +struct rcar_du_format_info { + u32 fourcc; + unsigned int bpp; + unsigned int planes; + unsigned int pnmr; + unsigned int edf; +}; + +struct rcar_du_encoder { + struct drm_encoder encoder; + unsigned int output; +}; + +#define to_rcar_encoder(e) \ + container_of(e, struct rcar_du_encoder, encoder) + +struct rcar_du_connector { + struct drm_connector connector; + struct rcar_du_encoder *encoder; +}; + +#define to_rcar_connector(c) \ + container_of(c, struct rcar_du_connector, connector) + +const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc); + +struct drm_encoder * +rcar_du_connector_best_encoder(struct drm_connector *connector); +void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder); +void rcar_du_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +void rcar_du_encoder_mode_commit(struct drm_encoder *encoder); + +int rcar_du_modeset_init(struct rcar_du_device *rcdu); + +#endif /* __RCAR_DU_KMS_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.c b/drivers/gpu/drm/rcar-du/rcar_du_lvds.c new file mode 100644 index 000000000000..7aefe7267e1d --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvds.c @@ -0,0 +1,216 @@ +/* + * rcar_du_lvds.c -- R-Car Display Unit LVDS Encoder and Connector + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include "rcar_du_drv.h" +#include "rcar_du_kms.h" +#include "rcar_du_lvds.h" + +struct rcar_du_lvds_connector { + struct rcar_du_connector connector; + + const struct rcar_du_panel_data *panel; +}; + +#define to_rcar_lvds_connector(c) \ + container_of(c, struct rcar_du_lvds_connector, connector.connector) + +/* ----------------------------------------------------------------------------- + * Connector + */ + +static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector) +{ + struct rcar_du_lvds_connector *lvdscon = to_rcar_lvds_connector(connector); + struct drm_display_mode *mode; + + mode = drm_mode_create(connector->dev); + if (mode == NULL) + return 0; + + mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; + mode->clock = lvdscon->panel->mode.clock; + mode->hdisplay = lvdscon->panel->mode.hdisplay; + mode->hsync_start = lvdscon->panel->mode.hsync_start; + mode->hsync_end = lvdscon->panel->mode.hsync_end; + mode->htotal = lvdscon->panel->mode.htotal; + mode->vdisplay = lvdscon->panel->mode.vdisplay; + mode->vsync_start = lvdscon->panel->mode.vsync_start; + mode->vsync_end = lvdscon->panel->mode.vsync_end; + mode->vtotal = lvdscon->panel->mode.vtotal; + mode->flags = lvdscon->panel->mode.flags; + + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + + return 1; +} + +static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static const struct drm_connector_helper_funcs connector_helper_funcs = { + .get_modes = rcar_du_lvds_connector_get_modes, + .mode_valid = rcar_du_lvds_connector_mode_valid, + .best_encoder = rcar_du_connector_best_encoder, +}; + +static void rcar_du_lvds_connector_destroy(struct drm_connector *connector) +{ + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); +} + +static enum drm_connector_status +rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static const struct drm_connector_funcs connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = rcar_du_lvds_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = rcar_du_lvds_connector_destroy, +}; + +static int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, + struct rcar_du_encoder *renc, + const struct rcar_du_panel_data *panel) +{ + struct rcar_du_lvds_connector *lvdscon; + struct drm_connector *connector; + int ret; + + lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL); + if (lvdscon == NULL) + return -ENOMEM; + + lvdscon->panel = panel; + + connector = &lvdscon->connector.connector; + connector->display_info.width_mm = panel->width_mm; + connector->display_info.height_mm = panel->height_mm; + + ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + if (ret < 0) + return ret; + + drm_connector_helper_add(connector, &connector_helper_funcs); + ret = drm_sysfs_connector_add(connector); + if (ret < 0) + return ret; + + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + drm_object_property_set_value(&connector->base, + rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); + + ret = drm_mode_connector_attach_encoder(connector, &renc->encoder); + if (ret < 0) + return ret; + + connector->encoder = &renc->encoder; + lvdscon->connector.encoder = renc; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Encoder + */ + +static void rcar_du_lvds_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +} + +static bool rcar_du_lvds_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + const struct drm_display_mode *panel_mode; + struct drm_device *dev = encoder->dev; + struct drm_connector *connector; + bool found = false; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder == encoder) { + found = true; + break; + } + } + + if (!found) { + dev_dbg(dev->dev, "mode_fixup: no connector found\n"); + return false; + } + + if (list_empty(&connector->modes)) { + dev_dbg(dev->dev, "mode_fixup: empty modes list\n"); + return false; + } + + panel_mode = list_first_entry(&connector->modes, + struct drm_display_mode, head); + + /* We're not allowed to modify the resolution. */ + if (mode->hdisplay != panel_mode->hdisplay || + mode->vdisplay != panel_mode->vdisplay) + return false; + + /* The flat panel mode is fixed, just copy it to the adjusted mode. */ + drm_mode_copy(adjusted_mode, panel_mode); + + return true; +} + +static const struct drm_encoder_helper_funcs encoder_helper_funcs = { + .dpms = rcar_du_lvds_encoder_dpms, + .mode_fixup = rcar_du_lvds_encoder_mode_fixup, + .prepare = rcar_du_encoder_mode_prepare, + .commit = rcar_du_encoder_mode_commit, + .mode_set = rcar_du_encoder_mode_set, +}; + +static const struct drm_encoder_funcs encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +int rcar_du_lvds_init(struct rcar_du_device *rcdu, + const struct rcar_du_encoder_lvds_data *data, + unsigned int output) +{ + struct rcar_du_encoder *renc; + int ret; + + renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); + if (renc == NULL) + return -ENOMEM; + + renc->output = output; + + ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs, + DRM_MODE_ENCODER_LVDS); + if (ret < 0) + return ret; + + drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); + + return rcar_du_lvds_connector_init(rcdu, renc, &data->panel); +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.h b/drivers/gpu/drm/rcar-du/rcar_du_lvds.h new file mode 100644 index 000000000000..b47f8328e103 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvds.h @@ -0,0 +1,24 @@ +/* + * rcar_du_lvds.h -- R-Car Display Unit LVDS Encoder and Connector + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_LVDS_H__ +#define __RCAR_DU_LVDS_H__ + +struct rcar_du_device; +struct rcar_du_encoder_lvds_data; + +int rcar_du_lvds_init(struct rcar_du_device *rcdu, + const struct rcar_du_encoder_lvds_data *data, + unsigned int output); + +#endif /* __RCAR_DU_LVDS_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c new file mode 100644 index 000000000000..a65f81ddf51d --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -0,0 +1,507 @@ +/* + * rcar_du_plane.c -- R-Car Display Unit Planes + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include "rcar_du_drv.h" +#include "rcar_du_kms.h" +#include "rcar_du_plane.h" +#include "rcar_du_regs.h" + +#define RCAR_DU_COLORKEY_NONE (0 << 24) +#define RCAR_DU_COLORKEY_SOURCE (1 << 24) +#define RCAR_DU_COLORKEY_MASK (1 << 24) + +struct rcar_du_kms_plane { + struct drm_plane plane; + struct rcar_du_plane *hwplane; +}; + +static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane) +{ + return container_of(plane, struct rcar_du_kms_plane, plane)->hwplane; +} + +static u32 rcar_du_plane_read(struct rcar_du_device *rcdu, + unsigned int index, u32 reg) +{ + return rcar_du_read(rcdu, index * PLANE_OFF + reg); +} + +static void rcar_du_plane_write(struct rcar_du_device *rcdu, + unsigned int index, u32 reg, u32 data) +{ + rcar_du_write(rcdu, index * PLANE_OFF + reg, data); +} + +int rcar_du_plane_reserve(struct rcar_du_plane *plane, + const struct rcar_du_format_info *format) +{ + struct rcar_du_device *rcdu = plane->dev; + unsigned int i; + int ret = -EBUSY; + + mutex_lock(&rcdu->planes.lock); + + for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) { + if (!(rcdu->planes.free & (1 << i))) + continue; + + if (format->planes == 1 || + rcdu->planes.free & (1 << ((i + 1) % 8))) + break; + } + + if (i == ARRAY_SIZE(rcdu->planes.planes)) + goto done; + + rcdu->planes.free &= ~(1 << i); + if (format->planes == 2) + rcdu->planes.free &= ~(1 << ((i + 1) % 8)); + + plane->hwindex = i; + + ret = 0; + +done: + mutex_unlock(&rcdu->planes.lock); + return ret; +} + +void rcar_du_plane_release(struct rcar_du_plane *plane) +{ + struct rcar_du_device *rcdu = plane->dev; + + if (plane->hwindex == -1) + return; + + mutex_lock(&rcdu->planes.lock); + rcdu->planes.free |= 1 << plane->hwindex; + if (plane->format->planes == 2) + rcdu->planes.free |= 1 << ((plane->hwindex + 1) % 8); + mutex_unlock(&rcdu->planes.lock); + + plane->hwindex = -1; +} + +void rcar_du_plane_update_base(struct rcar_du_plane *plane) +{ + struct rcar_du_device *rcdu = plane->dev; + unsigned int index = plane->hwindex; + + /* According to the datasheet the Y position is expressed in raster line + * units. However, 32bpp formats seem to require a doubled Y position + * value. Similarly, for the second plane, NV12 and NV21 formats seem to + * require a halved Y position value. + */ + rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x); + rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y * + (plane->format->bpp == 32 ? 2 : 1)); + rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[0]); + + if (plane->format->planes == 2) { + index = (index + 1) % 8; + + rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x); + rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y * + (plane->format->bpp == 16 ? 2 : 1) / 2); + rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[1]); + } +} + +void rcar_du_plane_compute_base(struct rcar_du_plane *plane, + struct drm_framebuffer *fb) +{ + struct drm_gem_cma_object *gem; + + gem = drm_fb_cma_get_gem_obj(fb, 0); + plane->dma[0] = gem->paddr + fb->offsets[0]; + + if (plane->format->planes == 2) { + gem = drm_fb_cma_get_gem_obj(fb, 1); + plane->dma[1] = gem->paddr + fb->offsets[1]; + } +} + +static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, + unsigned int index) +{ + struct rcar_du_device *rcdu = plane->dev; + u32 colorkey; + u32 pnmr; + + /* The PnALPHAR register controls alpha-blending in 16bpp formats + * (ARGB1555 and XRGB1555). + * + * For ARGB, set the alpha value to 0, and enable alpha-blending when + * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255. + * + * For XRGB, set the alpha value to the plane-wide alpha value and + * enable alpha-blending regardless of the X bit value. + */ + if (plane->format->fourcc != DRM_FORMAT_XRGB1555) + rcar_du_plane_write(rcdu, index, PnALPHAR, PnALPHAR_ABIT_0); + else + rcar_du_plane_write(rcdu, index, PnALPHAR, + PnALPHAR_ABIT_X | plane->alpha); + + pnmr = PnMR_BM_MD | plane->format->pnmr; + + /* Disable color keying when requested. YUV formats have the + * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying + * automatically. + */ + if ((plane->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE) + pnmr |= PnMR_SPIM_TP_OFF; + + /* For packed YUV formats we need to select the U/V order. */ + if (plane->format->fourcc == DRM_FORMAT_YUYV) + pnmr |= PnMR_YCDF_YUYV; + + rcar_du_plane_write(rcdu, index, PnMR, pnmr); + + switch (plane->format->fourcc) { + case DRM_FORMAT_RGB565: + colorkey = ((plane->colorkey & 0xf80000) >> 8) + | ((plane->colorkey & 0x00fc00) >> 5) + | ((plane->colorkey & 0x0000f8) >> 3); + rcar_du_plane_write(rcdu, index, PnTC2R, colorkey); + break; + + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_XRGB1555: + colorkey = ((plane->colorkey & 0xf80000) >> 9) + | ((plane->colorkey & 0x00f800) >> 6) + | ((plane->colorkey & 0x0000f8) >> 3); + rcar_du_plane_write(rcdu, index, PnTC2R, colorkey); + break; + + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + rcar_du_plane_write(rcdu, index, PnTC3R, + PnTC3R_CODE | (plane->colorkey & 0xffffff)); + break; + } +} + +static void __rcar_du_plane_setup(struct rcar_du_plane *plane, + unsigned int index) +{ + struct rcar_du_device *rcdu = plane->dev; + u32 ddcr2 = PnDDCR2_CODE; + u32 ddcr4; + u32 mwr; + + /* Data format + * + * The data format is selected by the DDDF field in PnMR and the EDF + * field in DDCR4. + */ + ddcr4 = rcar_du_plane_read(rcdu, index, PnDDCR4); + ddcr4 &= ~PnDDCR4_EDF_MASK; + ddcr4 |= plane->format->edf | PnDDCR4_CODE; + + rcar_du_plane_setup_mode(plane, index); + + if (plane->format->planes == 2) { + if (plane->hwindex != index) { + if (plane->format->fourcc == DRM_FORMAT_NV12 || + plane->format->fourcc == DRM_FORMAT_NV21) + ddcr2 |= PnDDCR2_Y420; + + if (plane->format->fourcc == DRM_FORMAT_NV21) + ddcr2 |= PnDDCR2_NV21; + + ddcr2 |= PnDDCR2_DIVU; + } else { + ddcr2 |= PnDDCR2_DIVY; + } + } + + rcar_du_plane_write(rcdu, index, PnDDCR2, ddcr2); + rcar_du_plane_write(rcdu, index, PnDDCR4, ddcr4); + + /* Memory pitch (expressed in pixels) */ + if (plane->format->planes == 2) + mwr = plane->pitch; + else + mwr = plane->pitch * 8 / plane->format->bpp; + + rcar_du_plane_write(rcdu, index, PnMWR, mwr); + + /* Destination position and size */ + rcar_du_plane_write(rcdu, index, PnDSXR, plane->width); + rcar_du_plane_write(rcdu, index, PnDSYR, plane->height); + rcar_du_plane_write(rcdu, index, PnDPXR, plane->dst_x); + rcar_du_plane_write(rcdu, index, PnDPYR, plane->dst_y); + + /* Wrap-around and blinking, disabled */ + rcar_du_plane_write(rcdu, index, PnWASPR, 0); + rcar_du_plane_write(rcdu, index, PnWAMWR, 4095); + rcar_du_plane_write(rcdu, index, PnBTR, 0); + rcar_du_plane_write(rcdu, index, PnMLR, 0); +} + +void rcar_du_plane_setup(struct rcar_du_plane *plane) +{ + __rcar_du_plane_setup(plane, plane->hwindex); + if (plane->format->planes == 2) + __rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8); + + rcar_du_plane_update_base(plane); +} + +static int +rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct rcar_du_plane *rplane = to_rcar_plane(plane); + struct rcar_du_device *rcdu = plane->dev->dev_private; + const struct rcar_du_format_info *format; + unsigned int nplanes; + int ret; + + format = rcar_du_format_info(fb->pixel_format); + if (format == NULL) { + dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, + fb->pixel_format); + return -EINVAL; + } + + if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) { + dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__); + return -EINVAL; + } + + nplanes = rplane->format ? rplane->format->planes : 0; + + /* Reallocate hardware planes if the number of required planes has + * changed. + */ + if (format->planes != nplanes) { + rcar_du_plane_release(rplane); + ret = rcar_du_plane_reserve(rplane, format); + if (ret < 0) + return ret; + } + + rplane->crtc = crtc; + rplane->format = format; + rplane->pitch = fb->pitches[0]; + + rplane->src_x = src_x >> 16; + rplane->src_y = src_y >> 16; + rplane->dst_x = crtc_x; + rplane->dst_y = crtc_y; + rplane->width = crtc_w; + rplane->height = crtc_h; + + rcar_du_plane_compute_base(rplane, fb); + rcar_du_plane_setup(rplane); + + mutex_lock(&rcdu->planes.lock); + rplane->enabled = true; + rcar_du_crtc_update_planes(rplane->crtc); + mutex_unlock(&rcdu->planes.lock); + + return 0; +} + +static int rcar_du_plane_disable(struct drm_plane *plane) +{ + struct rcar_du_device *rcdu = plane->dev->dev_private; + struct rcar_du_plane *rplane = to_rcar_plane(plane); + + if (!rplane->enabled) + return 0; + + mutex_lock(&rcdu->planes.lock); + rplane->enabled = false; + rcar_du_crtc_update_planes(rplane->crtc); + mutex_unlock(&rcdu->planes.lock); + + rcar_du_plane_release(rplane); + + rplane->crtc = NULL; + rplane->format = NULL; + + return 0; +} + +/* Both the .set_property and the .update_plane operations are called with the + * mode_config lock held. There is this no need to explicitly protect access to + * the alpha and colorkey fields and the mode register. + */ +static void rcar_du_plane_set_alpha(struct rcar_du_plane *plane, u32 alpha) +{ + if (plane->alpha == alpha) + return; + + plane->alpha = alpha; + if (!plane->enabled || plane->format->fourcc != DRM_FORMAT_XRGB1555) + return; + + rcar_du_plane_setup_mode(plane, plane->hwindex); +} + +static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane, + u32 colorkey) +{ + if (plane->colorkey == colorkey) + return; + + plane->colorkey = colorkey; + if (!plane->enabled) + return; + + rcar_du_plane_setup_mode(plane, plane->hwindex); +} + +static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane, + unsigned int zpos) +{ + struct rcar_du_device *rcdu = plane->dev; + + mutex_lock(&rcdu->planes.lock); + if (plane->zpos == zpos) + goto done; + + plane->zpos = zpos; + if (!plane->enabled) + goto done; + + rcar_du_crtc_update_planes(plane->crtc); + +done: + mutex_unlock(&rcdu->planes.lock); +} + +static int rcar_du_plane_set_property(struct drm_plane *plane, + struct drm_property *property, + uint64_t value) +{ + struct rcar_du_device *rcdu = plane->dev->dev_private; + struct rcar_du_plane *rplane = to_rcar_plane(plane); + + if (property == rcdu->planes.alpha) + rcar_du_plane_set_alpha(rplane, value); + else if (property == rcdu->planes.colorkey) + rcar_du_plane_set_colorkey(rplane, value); + else if (property == rcdu->planes.zpos) + rcar_du_plane_set_zpos(rplane, value); + else + return -EINVAL; + + return 0; +} + +static const struct drm_plane_funcs rcar_du_plane_funcs = { + .update_plane = rcar_du_plane_update, + .disable_plane = rcar_du_plane_disable, + .set_property = rcar_du_plane_set_property, + .destroy = drm_plane_cleanup, +}; + +static const uint32_t formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, + DRM_FORMAT_NV16, +}; + +int rcar_du_plane_init(struct rcar_du_device *rcdu) +{ + unsigned int i; + + mutex_init(&rcdu->planes.lock); + rcdu->planes.free = 0xff; + + rcdu->planes.alpha = + drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255); + if (rcdu->planes.alpha == NULL) + return -ENOMEM; + + /* The color key is expressed as an RGB888 triplet stored in a 32-bit + * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0) + * or enable source color keying (1). + */ + rcdu->planes.colorkey = + drm_property_create_range(rcdu->ddev, 0, "colorkey", + 0, 0x01ffffff); + if (rcdu->planes.colorkey == NULL) + return -ENOMEM; + + rcdu->planes.zpos = + drm_property_create_range(rcdu->ddev, 0, "zpos", 1, 7); + if (rcdu->planes.zpos == NULL) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) { + struct rcar_du_plane *plane = &rcdu->planes.planes[i]; + + plane->dev = rcdu; + plane->hwindex = -1; + plane->alpha = 255; + plane->colorkey = RCAR_DU_COLORKEY_NONE; + plane->zpos = 0; + } + + return 0; +} + +int rcar_du_plane_register(struct rcar_du_device *rcdu) +{ + unsigned int i; + int ret; + + for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) { + struct rcar_du_kms_plane *plane; + + plane = devm_kzalloc(rcdu->dev, sizeof(*plane), GFP_KERNEL); + if (plane == NULL) + return -ENOMEM; + + plane->hwplane = &rcdu->planes.planes[i + 2]; + plane->hwplane->zpos = 1; + + ret = drm_plane_init(rcdu->ddev, &plane->plane, + (1 << rcdu->num_crtcs) - 1, + &rcar_du_plane_funcs, formats, + ARRAY_SIZE(formats), false); + if (ret < 0) + return ret; + + drm_object_attach_property(&plane->plane.base, + rcdu->planes.alpha, 255); + drm_object_attach_property(&plane->plane.base, + rcdu->planes.colorkey, + RCAR_DU_COLORKEY_NONE); + drm_object_attach_property(&plane->plane.base, + rcdu->planes.zpos, 1); + } + + return 0; +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h new file mode 100644 index 000000000000..5397dba2fe57 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -0,0 +1,67 @@ +/* + * rcar_du_plane.h -- R-Car Display Unit Planes + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_PLANE_H__ +#define __RCAR_DU_PLANE_H__ + +struct drm_crtc; +struct drm_framebuffer; +struct rcar_du_device; +struct rcar_du_format_info; + +/* The RCAR DU has 8 hardware planes, shared between KMS planes and CRTCs. As + * using KMS planes requires at least one of the CRTCs being enabled, no more + * than 7 KMS planes can be available. We thus create 7 KMS planes and + * 9 software planes (one for each KMS planes and one for each CRTC). + */ + +#define RCAR_DU_NUM_KMS_PLANES 7 +#define RCAR_DU_NUM_HW_PLANES 8 +#define RCAR_DU_NUM_SW_PLANES 9 + +struct rcar_du_plane { + struct rcar_du_device *dev; + struct drm_crtc *crtc; + + bool enabled; + + int hwindex; /* 0-based, -1 means unused */ + unsigned int alpha; + unsigned int colorkey; + unsigned int zpos; + + const struct rcar_du_format_info *format; + + unsigned long dma[2]; + unsigned int pitch; + + unsigned int width; + unsigned int height; + + unsigned int src_x; + unsigned int src_y; + unsigned int dst_x; + unsigned int dst_y; +}; + +int rcar_du_plane_init(struct rcar_du_device *rcdu); +int rcar_du_plane_register(struct rcar_du_device *rcdu); +void rcar_du_plane_setup(struct rcar_du_plane *plane); +void rcar_du_plane_update_base(struct rcar_du_plane *plane); +void rcar_du_plane_compute_base(struct rcar_du_plane *plane, + struct drm_framebuffer *fb); +int rcar_du_plane_reserve(struct rcar_du_plane *plane, + const struct rcar_du_format_info *format); +void rcar_du_plane_release(struct rcar_du_plane *plane); + +#endif /* __RCAR_DU_PLANE_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h new file mode 100644 index 000000000000..69f21f19b51c --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h @@ -0,0 +1,445 @@ +/* + * rcar_du_regs.h -- R-Car Display Unit Registers Definitions + * + * Copyright (C) 2013 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + */ + +#ifndef __RCAR_DU_REGS_H__ +#define __RCAR_DU_REGS_H__ + +#define DISP2_REG_OFFSET 0x30000 + +/* ----------------------------------------------------------------------------- + * Display Control Registers + */ + +#define DSYSR 0x00000 /* display 1 */ +#define D2SYSR 0x30000 /* display 2 */ +#define DSYSR_ILTS (1 << 29) +#define DSYSR_DSEC (1 << 20) +#define DSYSR_IUPD (1 << 16) +#define DSYSR_DRES (1 << 9) +#define DSYSR_DEN (1 << 8) +#define DSYSR_TVM_MASTER (0 << 6) +#define DSYSR_TVM_SWITCH (1 << 6) +#define DSYSR_TVM_TVSYNC (2 << 6) +#define DSYSR_TVM_MASK (3 << 6) +#define DSYSR_SCM_INT_NONE (0 << 4) +#define DSYSR_SCM_INT_SYNC (2 << 4) +#define DSYSR_SCM_INT_VIDEO (3 << 4) + +#define DSMR 0x00004 +#define D2SMR 0x30004 +#define DSMR_VSPM (1 << 28) +#define DSMR_ODPM (1 << 27) +#define DSMR_DIPM_DISP (0 << 25) +#define DSMR_DIPM_CSYNC (1 << 25) +#define DSMR_DIPM_DE (3 << 25) +#define DSMR_DIPM_MASK (3 << 25) +#define DSMR_CSPM (1 << 24) +#define DSMR_DIL (1 << 19) +#define DSMR_VSL (1 << 18) +#define DSMR_HSL (1 << 17) +#define DSMR_DDIS (1 << 16) +#define DSMR_CDEL (1 << 15) +#define DSMR_CDEM_CDE (0 << 13) +#define DSMR_CDEM_LOW (2 << 13) +#define DSMR_CDEM_HIGH (3 << 13) +#define DSMR_CDEM_MASK (3 << 13) +#define DSMR_CDED (1 << 12) +#define DSMR_ODEV (1 << 8) +#define DSMR_CSY_VH_OR (0 << 6) +#define DSMR_CSY_333 (2 << 6) +#define DSMR_CSY_222 (3 << 6) +#define DSMR_CSY_MASK (3 << 6) + +#define DSSR 0x00008 +#define D2SSR 0x30008 +#define DSSR_VC1FB_DSA0 (0 << 30) +#define DSSR_VC1FB_DSA1 (1 << 30) +#define DSSR_VC1FB_DSA2 (2 << 30) +#define DSSR_VC1FB_INIT (3 << 30) +#define DSSR_VC1FB_MASK (3 << 30) +#define DSSR_VC0FB_DSA0 (0 << 28) +#define DSSR_VC0FB_DSA1 (1 << 28) +#define DSSR_VC0FB_DSA2 (2 << 28) +#define DSSR_VC0FB_INIT (3 << 28) +#define DSSR_VC0FB_MASK (3 << 28) +#define DSSR_DFB(n) (1 << ((n)+15)) +#define DSSR_TVR (1 << 15) +#define DSSR_FRM (1 << 14) +#define DSSR_VBK (1 << 11) +#define DSSR_RINT (1 << 9) +#define DSSR_HBK (1 << 8) +#define DSSR_ADC(n) (1 << ((n)-1)) + +#define DSRCR 0x0000c +#define D2SRCR 0x3000c +#define DSRCR_TVCL (1 << 15) +#define DSRCR_FRCL (1 << 14) +#define DSRCR_VBCL (1 << 11) +#define DSRCR_RICL (1 << 9) +#define DSRCR_HBCL (1 << 8) +#define DSRCR_ADCL(n) (1 << ((n)-1)) +#define DSRCR_MASK 0x0000cbff + +#define DIER 0x00010 +#define D2IER 0x30010 +#define DIER_TVE (1 << 15) +#define DIER_FRE (1 << 14) +#define DIER_VBE (1 << 11) +#define DIER_RIE (1 << 9) +#define DIER_HBE (1 << 8) +#define DIER_ADCE(n) (1 << ((n)-1)) + +#define CPCR 0x00014 +#define CPCR_CP4CE (1 << 19) +#define CPCR_CP3CE (1 << 18) +#define CPCR_CP2CE (1 << 17) +#define CPCR_CP1CE (1 << 16) + +#define DPPR 0x00018 +#define DPPR_DPE(n) (1 << ((n)*4-1)) +#define DPPR_DPS(n, p) (((p)-1) << DPPR_DPS_SHIFT(n)) +#define DPPR_DPS_SHIFT(n) (((n)-1)*4) +#define DPPR_BPP16 (DPPR_DPE(8) | DPPR_DPS(8, 1)) /* plane1 */ +#define DPPR_BPP32_P1 (DPPR_DPE(7) | DPPR_DPS(7, 1)) +#define DPPR_BPP32_P2 (DPPR_DPE(8) | DPPR_DPS(8, 2)) +#define DPPR_BPP32 (DPPR_BPP32_P1 | DPPR_BPP32_P2) /* plane1 & 2 */ + +#define DEFR 0x00020 +#define D2EFR 0x30020 +#define DEFR_CODE (0x7773 << 16) +#define DEFR_EXSL (1 << 12) +#define DEFR_EXVL (1 << 11) +#define DEFR_EXUP (1 << 5) +#define DEFR_VCUP (1 << 4) +#define DEFR_DEFE (1 << 0) + +#define DAPCR 0x00024 +#define DAPCR_CODE (0x7773 << 16) +#define DAPCR_AP2E (1 << 4) +#define DAPCR_AP1E (1 << 0) + +#define DCPCR 0x00028 +#define DCPCR_CODE (0x7773 << 16) +#define DCPCR_CA2B (1 << 13) +#define DCPCR_CD2F (1 << 12) +#define DCPCR_DC2E (1 << 8) +#define DCPCR_CAB (1 << 5) +#define DCPCR_CDF (1 << 4) +#define DCPCR_DCE (1 << 0) + +#define DEFR2 0x00034 +#define D2EFR2 0x30034 +#define DEFR2_CODE (0x7775 << 16) +#define DEFR2_DEFE2G (1 << 0) + +#define DEFR3 0x00038 +#define D2EFR3 0x30038 +#define DEFR3_CODE (0x7776 << 16) +#define DEFR3_EVDA (1 << 14) +#define DEFR3_EVDM_1 (1 << 12) +#define DEFR3_EVDM_2 (2 << 12) +#define DEFR3_EVDM_3 (3 << 12) +#define DEFR3_VMSM2_EMA (1 << 6) +#define DEFR3_VMSM1_ENA (1 << 4) +#define DEFR3_DEFE3 (1 << 0) + +#define DEFR4 0x0003c +#define D2EFR4 0x3003c +#define DEFR4_CODE (0x7777 << 16) +#define DEFR4_LRUO (1 << 5) +#define DEFR4_SPCE (1 << 4) + +#define DVCSR 0x000d0 +#define DVCSR_VCnFB2_DSA0(n) (0 << ((n)*2+16)) +#define DVCSR_VCnFB2_DSA1(n) (1 << ((n)*2+16)) +#define DVCSR_VCnFB2_DSA2(n) (2 << ((n)*2+16)) +#define DVCSR_VCnFB2_INIT(n) (3 << ((n)*2+16)) +#define DVCSR_VCnFB2_MASK(n) (3 << ((n)*2+16)) +#define DVCSR_VCnFB_DSA0(n) (0 << ((n)*2)) +#define DVCSR_VCnFB_DSA1(n) (1 << ((n)*2)) +#define DVCSR_VCnFB_DSA2(n) (2 << ((n)*2)) +#define DVCSR_VCnFB_INIT(n) (3 << ((n)*2)) +#define DVCSR_VCnFB_MASK(n) (3 << ((n)*2)) + +#define DEFR5 0x000e0 +#define DEFR5_CODE (0x66 << 24) +#define DEFR5_YCRGB2_DIS (0 << 14) +#define DEFR5_YCRGB2_PRI1 (1 << 14) +#define DEFR5_YCRGB2_PRI2 (2 << 14) +#define DEFR5_YCRGB2_PRI3 (3 << 14) +#define DEFR5_YCRGB2_MASK (3 << 14) +#define DEFR5_YCRGB1_DIS (0 << 12) +#define DEFR5_YCRGB1_PRI1 (1 << 12) +#define DEFR5_YCRGB1_PRI2 (2 << 12) +#define DEFR5_YCRGB1_PRI3 (3 << 12) +#define DEFR5_YCRGB1_MASK (3 << 12) +#define DEFR5_DEFE5 (1 << 0) + +#define DDLTR 0x000e4 +#define DDLTR_CODE (0x7766 << 16) +#define DDLTR_DLAR2 (1 << 6) +#define DDLTR_DLAY2 (1 << 5) +#define DDLTR_DLAY1 (1 << 1) + +#define DEFR6 0x000e8 +#define DEFR6_CODE (0x7778 << 16) +#define DEFR6_ODPM22_D2SMR (0 << 10) +#define DEFR6_ODPM22_DISP (2 << 10) +#define DEFR6_ODPM22_CDE (3 << 10) +#define DEFR6_ODPM22_MASK (3 << 10) +#define DEFR6_ODPM12_DSMR (0 << 8) +#define DEFR6_ODPM12_DISP (2 << 8) +#define DEFR6_ODPM12_CDE (3 << 8) +#define DEFR6_ODPM12_MASK (3 << 8) +#define DEFR6_TCNE2 (1 << 6) +#define DEFR6_MLOS1 (1 << 2) +#define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE2) + +/* ----------------------------------------------------------------------------- + * Display Timing Generation Registers + */ + +#define HDSR 0x00040 +#define HDER 0x00044 +#define VDSR 0x00048 +#define VDER 0x0004c +#define HCR 0x00050 +#define HSWR 0x00054 +#define VCR 0x00058 +#define VSPR 0x0005c +#define EQWR 0x00060 +#define SPWR 0x00064 +#define CLAMPSR 0x00070 +#define CLAMPWR 0x00074 +#define DESR 0x00078 +#define DEWR 0x0007c + +/* ----------------------------------------------------------------------------- + * Display Attribute Registers + */ + +#define CP1TR 0x00080 +#define CP2TR 0x00084 +#define CP3TR 0x00088 +#define CP4TR 0x0008c + +#define DOOR 0x00090 +#define DOOR_RGB(r, g, b) (((r) << 18) | ((g) << 10) | ((b) << 2)) +#define CDER 0x00094 +#define CDER_RGB(r, g, b) (((r) << 18) | ((g) << 10) | ((b) << 2)) +#define BPOR 0x00098 +#define BPOR_RGB(r, g, b) (((r) << 18) | ((g) << 10) | ((b) << 2)) + +#define RINTOFSR 0x0009c + +#define DSHPR 0x000c8 +#define DSHPR_CODE (0x7776 << 16) +#define DSHPR_PRIH (0xa << 4) +#define DSHPR_PRIL_BPP16 (0x8 << 0) +#define DSHPR_PRIL_BPP32 (0x9 << 0) + +/* ----------------------------------------------------------------------------- + * Display Plane Registers + */ + +#define PLANE_OFF 0x00100 + +#define PnMR 0x00100 /* plane 1 */ +#define PnMR_VISL_VIN0 (0 << 26) /* use Video Input 0 */ +#define PnMR_VISL_VIN1 (1 << 26) /* use Video Input 1 */ +#define PnMR_VISL_VIN2 (2 << 26) /* use Video Input 2 */ +#define PnMR_VISL_VIN3 (3 << 26) /* use Video Input 3 */ +#define PnMR_YCDF_YUYV (1 << 20) /* YUYV format */ +#define PnMR_TC_R (0 << 17) /* Tranparent color is PnTC1R */ +#define PnMR_TC_CP (1 << 17) /* Tranparent color is color palette */ +#define PnMR_WAE (1 << 16) /* Wrap around Enable */ +#define PnMR_SPIM_TP (0 << 12) /* Transparent Color */ +#define PnMR_SPIM_ALP (1 << 12) /* Alpha Blending */ +#define PnMR_SPIM_EOR (2 << 12) /* EOR */ +#define PnMR_SPIM_TP_OFF (1 << 14) /* No Transparent Color */ +#define PnMR_CPSL_CP1 (0 << 8) /* Color Palette selected 1 */ +#define PnMR_CPSL_CP2 (1 << 8) /* Color Palette selected 2 */ +#define PnMR_CPSL_CP3 (2 << 8) /* Color Palette selected 3 */ +#define PnMR_CPSL_CP4 (3 << 8) /* Color Palette selected 4 */ +#define PnMR_DC (1 << 7) /* Display Area Change */ +#define PnMR_BM_MD (0 << 4) /* Manual Display Change Mode */ +#define PnMR_BM_AR (1 << 4) /* Auto Rendering Mode */ +#define PnMR_BM_AD (2 << 4) /* Auto Display Change Mode */ +#define PnMR_BM_VC (3 << 4) /* Video Capture Mode */ +#define PnMR_DDDF_8BPP (0 << 0) /* 8bit */ +#define PnMR_DDDF_16BPP (1 << 0) /* 16bit or 32bit */ +#define PnMR_DDDF_ARGB (2 << 0) /* ARGB */ +#define PnMR_DDDF_YC (3 << 0) /* YC */ +#define PnMR_DDDF_MASK (3 << 0) + +#define PnMWR 0x00104 + +#define PnALPHAR 0x00108 +#define PnALPHAR_ABIT_1 (0 << 12) +#define PnALPHAR_ABIT_0 (1 << 12) +#define PnALPHAR_ABIT_X (2 << 12) + +#define PnDSXR 0x00110 +#define PnDSYR 0x00114 +#define PnDPXR 0x00118 +#define PnDPYR 0x0011c + +#define PnDSA0R 0x00120 +#define PnDSA1R 0x00124 +#define PnDSA2R 0x00128 +#define PnDSA_MASK 0xfffffff0 + +#define PnSPXR 0x00130 +#define PnSPYR 0x00134 +#define PnWASPR 0x00138 +#define PnWAMWR 0x0013c + +#define PnBTR 0x00140 + +#define PnTC1R 0x00144 +#define PnTC2R 0x00148 +#define PnTC3R 0x0014c +#define PnTC3R_CODE (0x66 << 24) + +#define PnMLR 0x00150 + +#define PnSWAPR 0x00180 +#define PnSWAPR_DIGN (1 << 4) +#define PnSWAPR_SPQW (1 << 3) +#define PnSWAPR_SPLW (1 << 2) +#define PnSWAPR_SPWD (1 << 1) +#define PnSWAPR_SPBY (1 << 0) + +#define PnDDCR 0x00184 +#define PnDDCR_CODE (0x7775 << 16) +#define PnDDCR_LRGB1 (1 << 11) +#define PnDDCR_LRGB0 (1 << 10) + +#define PnDDCR2 0x00188 +#define PnDDCR2_CODE (0x7776 << 16) +#define PnDDCR2_NV21 (1 << 5) +#define PnDDCR2_Y420 (1 << 4) +#define PnDDCR2_DIVU (1 << 1) +#define PnDDCR2_DIVY (1 << 0) + +#define PnDDCR4 0x00190 +#define PnDDCR4_CODE (0x7766 << 16) +#define PnDDCR4_SDFS_RGB (0 << 4) +#define PnDDCR4_SDFS_YC (5 << 4) +#define PnDDCR4_SDFS_MASK (7 << 4) +#define PnDDCR4_EDF_NONE (0 << 0) +#define PnDDCR4_EDF_ARGB8888 (1 << 0) +#define PnDDCR4_EDF_RGB888 (2 << 0) +#define PnDDCR4_EDF_RGB666 (3 << 0) +#define PnDDCR4_EDF_MASK (7 << 0) + +#define APnMR 0x0a100 +#define APnMR_WAE (1 << 16) /* Wrap around Enable */ +#define APnMR_DC (1 << 7) /* Display Area Change */ +#define APnMR_BM_MD (0 << 4) /* Manual Display Change Mode */ +#define APnMR_BM_AD (2 << 4) /* Auto Display Change Mode */ + +#define APnMWR 0x0a104 +#define APnDSA0R 0x0a120 +#define APnDSA1R 0x0a124 +#define APnDSA2R 0x0a128 +#define APnMLR 0x0a150 + +/* ----------------------------------------------------------------------------- + * Display Capture Registers + */ + +#define DCMWR 0x0c104 +#define DC2MWR 0x0c204 +#define DCSAR 0x0c120 +#define DC2SAR 0x0c220 +#define DCMLR 0x0c150 +#define DC2MLR 0x0c250 + +/* ----------------------------------------------------------------------------- + * Color Palette Registers + */ + +#define CP1_000R 0x01000 +#define CP1_255R 0x013fc +#define CP2_000R 0x02000 +#define CP2_255R 0x023fc +#define CP3_000R 0x03000 +#define CP3_255R 0x033fc +#define CP4_000R 0x04000 +#define CP4_255R 0x043fc + +/* ----------------------------------------------------------------------------- + * External Synchronization Control Registers + */ + +#define ESCR 0x10000 +#define ESCR2 0x31000 +#define ESCR_DCLKOINV (1 << 25) +#define ESCR_DCLKSEL_DCLKIN (0 << 20) +#define ESCR_DCLKSEL_CLKS (1 << 20) +#define ESCR_DCLKSEL_MASK (1 << 20) +#define ESCR_DCLKDIS (1 << 16) +#define ESCR_SYNCSEL_OFF (0 << 8) +#define ESCR_SYNCSEL_EXVSYNC (2 << 8) +#define ESCR_SYNCSEL_EXHSYNC (3 << 8) +#define ESCR_FRQSEL_MASK (0x3f << 0) + +#define OTAR 0x10004 +#define OTAR2 0x31004 + +/* ----------------------------------------------------------------------------- + * Dual Display Output Control Registers + */ + +#define DORCR 0x11000 +#define DORCR_PG2T (1 << 30) +#define DORCR_DK2S (1 << 28) +#define DORCR_PG2D_DS1 (0 << 24) +#define DORCR_PG2D_DS2 (1 << 24) +#define DORCR_PG2D_FIX0 (2 << 24) +#define DORCR_PG2D_DOOR (3 << 24) +#define DORCR_PG2D_MASK (3 << 24) +#define DORCR_DR1D (1 << 21) +#define DORCR_PG1D_DS1 (0 << 16) +#define DORCR_PG1D_DS2 (1 << 16) +#define DORCR_PG1D_FIX0 (2 << 16) +#define DORCR_PG1D_DOOR (3 << 16) +#define DORCR_PG1D_MASK (3 << 16) +#define DORCR_RGPV (1 << 4) +#define DORCR_DPRS (1 << 0) + +#define DPTSR 0x11004 +#define DPTSR_PnDK(n) (1 << ((n) + 16)) +#define DPTSR_PnTS(n) (1 << (n)) + +#define DAPTSR 0x11008 +#define DAPTSR_APnDK(n) (1 << ((n) + 16)) +#define DAPTSR_APnTS(n) (1 << (n)) + +#define DS1PR 0x11020 +#define DS2PR 0x11024 + +/* ----------------------------------------------------------------------------- + * YC-RGB Conversion Coefficient Registers + */ + +#define YNCR 0x11080 +#define YNOR 0x11084 +#define CRNOR 0x11088 +#define CBNOR 0x1108c +#define RCRCR 0x11090 +#define GCRCR 0x11094 +#define GCBCR 0x11098 +#define BCBCR 0x1109c + +#endif /* __RCAR_DU_REGS_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.c b/drivers/gpu/drm/rcar-du/rcar_du_vga.c new file mode 100644 index 000000000000..327289ec380d --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_vga.c @@ -0,0 +1,149 @@ +/* + * rcar_du_vga.c -- R-Car Display Unit VGA DAC and Connector + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include "rcar_du_drv.h" +#include "rcar_du_kms.h" +#include "rcar_du_vga.h" + +/* ----------------------------------------------------------------------------- + * Connector + */ + +static int rcar_du_vga_connector_get_modes(struct drm_connector *connector) +{ + return 0; +} + +static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static const struct drm_connector_helper_funcs connector_helper_funcs = { + .get_modes = rcar_du_vga_connector_get_modes, + .mode_valid = rcar_du_vga_connector_mode_valid, + .best_encoder = rcar_du_connector_best_encoder, +}; + +static void rcar_du_vga_connector_destroy(struct drm_connector *connector) +{ + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); +} + +static enum drm_connector_status +rcar_du_vga_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_unknown; +} + +static const struct drm_connector_funcs connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = rcar_du_vga_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = rcar_du_vga_connector_destroy, +}; + +static int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, + struct rcar_du_encoder *renc) +{ + struct rcar_du_connector *rcon; + struct drm_connector *connector; + int ret; + + rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL); + if (rcon == NULL) + return -ENOMEM; + + connector = &rcon->connector; + connector->display_info.width_mm = 0; + connector->display_info.height_mm = 0; + + ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs, + DRM_MODE_CONNECTOR_VGA); + if (ret < 0) + return ret; + + drm_connector_helper_add(connector, &connector_helper_funcs); + ret = drm_sysfs_connector_add(connector); + if (ret < 0) + return ret; + + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + drm_object_property_set_value(&connector->base, + rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); + + ret = drm_mode_connector_attach_encoder(connector, &renc->encoder); + if (ret < 0) + return ret; + + connector->encoder = &renc->encoder; + rcon->encoder = renc; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Encoder + */ + +static void rcar_du_vga_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +} + +static bool rcar_du_vga_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static const struct drm_encoder_helper_funcs encoder_helper_funcs = { + .dpms = rcar_du_vga_encoder_dpms, + .mode_fixup = rcar_du_vga_encoder_mode_fixup, + .prepare = rcar_du_encoder_mode_prepare, + .commit = rcar_du_encoder_mode_commit, + .mode_set = rcar_du_encoder_mode_set, +}; + +static const struct drm_encoder_funcs encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +int rcar_du_vga_init(struct rcar_du_device *rcdu, + const struct rcar_du_encoder_vga_data *data, + unsigned int output) +{ + struct rcar_du_encoder *renc; + int ret; + + renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); + if (renc == NULL) + return -ENOMEM; + + renc->output = output; + + ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs, + DRM_MODE_ENCODER_DAC); + if (ret < 0) + return ret; + + drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); + + return rcar_du_vga_connector_init(rcdu, renc); +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.h b/drivers/gpu/drm/rcar-du/rcar_du_vga.h new file mode 100644 index 000000000000..66b4d2d7190d --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_vga.h @@ -0,0 +1,24 @@ +/* + * rcar_du_vga.h -- R-Car Display Unit VGA DAC and Connector + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_VGA_H__ +#define __RCAR_DU_VGA_H__ + +struct rcar_du_device; +struct rcar_du_encoder_vga_data; + +int rcar_du_vga_init(struct rcar_du_device *rcdu, + const struct rcar_du_encoder_vga_data *data, + unsigned int output); + +#endif /* __RCAR_DU_VGA_H__ */ diff --git a/include/linux/platform_data/rcar-du.h b/include/linux/platform_data/rcar-du.h new file mode 100644 index 000000000000..80587fdbba3e --- /dev/null +++ b/include/linux/platform_data/rcar-du.h @@ -0,0 +1,54 @@ +/* + * rcar_du.h -- R-Car Display Unit DRM driver + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_H__ +#define __RCAR_DU_H__ + +#include + +enum rcar_du_encoder_type { + RCAR_DU_ENCODER_UNUSED = 0, + RCAR_DU_ENCODER_VGA, + RCAR_DU_ENCODER_LVDS, +}; + +struct rcar_du_panel_data { + unsigned int width_mm; /* Panel width in mm */ + unsigned int height_mm; /* Panel height in mm */ + struct drm_mode_modeinfo mode; +}; + +struct rcar_du_encoder_lvds_data { + struct rcar_du_panel_data panel; +}; + +struct rcar_du_encoder_vga_data { + /* TODO: Add DDC information for EDID retrieval */ +}; + +struct rcar_du_encoder_data { + enum rcar_du_encoder_type encoder; + unsigned int output; + + union { + struct rcar_du_encoder_lvds_data lvds; + struct rcar_du_encoder_vga_data vga; + } u; +}; + +struct rcar_du_platform_data { + struct rcar_du_encoder_data *encoders; + unsigned int num_encoders; +}; + +#endif /* __RCAR_DU_H__ */ -- cgit v1.3-18-gd494 From 0ddf03c95bbb4f4ed57281fa7b781472950df749 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 5 Jun 2013 15:13:26 +0200 Subject: mmc: esdhc-imx: parse max-frequency from devicetree In order to make it possible to reduce the SD bus frequency, parse the optional "max-frequency" attribute as documented in devicetree/bindings/mmc/mmc.txt Signed-off-by: Lucas Stach Acked-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 18 +++++++++++++++++- include/linux/platform_data/mmc-esdhc-imx.h | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include/linux/platform_data') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index eb1310ca021e..1dd5ba858754 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -384,6 +384,20 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) } } +static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + struct esdhc_platform_data *boarddata = &imx_data->boarddata; + + u32 f_host = clk_get_rate(pltfm_host->clk); + + if (boarddata->f_max && (boarddata->f_max < f_host)) + return boarddata->f_max; + else + return f_host; +} + static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -447,7 +461,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { .write_w = esdhc_writew_le, .write_b = esdhc_writeb_le, .set_clock = esdhc_pltfm_set_clock, - .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .get_max_clock = esdhc_pltfm_get_max_clock, .get_min_clock = esdhc_pltfm_get_min_clock, .get_ro = esdhc_pltfm_get_ro, .platform_bus_width = esdhc_pltfm_bus_width, @@ -490,6 +504,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, of_property_read_u32(np, "bus-width", &boarddata->max_bus_width); + of_property_read_u32(np, "max-frequency", &boarddata->f_max); + return 0; } #else diff --git a/include/linux/platform_data/mmc-esdhc-imx.h b/include/linux/platform_data/mmc-esdhc-imx.h index b4a0521ce411..d44912d81578 100644 --- a/include/linux/platform_data/mmc-esdhc-imx.h +++ b/include/linux/platform_data/mmc-esdhc-imx.h @@ -40,5 +40,6 @@ struct esdhc_platform_data { enum wp_types wp_type; enum cd_types cd_type; int max_bus_width; + unsigned int f_max; }; #endif /* __ASM_ARCH_IMX_ESDHC_H */ -- cgit v1.3-18-gd494 From 594fbe713bf60073ed884dc317a74dd5b327aba7 Mon Sep 17 00:00:00 2001 From: Arnaud Ebalard Date: Thu, 20 Jun 2013 22:21:04 +0200 Subject: Add support for GMT G762/G763 PWM fan controllers GMT G762/763 fan speed PWM controller is connected directly to a fan and performs closed-loop or open-loop control of the fan speed. Two modes - PWM or DC - are supported by the chip. Introduced driver provides various knobs to control the operations of the chip (via sysfs interface). Specific characteristics of the system can be passed either using board init code or via DT. Documentation for both the driver and DT bindings are also provided. Signed-off-by: Arnaud Ebalard Tested-by: Simon Guinot Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/g762.txt | 47 + Documentation/hwmon/g762 | 65 ++ drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/g762.c | 1149 ++++++++++++++++++++++ include/linux/platform_data/g762.h | 37 + 6 files changed, 1309 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/g762.txt create mode 100644 Documentation/hwmon/g762 create mode 100644 drivers/hwmon/g762.c create mode 100644 include/linux/platform_data/g762.h (limited to 'include/linux/platform_data') diff --git a/Documentation/devicetree/bindings/hwmon/g762.txt b/Documentation/devicetree/bindings/hwmon/g762.txt new file mode 100644 index 000000000000..25cc6d8ee575 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/g762.txt @@ -0,0 +1,47 @@ +GMT G762/G763 PWM Fan controller + +Required node properties: + + - "compatible": must be either "gmt,g762" or "gmt,g763" + - "reg": I2C bus address of the device + - "clocks": a fixed clock providing input clock frequency + on CLK pin of the chip. + +Optional properties: + + - "fan_startv": fan startup voltage. Accepted values are 0, 1, 2 and 3. + The higher the more. + + - "pwm_polarity": pwm polarity. Accepted values are 0 (positive duty) + and 1 (negative duty). + + - "fan_gear_mode": fan gear mode. Supported values are 0, 1 and 2. + +If an optional property is not set in .dts file, then current value is kept +unmodified (e.g. u-boot installed value). + +Additional information on operational parameters for the device is available +in Documentation/hwmon/g762. A detailed datasheet for the device is available +at http://natisbad.org/NAS/refs/GMT_EDS-762_763-080710-0.2.pdf. + +Example g762 node: + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + g762_clk: fixedclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <8192>; + } + } + + g762: g762@3e { + compatible = "gmt,g762"; + reg = <0x3e>; + clocks = <&g762_clk> + fan_gear_mode = <0>; /* chip default */ + fan_startv = <1>; /* chip default */ + pwm_polarity = <0>; /* chip default */ + }; diff --git a/Documentation/hwmon/g762 b/Documentation/hwmon/g762 new file mode 100644 index 000000000000..923db9c5b5bc --- /dev/null +++ b/Documentation/hwmon/g762 @@ -0,0 +1,65 @@ +Kernel driver g762 +================== + +The GMT G762 Fan Speed PWM Controller is connected directly to a fan +and performs closed-loop or open-loop control of the fan speed. Two +modes - PWM or DC - are supported by the device. + +For additional information, a detailed datasheet is available at +http://natisbad.org/NAS/ref/GMT_EDS-762_763-080710-0.2.pdf. sysfs +bindings are described in Documentation/hwmon/sysfs-interface. + +The following entries are available to the user in a subdirectory of +/sys/bus/i2c/drivers/g762/ to control the operation of the device. +This can be done manually using the following entries but is usually +done via a userland daemon like fancontrol. + +Note that those entries do not provide ways to setup the specific +hardware characteristics of the system (reference clock, pulses per +fan revolution, ...); Those can be modified via devicetree bindings +documented in Documentation/devicetree/bindings/hwmon/g762.txt or +using a specific platform_data structure in board initialization +file (see include/linux/platform_data/g762.h). + + fan1_target: set desired fan speed. This only makes sense in closed-loop + fan speed control (i.e. when pwm1_enable is set to 2). + + fan1_input: provide current fan rotation value in RPM as reported by + the fan to the device. + + fan1_div: fan clock divisor. Supported value are 1, 2, 4 and 8. + + fan1_pulses: number of pulses per fan revolution. Supported values + are 2 and 4. + + fan1_fault: reports fan failure, i.e. no transition on fan gear pin for + about 0.7s (if the fan is not voluntarily set off). + + fan1_alarm: in closed-loop control mode, if fan RPM value is 25% out + of the programmed value for over 6 seconds 'fan1_alarm' is + set to 1. + + pwm1_enable: set current fan speed control mode i.e. 1 for manual fan + speed control (open-loop) via pwm1 described below, 2 for + automatic fan speed control (closed-loop) via fan1_target + above. + + pwm1_mode: set or get fan driving mode: 1 for PWM mode, 0 for DC mode. + + pwm1: get or set PWM fan control value in open-loop mode. This is an + integer value between 0 and 255. 0 stops the fan, 255 makes + it run at full speed. + +Both in PWM mode ('pwm1_mode' set to 1) and DC mode ('pwm1_mode' set to 0), +when current fan speed control mode is open-loop ('pwm1_enable' set to 1), +the fan speed is programmed by setting a value between 0 and 255 via 'pwm1' +entry (0 stops the fan, 255 makes it run at full speed). In closed-loop mode +('pwm1_enable' set to 2), the expected rotation speed in RPM can be passed to +the chip via 'fan1_target'. In closed-loop mode, the target speed is compared +with current speed (available via 'fan1_input') by the device and a feedback +is performed to match that target value. The fan speed value is computed +based on the parameters associated with the physical characteristics of the +system: a reference clock source frequency, a number of pulses per fan +revolution, etc. + +Note that the driver will update its values at most once per second. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 683c769a8fca..e989f7fd645b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -461,6 +461,16 @@ config SENSORS_G760A This driver can also be built as a module. If so, the module will be called g760a. +config SENSORS_G762 + tristate "GMT G762 and G763" + depends on I2C + help + If you say yes here you get support for Global Mixed-mode + Technology Inc G762 and G763 fan speed PWM controller chips. + + This driver can also be built as a module. If so, the module + will be called g762. + config SENSORS_GL518SM tristate "Genesys Logic GL518SM" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index d17d3e64f9f4..4f0fb5235f42 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_SENSORS_F75375S) += f75375s.o obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o obj-$(CONFIG_SENSORS_G760A) += g760a.o +obj-$(CONFIG_SENSORS_G762) += g762.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c new file mode 100644 index 000000000000..73adf01b0ef2 --- /dev/null +++ b/drivers/hwmon/g762.c @@ -0,0 +1,1149 @@ +/* + * g762 - Driver for the Global Mixed-mode Technology Inc. fan speed + * PWM controller chips from G762 family, i.e. G762 and G763 + * + * Copyright (C) 2013, Arnaud EBALARD + * + * This work is based on a basic version for 2.6.31 kernel developed + * by Olivier Mouchet for LaCie. Updates and correction have been + * performed to run on recent kernels. Additional features, like the + * ability to configure various characteristics via .dts file or + * board init file have been added. Detailed datasheet on which this + * development is based is available here: + * + * http://natisbad.org/NAS/refs/GMT_EDS-762_763-080710-0.2.pdf + * + * Headers from previous developments have been kept below: + * + * Copyright (c) 2009 LaCie + * + * Author: Olivier Mouchet + * + * based on g760a code written by Herbert Valerio Riedel + * Copyright (C) 2007 Herbert Valerio Riedel + * + * g762: minimal datasheet available at: + * http://www.gmt.com.tw/product/datasheet/EDS-762_3.pdf + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "g762" + +static const struct i2c_device_id g762_id[] = { + { "g762", 0 }, + { "g763", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, g762_id); + +enum g762_regs { + G762_REG_SET_CNT = 0x00, + G762_REG_ACT_CNT = 0x01, + G762_REG_FAN_STA = 0x02, + G762_REG_SET_OUT = 0x03, + G762_REG_FAN_CMD1 = 0x04, + G762_REG_FAN_CMD2 = 0x05, +}; + +/* Config register bits */ +#define G762_REG_FAN_CMD1_DET_FAN_FAIL 0x80 /* enable fan_fail signal */ +#define G762_REG_FAN_CMD1_DET_FAN_OOC 0x40 /* enable fan_out_of_control */ +#define G762_REG_FAN_CMD1_OUT_MODE 0x20 /* out mode: PWM or DC */ +#define G762_REG_FAN_CMD1_FAN_MODE 0x10 /* fan mode: closed/open-loop */ +#define G762_REG_FAN_CMD1_CLK_DIV_ID1 0x08 /* clock divisor value */ +#define G762_REG_FAN_CMD1_CLK_DIV_ID0 0x04 +#define G762_REG_FAN_CMD1_PWM_POLARITY 0x02 /* PWM polarity */ +#define G762_REG_FAN_CMD1_PULSE_PER_REV 0x01 /* pulse per fan revolution */ + +#define G762_REG_FAN_CMD2_GEAR_MODE_1 0x08 /* fan gear mode */ +#define G762_REG_FAN_CMD2_GEAR_MODE_0 0x04 +#define G762_REG_FAN_CMD2_FAN_STARTV_1 0x02 /* fan startup voltage */ +#define G762_REG_FAN_CMD2_FAN_STARTV_0 0x01 + +#define G762_REG_FAN_STA_FAIL 0x02 /* fan fail */ +#define G762_REG_FAN_STA_OOC 0x01 /* fan out of control */ + +/* Config register values */ +#define G762_OUT_MODE_PWM 1 +#define G762_OUT_MODE_DC 0 + +#define G762_FAN_MODE_CLOSED_LOOP 2 +#define G762_FAN_MODE_OPEN_LOOP 1 + +#define G762_PWM_POLARITY_NEGATIVE 1 +#define G762_PWM_POLARITY_POSITIVE 0 + +/* Register data is read (and cached) at most once per second. */ +#define G762_UPDATE_INTERVAL HZ + +/* + * Extract pulse count per fan revolution value (2 or 4) from given + * FAN_CMD1 register value. + */ +#define G762_PULSE_FROM_REG(reg) \ + ((((reg) & G762_REG_FAN_CMD1_PULSE_PER_REV) + 1) << 1) + +/* + * Extract fan clock divisor (1, 2, 4 or 8) from given FAN_CMD1 + * register value. + */ +#define G762_CLKDIV_FROM_REG(reg) \ + (1 << (((reg) & (G762_REG_FAN_CMD1_CLK_DIV_ID0 | \ + G762_REG_FAN_CMD1_CLK_DIV_ID1)) >> 2)) + +/* + * Extract fan gear mode multiplier value (0, 2 or 4) from given + * FAN_CMD2 register value. + */ +#define G762_GEARMULT_FROM_REG(reg) \ + (1 << (((reg) & (G762_REG_FAN_CMD2_GEAR_MODE_0 | \ + G762_REG_FAN_CMD2_GEAR_MODE_1)) >> 2)) + +struct g762_data { + struct i2c_client *client; + struct device *hwmon_dev; + struct clk *clk; + + /* update mutex */ + struct mutex update_lock; + + /* board specific parameters. */ + u32 clk_freq; + + /* g762 register cache */ + bool valid; + unsigned long last_updated; /* in jiffies */ + + u8 set_cnt; /* controls fan rotation speed in closed-loop mode */ + u8 act_cnt; /* provides access to current fan RPM value */ + u8 fan_sta; /* bit 0: set when actual fan speed is more than + * 25% outside requested fan speed + * bit 1: set when no transition occurs on fan + * pin for 0.7s + */ + u8 set_out; /* controls fan rotation speed in open-loop mode */ + u8 fan_cmd1; /* 0: FG_PLS_ID0 FG pulses count per revolution + * 0: 2 counts per revolution + * 1: 4 counts per revolution + * 1: PWM_POLARITY 1: negative_duty + * 0: positive_duty + * 2,3: [FG_CLOCK_ID0, FG_CLK_ID1] + * 00: Divide fan clock by 1 + * 01: Divide fan clock by 2 + * 10: Divide fan clock by 4 + * 11: Divide fan clock by 8 + * 4: FAN_MODE 1:closed-loop, 0:open-loop + * 5: OUT_MODE 1:PWM, 0:DC + * 6: DET_FAN_OOC enable "fan ooc" status + * 7: DET_FAN_FAIL enable "fan fail" status + */ + u8 fan_cmd2; /* 0,1: FAN_STARTV 0,1,2,3 -> 0,32,64,96 dac_code + * 2,3: FG_GEAR_MODE + * 00: multiplier = 1 + * 01: multiplier = 2 + * 10: multiplier = 4 + * 4: Mask ALERT# (g763 only) + */ +}; + +/* + * Convert count value from fan controller register (FAN_SET_CNT) into fan + * speed RPM value. Note that the datasheet documents a basic formula; + * influence of additional parameters (fan clock divisor, fan gear mode) + * have been infered from examples in the datasheet and tests. + */ +static inline unsigned int rpm_from_cnt(u8 cnt, u32 clk_freq, u16 p, + u8 clk_div, u8 gear_mult) +{ + if (cnt == 0xff) /* setting cnt to 255 stops the fan */ + return 0; + + return (clk_freq * 30 * gear_mult) / ((cnt ? cnt : 1) * p * clk_div); +} + +/* + * Convert fan RPM value from sysfs into count value for fan controller + * register (FAN_SET_CNT). + */ +static inline unsigned char cnt_from_rpm(u32 rpm, u32 clk_freq, u16 p, + u8 clk_div, u8 gear_mult) +{ + if (!rpm) /* to stop the fan, set cnt to 255 */ + return 0xff; + + return clamp_val(((clk_freq * 30 * gear_mult) / (rpm * p * clk_div)), + 0, 255); +} + +/* helper to grab and cache data, at most one time per second */ +static struct g762_data *g762_update_client(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = i2c_get_clientdata(client); + int ret = 0; + + mutex_lock(&data->update_lock); + if (time_before(jiffies, data->last_updated + G762_UPDATE_INTERVAL) && + likely(data->valid)) + goto out; + + ret = i2c_smbus_read_byte_data(client, G762_REG_SET_CNT); + if (ret < 0) + goto out; + data->set_cnt = ret; + + ret = i2c_smbus_read_byte_data(client, G762_REG_ACT_CNT); + if (ret < 0) + goto out; + data->act_cnt = ret; + + ret = i2c_smbus_read_byte_data(client, G762_REG_FAN_STA); + if (ret < 0) + goto out; + data->fan_sta = ret; + + ret = i2c_smbus_read_byte_data(client, G762_REG_SET_OUT); + if (ret < 0) + goto out; + data->set_out = ret; + + ret = i2c_smbus_read_byte_data(client, G762_REG_FAN_CMD1); + if (ret < 0) + goto out; + data->fan_cmd1 = ret; + + ret = i2c_smbus_read_byte_data(client, G762_REG_FAN_CMD2); + if (ret < 0) + goto out; + data->fan_cmd2 = ret; + + data->last_updated = jiffies; + data->valid = true; + out: + mutex_unlock(&data->update_lock); + + if (ret < 0) /* upon error, encode it in return value */ + data = ERR_PTR(ret); + + return data; +} + +/* helpers for writing hardware parameters */ + +/* + * Set input clock frequency received on CLK pin of the chip. Accepted values + * are between 0 and 0xffffff. If zero is given, then default frequency + * (32,768Hz) is used. Note that clock frequency is a characteristic of the + * system but an internal parameter, i.e. value is not passed to the device. + */ +static int do_set_clk_freq(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = i2c_get_clientdata(client); + + if (val > 0xffffff) + return -EINVAL; + if (!val) + val = 32768; + + data->clk_freq = val; + + return 0; +} + +/* Set pwm mode. Accepts either 0 (PWM mode) or 1 (DC mode) */ +static int do_set_pwm_mode(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case G762_OUT_MODE_PWM: + data->fan_cmd1 |= G762_REG_FAN_CMD1_OUT_MODE; + break; + case G762_OUT_MODE_DC: + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_OUT_MODE; + break; + default: + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD1, + data->fan_cmd1); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* Set fan clock divisor. Accepts either 1, 2, 4 or 8. */ +static int do_set_fan_div(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case 1: + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_CLK_DIV_ID0; + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_CLK_DIV_ID1; + break; + case 2: + data->fan_cmd1 |= G762_REG_FAN_CMD1_CLK_DIV_ID0; + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_CLK_DIV_ID1; + break; + case 4: + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_CLK_DIV_ID0; + data->fan_cmd1 |= G762_REG_FAN_CMD1_CLK_DIV_ID1; + break; + case 8: + data->fan_cmd1 |= G762_REG_FAN_CMD1_CLK_DIV_ID0; + data->fan_cmd1 |= G762_REG_FAN_CMD1_CLK_DIV_ID1; + break; + default: + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD1, + data->fan_cmd1); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* Set fan gear mode. Accepts either 0, 1 or 2. */ +static int do_set_fan_gear_mode(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case 0: + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_GEAR_MODE_0; + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_GEAR_MODE_1; + break; + case 1: + data->fan_cmd2 |= G762_REG_FAN_CMD2_GEAR_MODE_0; + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_GEAR_MODE_1; + break; + case 2: + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_GEAR_MODE_0; + data->fan_cmd2 |= G762_REG_FAN_CMD2_GEAR_MODE_1; + break; + default: + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD2, + data->fan_cmd2); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* Set number of fan pulses per revolution. Accepts either 2 or 4. */ +static int do_set_fan_pulses(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case 2: + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_PULSE_PER_REV; + break; + case 4: + data->fan_cmd1 |= G762_REG_FAN_CMD1_PULSE_PER_REV; + break; + default: + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD1, + data->fan_cmd1); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* Set fan mode. Accepts either 1 (open-loop) or 2 (closed-loop). */ +static int do_set_pwm_enable(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case G762_FAN_MODE_CLOSED_LOOP: + data->fan_cmd1 |= G762_REG_FAN_CMD1_FAN_MODE; + break; + case G762_FAN_MODE_OPEN_LOOP: + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_FAN_MODE; + /* + * BUG FIX: if SET_CNT register value is 255 then, for some + * unknown reason, fan will not rotate as expected, no matter + * the value of SET_OUT (to be specific, this seems to happen + * only in PWM mode). To workaround this bug, we give SET_CNT + * value of 254 if it is 255 when switching to open-loop. + */ + if (data->set_cnt == 0xff) + i2c_smbus_write_byte_data(client, G762_REG_SET_CNT, + 254); + break; + default: + ret = -EINVAL; + goto out; + } + + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD1, + data->fan_cmd1); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* Set PWM polarity. Accepts either 0 (positive duty) or 1 (negative duty) */ +static int do_set_pwm_polarity(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case G762_PWM_POLARITY_POSITIVE: + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_PWM_POLARITY; + break; + case G762_PWM_POLARITY_NEGATIVE: + data->fan_cmd1 |= G762_REG_FAN_CMD1_PWM_POLARITY; + break; + default: + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD1, + data->fan_cmd1); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* + * Set pwm value. Accepts values between 0 (stops the fan) and + * 255 (full speed). This only makes sense in open-loop mode. + */ +static int do_set_pwm(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = i2c_get_clientdata(client); + int ret; + + if (val > 255) + return -EINVAL; + + mutex_lock(&data->update_lock); + ret = i2c_smbus_write_byte_data(client, G762_REG_SET_OUT, val); + data->valid = false; + mutex_unlock(&data->update_lock); + + return ret; +} + +/* + * Set fan RPM value. Can be called both in closed and open-loop mode + * but effect will only be seen after closed-loop mode is configured. + */ +static int do_set_fan_target(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + data->set_cnt = cnt_from_rpm(val, data->clk_freq, + G762_PULSE_FROM_REG(data->fan_cmd1), + G762_CLKDIV_FROM_REG(data->fan_cmd1), + G762_GEARMULT_FROM_REG(data->fan_cmd2)); + ret = i2c_smbus_write_byte_data(client, G762_REG_SET_CNT, + data->set_cnt); + data->valid = false; + mutex_unlock(&data->update_lock); + + return ret; +} + +/* Set fan startup voltage. Accepted values are either 0, 1, 2 or 3. */ +static int do_set_fan_startv(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case 0: + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_FAN_STARTV_0; + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_FAN_STARTV_1; + break; + case 1: + data->fan_cmd2 |= G762_REG_FAN_CMD2_FAN_STARTV_0; + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_FAN_STARTV_1; + break; + case 2: + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_FAN_STARTV_0; + data->fan_cmd2 |= G762_REG_FAN_CMD2_FAN_STARTV_1; + break; + case 3: + data->fan_cmd2 |= G762_REG_FAN_CMD2_FAN_STARTV_0; + data->fan_cmd2 |= G762_REG_FAN_CMD2_FAN_STARTV_1; + break; + default: + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD2, + data->fan_cmd2); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* + * Helper to import hardware characteristics from .dts file and push + * those to the chip. + */ + +#ifdef CONFIG_OF +static struct of_device_id g762_dt_match[] = { + { .compatible = "gmt,g762" }, + { .compatible = "gmt,g763" }, + { }, +}; + +/* + * Grab clock (a required property), enable it, get (fixed) clock frequency + * and store it. Note: upon success, clock has been prepared and enabled; it + * must later be unprepared and disabled (e.g. during module unloading) by a + * call to g762_of_clock_disable(). Note that a reference to clock is kept + * in our private data structure to be used in this function. + */ +static int g762_of_clock_enable(struct i2c_client *client) +{ + struct g762_data *data; + unsigned long clk_freq; + struct clk *clk; + int ret; + + if (!client->dev.of_node) + return 0; + + clk = of_clk_get(client->dev.of_node, 0); + if (IS_ERR(clk)) { + dev_err(&client->dev, "failed to get clock\n"); + return PTR_ERR(clk); + } + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(&client->dev, "failed to enable clock\n"); + goto clk_put; + } + + clk_freq = clk_get_rate(clk); + ret = do_set_clk_freq(&client->dev, clk_freq); + if (ret) { + dev_err(&client->dev, "invalid clock freq %lu\n", clk_freq); + goto clk_unprep; + } + + data = i2c_get_clientdata(client); + data->clk = clk; + + return 0; + + clk_unprep: + clk_disable_unprepare(clk); + + clk_put: + clk_put(clk); + + return ret; +} + +static void g762_of_clock_disable(struct i2c_client *client) +{ + struct g762_data *data = i2c_get_clientdata(client); + + if (!data->clk) + return; + + clk_disable_unprepare(data->clk); + clk_put(data->clk); +} + +static int g762_of_prop_import_one(struct i2c_client *client, + const char *pname, + int (*psetter)(struct device *dev, + unsigned long val)) +{ + const __be32 *prop; + int len, ret; + u32 pval; + + prop = of_get_property(client->dev.of_node, pname, &len); + if (!prop || len != sizeof(u32)) + return 0; + + pval = be32_to_cpu(prop[0]); + dev_dbg(&client->dev, "found %s (%d)\n", pname, pval); + ret = (*psetter)(&client->dev, pval); + if (ret) + dev_err(&client->dev, "unable to set %s (%d)\n", pname, pval); + + return ret; +} + +static int g762_of_prop_import(struct i2c_client *client) +{ + int ret; + + if (!client->dev.of_node) + return 0; + + ret = g762_of_prop_import_one(client, "fan_gear_mode", + do_set_fan_gear_mode); + if (ret) + return ret; + + ret = g762_of_prop_import_one(client, "pwm_polarity", + do_set_pwm_polarity); + if (ret) + return ret; + + return g762_of_prop_import_one(client, "fan_startv", + do_set_fan_startv); +} + +#else +static int g762_of_prop_import(struct i2c_client *client) +{ + return 0; +} + +static int g762_of_clock_enable(struct i2c_client *client) +{ + return 0; +} + +static void g762_of_clock_disable(struct i2c_client *client) { } +#endif + +/* + * Helper to import hardware characteristics from .dts file and push + * those to the chip. + */ + +static int g762_pdata_prop_import(struct i2c_client *client) +{ + struct g762_platform_data *pdata = client->dev.platform_data; + int ret; + + if (!pdata) + return 0; + + ret = do_set_fan_gear_mode(&client->dev, pdata->fan_gear_mode); + if (ret) + return ret; + + ret = do_set_pwm_polarity(&client->dev, pdata->pwm_polarity); + if (ret) + return ret; + + ret = do_set_fan_startv(&client->dev, pdata->fan_startv); + if (ret) + return ret; + + return do_set_clk_freq(&client->dev, pdata->clk_freq); +} + +/* + * sysfs attributes + */ + +/* + * Read function for fan1_input sysfs file. Return current fan RPM value, or + * 0 if fan is out of control. + */ +static ssize_t get_fan_rpm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct g762_data *data = g762_update_client(dev); + unsigned int rpm = 0; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + /* reverse logic: fan out of control reporting is enabled low */ + if (data->fan_sta & G762_REG_FAN_STA_OOC) { + rpm = rpm_from_cnt(data->act_cnt, data->clk_freq, + G762_PULSE_FROM_REG(data->fan_cmd1), + G762_CLKDIV_FROM_REG(data->fan_cmd1), + G762_GEARMULT_FROM_REG(data->fan_cmd2)); + } + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", rpm); +} + +/* + * Read and write functions for pwm1_mode sysfs file. Get and set fan speed + * control mode i.e. PWM (1) or DC (0). + */ +static ssize_t get_pwm_mode(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", + !!(data->fan_cmd1 & G762_REG_FAN_CMD1_OUT_MODE)); +} + +static ssize_t set_pwm_mode(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + ret = do_set_pwm_mode(dev, val); + if (ret < 0) + return ret; + + return count; +} + +/* + * Read and write functions for fan1_div sysfs file. Get and set fan + * controller prescaler value + */ +static ssize_t get_fan_div(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", G762_CLKDIV_FROM_REG(data->fan_cmd1)); +} + +static ssize_t set_fan_div(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + ret = do_set_fan_div(dev, val); + if (ret < 0) + return ret; + + return count; +} + +/* + * Read and write functions for fan1_pulses sysfs file. Get and set number + * of tachometer pulses per fan revolution. + */ +static ssize_t get_fan_pulses(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", G762_PULSE_FROM_REG(data->fan_cmd1)); +} + +static ssize_t set_fan_pulses(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + ret = do_set_fan_pulses(dev, val); + if (ret < 0) + return ret; + + return count; +} + +/* + * Read and write functions for pwm1_enable. Get and set fan speed control mode + * (i.e. closed or open-loop). + * + * Following documentation about hwmon's sysfs interface, a pwm1_enable node + * should accept followings: + * + * 0 : no fan speed control (i.e. fan at full speed) + * 1 : manual fan speed control enabled (use pwm[1-*]) (open-loop) + * 2+: automatic fan speed control enabled (use fan[1-*]_target) (closed-loop) + * + * but we do not accept 0 as this mode is not natively supported by the chip + * and it is not emulated by g762 driver. -EINVAL is returned in this case. + */ +static ssize_t get_pwm_enable(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", + (!!(data->fan_cmd1 & G762_REG_FAN_CMD1_FAN_MODE)) + 1); +} + +static ssize_t set_pwm_enable(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + ret = do_set_pwm_enable(dev, val); + if (ret < 0) + return ret; + + return count; +} + +/* + * Read and write functions for pwm1 sysfs file. Get and set pwm value + * (which affects fan speed) in open-loop mode. 0 stops the fan and 255 + * makes it run at full speed. + */ +static ssize_t get_pwm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->set_out); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + ret = do_set_pwm(dev, val); + if (ret < 0) + return ret; + + return count; +} + +/* + * Read and write function for fan1_target sysfs file. Get/set the fan speed in + * closed-loop mode. Speed is given as a RPM value; then the chip will regulate + * the fan speed using pulses from fan tachometer. + * + * Refer to rpm_from_cnt() implementation above to get info about count number + * calculation. + * + * Also note that due to rounding errors it is possible that you don't read + * back exactly the value you have set. + */ +static ssize_t get_fan_target(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct g762_data *data = g762_update_client(dev); + unsigned int rpm; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + rpm = rpm_from_cnt(data->set_cnt, data->clk_freq, + G762_PULSE_FROM_REG(data->fan_cmd1), + G762_CLKDIV_FROM_REG(data->fan_cmd1), + G762_GEARMULT_FROM_REG(data->fan_cmd2)); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", rpm); +} + +static ssize_t set_fan_target(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + ret = do_set_fan_target(dev, val); + if (ret < 0) + return ret; + + return count; +} + +/* read function for fan1_fault sysfs file. */ +static ssize_t get_fan_failure(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%u\n", !!(data->fan_sta & G762_REG_FAN_STA_FAIL)); +} + +/* + * read function for fan1_alarm sysfs file. Note that OOC condition is + * enabled low + */ +static ssize_t get_fan_ooc(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%u\n", !(data->fan_sta & G762_REG_FAN_STA_OOC)); +} + +static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm); +static DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, get_pwm_mode, set_pwm_mode); +static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable); +static DEVICE_ATTR(fan1_input, S_IRUGO, get_fan_rpm, NULL); +static DEVICE_ATTR(fan1_alarm, S_IRUGO, get_fan_ooc, NULL); +static DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_failure, NULL); +static DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target); +static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_fan_div, set_fan_div); +static DEVICE_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, + get_fan_pulses, set_fan_pulses); + +/* Driver data */ +static struct attribute *g762_attributes[] = { + &dev_attr_fan1_input.attr, + &dev_attr_fan1_alarm.attr, + &dev_attr_fan1_fault.attr, + &dev_attr_fan1_target.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan1_pulses.attr, + &dev_attr_pwm1.attr, + &dev_attr_pwm1_mode.attr, + &dev_attr_pwm1_enable.attr, + NULL +}; + +static const struct attribute_group g762_group = { + .attrs = g762_attributes, +}; + +/* + * Enable both fan failure detection and fan out of control protection. The + * function does not protect change/access to data structure; it must thus + * only be called during initialization. + */ +static inline int g762_fan_init(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_FAIL; + data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_OOC; + data->valid = false; + + return i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD1, + data->fan_cmd1); +} + +static int g762_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct g762_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(struct g762_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->client = client; + mutex_init(&data->update_lock); + + /* Enable fan failure detection and fan out of control protection */ + ret = g762_fan_init(&client->dev); + if (ret) + return ret; + + /* Get configuration via DT ... */ + ret = g762_of_clock_enable(client); + if (ret) + return ret; + ret = g762_of_prop_import(client); + if (ret) + goto clock_dis; + /* ... or platform_data */ + ret = g762_pdata_prop_import(client); + if (ret) + goto clock_dis; + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, &g762_group); + if (ret) + goto clock_dis; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto sysfs_rem; + } + + return 0; + + sysfs_rem: + sysfs_remove_group(&client->dev.kobj, &g762_group); + + clock_dis: + g762_of_clock_disable(client); + + return ret; +} + +static int g762_remove(struct i2c_client *client) +{ + struct g762_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &g762_group); + g762_of_clock_disable(client); + + return 0; +} + +static struct i2c_driver g762_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(g762_dt_match), + }, + .probe = g762_probe, + .remove = g762_remove, + .id_table = g762_id, +}; + +module_i2c_driver(g762_driver); + +MODULE_AUTHOR("Arnaud EBALARD "); +MODULE_DESCRIPTION("GMT G762/G763 driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/g762.h b/include/linux/platform_data/g762.h new file mode 100644 index 000000000000..d3c51283764d --- /dev/null +++ b/include/linux/platform_data/g762.h @@ -0,0 +1,37 @@ +/* + * Platform data structure for g762 fan controller driver + * + * Copyright (C) 2013, Arnaud EBALARD + * + * 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 + */ +#ifndef __LINUX_PLATFORM_DATA_G762_H__ +#define __LINUX_PLATFORM_DATA_G762_H__ + +/* + * Following structure can be used to set g762 driver platform specific data + * during board init. Note that passing a sparse structure is possible but + * will result in non-specified attributes to be set to default value, hence + * overloading those installed during boot (e.g. by u-boot). + */ + +struct g762_platform_data { + u32 fan_startv; + u32 fan_gear_mode; + u32 pwm_polarity; + u32 clk_freq; +}; + +#endif /* __LINUX_PLATFORM_DATA_G762_H__ */ -- cgit v1.3-18-gd494 From 3b81a6809480f3fc7d9d06562704c8df18ecec00 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Wed, 26 Jun 2013 14:20:18 +0200 Subject: brcmfmac: add broken scatter-gather DMA support DMA engine of some old SDIO host controllers require block size alignment for data length of each scatterlist item. This patch introduces an intermediate buffer list to support this kind of platform. It decreases the throughput because of an extra memcpy in critical data path. So don't turn this on unless it's necessary. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Arend van Spriel Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 78 ++++++++++++++++++++---- include/linux/platform_data/brcmfmac-sdio.h | 5 ++ 2 files changed, 72 insertions(+), 11 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 70cd0e9cd52b..e3f3c48f86d4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -331,10 +331,11 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, bool write, u32 addr, struct sk_buff_head *pktlist) { unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset; - unsigned int max_blks, max_req_sz; + unsigned int max_blks, max_req_sz, orig_offset, dst_offset; unsigned short max_seg_sz, seg_sz; - unsigned char *pkt_data; - struct sk_buff *pkt_next = NULL; + unsigned char *pkt_data, *orig_data, *dst_data; + struct sk_buff *pkt_next = NULL, *local_pkt_next; + struct sk_buff_head local_list, *target_list; struct mmc_request mmc_req; struct mmc_command mmc_cmd; struct mmc_data mmc_dat; @@ -371,6 +372,32 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, req_sz); } + target_list = pktlist; + /* for host with broken sg support, prepare a page aligned list */ + __skb_queue_head_init(&local_list); + if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) { + req_sz = 0; + skb_queue_walk(pktlist, pkt_next) + req_sz += pkt_next->len; + req_sz = ALIGN(req_sz, sdiodev->func[fn]->cur_blksize); + while (req_sz > PAGE_SIZE) { + pkt_next = brcmu_pkt_buf_get_skb(PAGE_SIZE); + if (pkt_next == NULL) { + ret = -ENOMEM; + goto exit; + } + __skb_queue_tail(&local_list, pkt_next); + req_sz -= PAGE_SIZE; + } + pkt_next = brcmu_pkt_buf_get_skb(req_sz); + if (pkt_next == NULL) { + ret = -ENOMEM; + goto exit; + } + __skb_queue_tail(&local_list, pkt_next); + target_list = &local_list; + } + host = sdiodev->func[fn]->card->host; func_blk_sz = sdiodev->func[fn]->cur_blksize; /* Blocks per command is limited by host count, host transfer @@ -380,13 +407,15 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, max_req_sz = min_t(unsigned int, host->max_req_size, max_blks * func_blk_sz); max_seg_sz = min_t(unsigned short, host->max_segs, SG_MAX_SINGLE_ALLOC); - max_seg_sz = min_t(unsigned short, max_seg_sz, pktlist->qlen); - seg_sz = pktlist->qlen; + max_seg_sz = min_t(unsigned short, max_seg_sz, target_list->qlen); + seg_sz = target_list->qlen; pkt_offset = 0; - pkt_next = pktlist->next; + pkt_next = target_list->next; - if (sg_alloc_table(&st, max_seg_sz, GFP_KERNEL)) - return -ENOMEM; + if (sg_alloc_table(&st, max_seg_sz, GFP_KERNEL)) { + ret = -ENOMEM; + goto exit; + } while (seg_sz) { req_sz = 0; @@ -396,7 +425,7 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, memset(&mmc_dat, 0, sizeof(struct mmc_data)); sgl = st.sgl; /* prep sg table */ - while (pkt_next != (struct sk_buff *)pktlist) { + while (pkt_next != (struct sk_buff *)target_list) { pkt_data = pkt_next->data + pkt_offset; sg_data_sz = pkt_next->len - pkt_offset; if (sg_data_sz > host->max_seg_size) @@ -423,8 +452,8 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, if (req_sz % func_blk_sz != 0) { brcmf_err("sg request length %u is not %u aligned\n", req_sz, func_blk_sz); - sg_free_table(&st); - return -ENOTBLK; + ret = -ENOTBLK; + goto exit; } mmc_dat.sg = st.sgl; mmc_dat.sg_len = sg_cnt; @@ -457,7 +486,34 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, } } + if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) { + local_pkt_next = local_list.next; + orig_offset = 0; + skb_queue_walk(pktlist, pkt_next) { + dst_offset = 0; + do { + req_sz = local_pkt_next->len - orig_offset; + req_sz = min_t(uint, pkt_next->len - dst_offset, + req_sz); + orig_data = local_pkt_next->data + orig_offset; + dst_data = pkt_next->data + dst_offset; + memcpy(dst_data, orig_data, req_sz); + orig_offset += req_sz; + dst_offset += req_sz; + if (orig_offset == local_pkt_next->len) { + orig_offset = 0; + local_pkt_next = local_pkt_next->next; + } + if (dst_offset == pkt_next->len) + break; + } while (!skb_queue_empty(&local_list)); + } + } + +exit: sg_free_table(&st); + while ((pkt_next = __skb_dequeue(&local_list)) != NULL) + brcmu_pkt_buf_free_skb(pkt_next); return ret; } diff --git a/include/linux/platform_data/brcmfmac-sdio.h b/include/linux/platform_data/brcmfmac-sdio.h index 1ade657d5fc1..b7174998c24a 100644 --- a/include/linux/platform_data/brcmfmac-sdio.h +++ b/include/linux/platform_data/brcmfmac-sdio.h @@ -90,6 +90,10 @@ void __init brcmfmac_init_pdata(void) * oob_irq_nr, oob_irq_flags: the OOB interrupt information. The values are * used for registering the irq using request_irq function. * + * broken_sg_support: flag for broken sg list support of SDIO host controller. + * Set this to true if the SDIO host controller has higher align requirement + * than 32 bytes for each scatterlist item. + * * power_on: This function is called by the brcmfmac when the module gets * loaded. This can be particularly useful for low power devices. The platform * spcific routine may for example decide to power up the complete device. @@ -116,6 +120,7 @@ struct brcmfmac_sdio_platform_data { bool oob_irq_supported; unsigned int oob_irq_nr; unsigned long oob_irq_flags; + bool broken_sg_support; void (*power_on)(void); void (*power_off)(void); void (*reset)(void); -- cgit v1.3-18-gd494 From 17fb1563d69b63fe7a79570fe870cf7e530cd2cd Mon Sep 17 00:00:00 2001 From: Ferruh Yigit Date: Sun, 30 Jun 2013 18:49:02 -0700 Subject: Input: cyttsp4 - add core driver for Cypress TMA4XX touchscreen devices Cypress TrueTouch(tm) Standard Product controllers, Generetion4 devices, Core driver. Core driver is interface between host and TTSP controller and processes data sent by controller. Responsibilities of module are IRQ handling, reading system information registers and sending multi-touch protocol type B events. Signed-off-by: Ferruh Yigit Acked-by: Greg Kroah-Hartman Acked-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/cyttsp4_core.c | 2169 ++++++++++++++++++++++++++++++ drivers/input/touchscreen/cyttsp4_core.h | 472 +++++++ include/linux/platform_data/cyttsp4.h | 76 ++ 5 files changed, 2730 insertions(+) create mode 100644 drivers/input/touchscreen/cyttsp4_core.c create mode 100644 drivers/input/touchscreen/cyttsp4_core.h create mode 100644 include/linux/platform_data/cyttsp4.h (limited to 'include/linux/platform_data') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f9a5fd89bc02..66df8df88140 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -167,6 +167,18 @@ config TOUCHSCREEN_CYTTSP_SPI To compile this driver as a module, choose M here: the module will be called cyttsp_spi. +config TOUCHSCREEN_CYTTSP4_CORE + tristate "Cypress TrueTouch Gen4 Touchscreen Driver" + help + Core driver for Cypress TrueTouch(tm) Standard Product + Generation4 touchscreen controllers. + + Say Y here if you have a Cypress Gen4 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 2b378b12ee6b..f44500674c32 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE) += cyttsp4_core.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c new file mode 100644 index 000000000000..963da052c15e --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -0,0 +1,2169 @@ +/* + * cyttsp4_core.c + * Cypress TrueTouch(TM) Standard Product V4 Core driver module. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2012 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include "cyttsp4_core.h" +#include +#include +#include +#include +#include +#include +#include + +/* Timeout in ms. */ +#define CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT 500 +#define CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT 5000 +#define CY_CORE_MODE_CHANGE_TIMEOUT 1000 +#define CY_CORE_RESET_AND_WAIT_TIMEOUT 500 +#define CY_CORE_WAKEUP_TIMEOUT 500 + +#define CY_CORE_STARTUP_RETRY_COUNT 3 + +static const u8 ldr_exit[] = { + 0xFF, 0x01, 0x3B, 0x00, 0x00, 0x4F, 0x6D, 0x17 +}; + +static const u8 ldr_err_app[] = { + 0x01, 0x02, 0x00, 0x00, 0x55, 0xDD, 0x17 +}; + +static inline size_t merge_bytes(u8 high, u8 low) +{ + return (high << 8) + low; +} + +#ifdef VERBOSE_DEBUG +static void cyttsp4_pr_buf(struct device *dev, u8 *pr_buf, u8 *dptr, int size, + const char *data_name) +{ + int i, k; + const char fmt[] = "%02X "; + int max; + + if (!size) + return; + + max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED); + + pr_buf[0] = 0; + for (i = k = 0; i < size && k < max; i++, k += 3) + scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]); + + dev_vdbg(dev, "%s: %s[0..%d]=%s%s\n", __func__, data_name, size - 1, + pr_buf, size <= max ? "" : CY_PR_TRUNCATED); +} +#else +#define cyttsp4_pr_buf(dev, pr_buf, dptr, size, data_name) do { } while (0) +#endif + +static int cyttsp4_load_status_regs(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + struct device *dev = cd->dev; + int rc; + + rc = cyttsp4_adap_read(cd, CY_REG_BASE, si->si_ofs.mode_size, + si->xy_mode); + if (rc < 0) + dev_err(dev, "%s: fail read mode regs r=%d\n", + __func__, rc); + else + cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_mode, + si->si_ofs.mode_size, "xy_mode"); + + return rc; +} + +static int cyttsp4_handshake(struct cyttsp4 *cd, u8 mode) +{ + u8 cmd = mode ^ CY_HST_TOGGLE; + int rc; + + /* + * Mode change issued, handshaking now will cause endless mode change + * requests, for sync mode modechange will do same with handshake + * */ + if (mode & CY_HST_MODE_CHANGE) + return 0; + + rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd); + if (rc < 0) + dev_err(cd->dev, "%s: bus write fail on handshake (ret=%d)\n", + __func__, rc); + + return rc; +} + +static int cyttsp4_hw_soft_reset(struct cyttsp4 *cd) +{ + u8 cmd = CY_HST_RESET; + int rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd); + if (rc < 0) { + dev_err(cd->dev, "%s: FAILED to execute SOFT reset\n", + __func__); + return rc; + } + return 0; +} + +static int cyttsp4_hw_hard_reset(struct cyttsp4 *cd) +{ + if (cd->cpdata->xres) { + cd->cpdata->xres(cd->cpdata, cd->dev); + dev_dbg(cd->dev, "%s: execute HARD reset\n", __func__); + return 0; + } + dev_err(cd->dev, "%s: FAILED to execute HARD reset\n", __func__); + return -ENOSYS; +} + +static int cyttsp4_hw_reset(struct cyttsp4 *cd) +{ + int rc = cyttsp4_hw_hard_reset(cd); + if (rc == -ENOSYS) + rc = cyttsp4_hw_soft_reset(cd); + return rc; +} + +/* + * Gets number of bits for a touch filed as parameter, + * sets maximum value for field which is used as bit mask + * and returns number of bytes required for that field + */ +static int cyttsp4_bits_2_bytes(unsigned int nbits, size_t *max) +{ + *max = 1 << nbits; + return (nbits + 7) / 8; +} + +static int cyttsp4_si_data_offsets(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(si->si_data), + &si->si_data); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read sysinfo data offsets r=%d\n", + __func__, rc); + return rc; + } + + /* Print sysinfo data offsets */ + cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)&si->si_data, + sizeof(si->si_data), "sysinfo_data_offsets"); + + /* convert sysinfo data offset bytes into integers */ + + si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh, + si->si_data.map_szl); + si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh, + si->si_data.map_szl); + si->si_ofs.cydata_ofs = merge_bytes(si->si_data.cydata_ofsh, + si->si_data.cydata_ofsl); + si->si_ofs.test_ofs = merge_bytes(si->si_data.test_ofsh, + si->si_data.test_ofsl); + si->si_ofs.pcfg_ofs = merge_bytes(si->si_data.pcfg_ofsh, + si->si_data.pcfg_ofsl); + si->si_ofs.opcfg_ofs = merge_bytes(si->si_data.opcfg_ofsh, + si->si_data.opcfg_ofsl); + si->si_ofs.ddata_ofs = merge_bytes(si->si_data.ddata_ofsh, + si->si_data.ddata_ofsl); + si->si_ofs.mdata_ofs = merge_bytes(si->si_data.mdata_ofsh, + si->si_data.mdata_ofsl); + return rc; +} + +static int cyttsp4_si_get_cydata(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int read_offset; + int mfgid_sz, calc_mfgid_sz; + void *p; + int rc; + + si->si_ofs.cydata_size = si->si_ofs.test_ofs - si->si_ofs.cydata_ofs; + dev_dbg(cd->dev, "%s: cydata size: %Zd\n", __func__, + si->si_ofs.cydata_size); + + p = krealloc(si->si_ptrs.cydata, si->si_ofs.cydata_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc cydata memory\n", __func__); + return -ENOMEM; + } + si->si_ptrs.cydata = p; + + read_offset = si->si_ofs.cydata_ofs; + + /* Read the CYDA registers up to MFGID field */ + rc = cyttsp4_adap_read(cd, read_offset, + offsetof(struct cyttsp4_cydata, mfgid_sz) + + sizeof(si->si_ptrs.cydata->mfgid_sz), + si->si_ptrs.cydata); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read cydata r=%d\n", + __func__, rc); + return rc; + } + + /* Check MFGID size */ + mfgid_sz = si->si_ptrs.cydata->mfgid_sz; + calc_mfgid_sz = si->si_ofs.cydata_size - sizeof(struct cyttsp4_cydata); + if (mfgid_sz != calc_mfgid_sz) { + dev_err(cd->dev, "%s: mismatch in MFGID size, reported:%d calculated:%d\n", + __func__, mfgid_sz, calc_mfgid_sz); + return -EINVAL; + } + + read_offset += offsetof(struct cyttsp4_cydata, mfgid_sz) + + sizeof(si->si_ptrs.cydata->mfgid_sz); + + /* Read the CYDA registers for MFGID field */ + rc = cyttsp4_adap_read(cd, read_offset, si->si_ptrs.cydata->mfgid_sz, + si->si_ptrs.cydata->mfg_id); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read cydata r=%d\n", + __func__, rc); + return rc; + } + + read_offset += si->si_ptrs.cydata->mfgid_sz; + + /* Read the rest of the CYDA registers */ + rc = cyttsp4_adap_read(cd, read_offset, + sizeof(struct cyttsp4_cydata) + - offsetof(struct cyttsp4_cydata, cyito_idh), + &si->si_ptrs.cydata->cyito_idh); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read cydata r=%d\n", + __func__, rc); + return rc; + } + + cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.cydata, + si->si_ofs.cydata_size, "sysinfo_cydata"); + return rc; +} + +static int cyttsp4_si_get_test_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + int rc; + + si->si_ofs.test_size = si->si_ofs.pcfg_ofs - si->si_ofs.test_ofs; + + p = krealloc(si->si_ptrs.test, si->si_ofs.test_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc test memory\n", __func__); + return -ENOMEM; + } + si->si_ptrs.test = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.test_ofs, si->si_ofs.test_size, + si->si_ptrs.test); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read test data r=%d\n", + __func__, rc); + return rc; + } + + cyttsp4_pr_buf(cd->dev, cd->pr_buf, + (u8 *)si->si_ptrs.test, si->si_ofs.test_size, + "sysinfo_test_data"); + if (si->si_ptrs.test->post_codel & + CY_POST_CODEL_WDG_RST) + dev_info(cd->dev, "%s: %s codel=%02X\n", + __func__, "Reset was a WATCHDOG RESET", + si->si_ptrs.test->post_codel); + + if (!(si->si_ptrs.test->post_codel & + CY_POST_CODEL_CFG_DATA_CRC_FAIL)) + dev_info(cd->dev, "%s: %s codel=%02X\n", __func__, + "Config Data CRC FAIL", + si->si_ptrs.test->post_codel); + + if (!(si->si_ptrs.test->post_codel & + CY_POST_CODEL_PANEL_TEST_FAIL)) + dev_info(cd->dev, "%s: %s codel=%02X\n", + __func__, "PANEL TEST FAIL", + si->si_ptrs.test->post_codel); + + dev_info(cd->dev, "%s: SCANNING is %s codel=%02X\n", + __func__, si->si_ptrs.test->post_codel & 0x08 ? + "ENABLED" : "DISABLED", + si->si_ptrs.test->post_codel); + return rc; +} + +static int cyttsp4_si_get_pcfg_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + int rc; + + si->si_ofs.pcfg_size = si->si_ofs.opcfg_ofs - si->si_ofs.pcfg_ofs; + + p = krealloc(si->si_ptrs.pcfg, si->si_ofs.pcfg_size, GFP_KERNEL); + if (p == NULL) { + rc = -ENOMEM; + dev_err(cd->dev, "%s: fail alloc pcfg memory r=%d\n", + __func__, rc); + return rc; + } + si->si_ptrs.pcfg = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size, + si->si_ptrs.pcfg); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read pcfg data r=%d\n", + __func__, rc); + return rc; + } + + si->si_ofs.max_x = merge_bytes((si->si_ptrs.pcfg->res_xh + & CY_PCFG_RESOLUTION_X_MASK), si->si_ptrs.pcfg->res_xl); + si->si_ofs.x_origin = !!(si->si_ptrs.pcfg->res_xh + & CY_PCFG_ORIGIN_X_MASK); + si->si_ofs.max_y = merge_bytes((si->si_ptrs.pcfg->res_yh + & CY_PCFG_RESOLUTION_Y_MASK), si->si_ptrs.pcfg->res_yl); + si->si_ofs.y_origin = !!(si->si_ptrs.pcfg->res_yh + & CY_PCFG_ORIGIN_Y_MASK); + si->si_ofs.max_p = merge_bytes(si->si_ptrs.pcfg->max_zh, + si->si_ptrs.pcfg->max_zl); + + cyttsp4_pr_buf(cd->dev, cd->pr_buf, + (u8 *)si->si_ptrs.pcfg, + si->si_ofs.pcfg_size, "sysinfo_pcfg_data"); + return rc; +} + +static int cyttsp4_si_get_opcfg_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + struct cyttsp4_tch_abs_params *tch; + struct cyttsp4_tch_rec_params *tch_old, *tch_new; + enum cyttsp4_tch_abs abs; + int i; + void *p; + int rc; + + si->si_ofs.opcfg_size = si->si_ofs.ddata_ofs - si->si_ofs.opcfg_ofs; + + p = krealloc(si->si_ptrs.opcfg, si->si_ofs.opcfg_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc opcfg memory\n", __func__); + rc = -ENOMEM; + goto cyttsp4_si_get_opcfg_data_exit; + } + si->si_ptrs.opcfg = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size, + si->si_ptrs.opcfg); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read opcfg data r=%d\n", + __func__, rc); + goto cyttsp4_si_get_opcfg_data_exit; + } + si->si_ofs.cmd_ofs = si->si_ptrs.opcfg->cmd_ofs; + si->si_ofs.rep_ofs = si->si_ptrs.opcfg->rep_ofs; + si->si_ofs.rep_sz = (si->si_ptrs.opcfg->rep_szh * 256) + + si->si_ptrs.opcfg->rep_szl; + si->si_ofs.num_btns = si->si_ptrs.opcfg->num_btns; + si->si_ofs.num_btn_regs = (si->si_ofs.num_btns + + CY_NUM_BTN_PER_REG - 1) / CY_NUM_BTN_PER_REG; + si->si_ofs.tt_stat_ofs = si->si_ptrs.opcfg->tt_stat_ofs; + si->si_ofs.obj_cfg0 = si->si_ptrs.opcfg->obj_cfg0; + si->si_ofs.max_tchs = si->si_ptrs.opcfg->max_tchs & + CY_BYTE_OFS_MASK; + si->si_ofs.tch_rec_size = si->si_ptrs.opcfg->tch_rec_size & + CY_BYTE_OFS_MASK; + + /* Get the old touch fields */ + for (abs = CY_TCH_X; abs < CY_NUM_TCH_FIELDS; abs++) { + tch = &si->si_ofs.tch_abs[abs]; + tch_old = &si->si_ptrs.opcfg->tch_rec_old[abs]; + + tch->ofs = tch_old->loc & CY_BYTE_OFS_MASK; + tch->size = cyttsp4_bits_2_bytes(tch_old->size, + &tch->max); + tch->bofs = (tch_old->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT; + } + + /* button fields */ + si->si_ofs.btn_rec_size = si->si_ptrs.opcfg->btn_rec_size; + si->si_ofs.btn_diff_ofs = si->si_ptrs.opcfg->btn_diff_ofs; + si->si_ofs.btn_diff_size = si->si_ptrs.opcfg->btn_diff_size; + + if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) { + /* Get the extended touch fields */ + for (i = 0; i < CY_NUM_EXT_TCH_FIELDS; abs++, i++) { + tch = &si->si_ofs.tch_abs[abs]; + tch_new = &si->si_ptrs.opcfg->tch_rec_new[i]; + + tch->ofs = tch_new->loc & CY_BYTE_OFS_MASK; + tch->size = cyttsp4_bits_2_bytes(tch_new->size, + &tch->max); + tch->bofs = (tch_new->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT; + } + } + + for (abs = 0; abs < CY_TCH_NUM_ABS; abs++) { + dev_dbg(cd->dev, "%s: tch_rec_%s\n", __func__, + cyttsp4_tch_abs_string[abs]); + dev_dbg(cd->dev, "%s: ofs =%2Zd\n", __func__, + si->si_ofs.tch_abs[abs].ofs); + dev_dbg(cd->dev, "%s: siz =%2Zd\n", __func__, + si->si_ofs.tch_abs[abs].size); + dev_dbg(cd->dev, "%s: max =%2Zd\n", __func__, + si->si_ofs.tch_abs[abs].max); + dev_dbg(cd->dev, "%s: bofs=%2Zd\n", __func__, + si->si_ofs.tch_abs[abs].bofs); + } + + si->si_ofs.mode_size = si->si_ofs.tt_stat_ofs + 1; + si->si_ofs.data_size = si->si_ofs.max_tchs * + si->si_ptrs.opcfg->tch_rec_size; + + cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.opcfg, + si->si_ofs.opcfg_size, "sysinfo_opcfg_data"); + +cyttsp4_si_get_opcfg_data_exit: + return rc; +} + +static int cyttsp4_si_get_ddata(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + int rc; + + si->si_ofs.ddata_size = si->si_ofs.mdata_ofs - si->si_ofs.ddata_ofs; + + p = krealloc(si->si_ptrs.ddata, si->si_ofs.ddata_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc ddata memory\n", __func__); + return -ENOMEM; + } + si->si_ptrs.ddata = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.ddata_ofs, si->si_ofs.ddata_size, + si->si_ptrs.ddata); + if (rc < 0) + dev_err(cd->dev, "%s: fail read ddata data r=%d\n", + __func__, rc); + else + cyttsp4_pr_buf(cd->dev, cd->pr_buf, + (u8 *)si->si_ptrs.ddata, + si->si_ofs.ddata_size, "sysinfo_ddata"); + return rc; +} + +static int cyttsp4_si_get_mdata(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + int rc; + + si->si_ofs.mdata_size = si->si_ofs.map_sz - si->si_ofs.mdata_ofs; + + p = krealloc(si->si_ptrs.mdata, si->si_ofs.mdata_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc mdata memory\n", __func__); + return -ENOMEM; + } + si->si_ptrs.mdata = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.mdata_ofs, si->si_ofs.mdata_size, + si->si_ptrs.mdata); + if (rc < 0) + dev_err(cd->dev, "%s: fail read mdata data r=%d\n", + __func__, rc); + else + cyttsp4_pr_buf(cd->dev, cd->pr_buf, + (u8 *)si->si_ptrs.mdata, + si->si_ofs.mdata_size, "sysinfo_mdata"); + return rc; +} + +static int cyttsp4_si_get_btn_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int btn; + int num_defined_keys; + u16 *key_table; + void *p; + int rc = 0; + + if (si->si_ofs.num_btns) { + si->si_ofs.btn_keys_size = si->si_ofs.num_btns * + sizeof(struct cyttsp4_btn); + + p = krealloc(si->btn, si->si_ofs.btn_keys_size, + GFP_KERNEL|__GFP_ZERO); + if (p == NULL) { + dev_err(cd->dev, "%s: %s\n", __func__, + "fail alloc btn_keys memory"); + return -ENOMEM; + } + si->btn = p; + + if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL) + num_defined_keys = 0; + else if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL) + num_defined_keys = 0; + else + num_defined_keys = cd->cpdata->sett + [CY_IC_GRPNUM_BTN_KEYS]->size; + + for (btn = 0; btn < si->si_ofs.num_btns && + btn < num_defined_keys; btn++) { + key_table = (u16 *)cd->cpdata->sett + [CY_IC_GRPNUM_BTN_KEYS]->data; + si->btn[btn].key_code = key_table[btn]; + si->btn[btn].state = CY_BTN_RELEASED; + si->btn[btn].enabled = true; + } + for (; btn < si->si_ofs.num_btns; btn++) { + si->btn[btn].key_code = KEY_RESERVED; + si->btn[btn].state = CY_BTN_RELEASED; + si->btn[btn].enabled = true; + } + + return rc; + } + + si->si_ofs.btn_keys_size = 0; + kfree(si->btn); + si->btn = NULL; + return rc; +} + +static int cyttsp4_si_get_op_data_ptrs(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + + p = krealloc(si->xy_mode, si->si_ofs.mode_size, GFP_KERNEL|__GFP_ZERO); + if (p == NULL) + return -ENOMEM; + si->xy_mode = p; + + p = krealloc(si->xy_data, si->si_ofs.data_size, GFP_KERNEL|__GFP_ZERO); + if (p == NULL) + return -ENOMEM; + si->xy_data = p; + + p = krealloc(si->btn_rec_data, + si->si_ofs.btn_rec_size * si->si_ofs.num_btns, + GFP_KERNEL|__GFP_ZERO); + if (p == NULL) + return -ENOMEM; + si->btn_rec_data = p; + + return 0; +} + +static void cyttsp4_si_put_log_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + dev_dbg(cd->dev, "%s: cydata_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.cydata_ofs, si->si_ofs.cydata_size); + dev_dbg(cd->dev, "%s: test_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.test_ofs, si->si_ofs.test_size); + dev_dbg(cd->dev, "%s: pcfg_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size); + dev_dbg(cd->dev, "%s: opcfg_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size); + dev_dbg(cd->dev, "%s: ddata_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.ddata_ofs, si->si_ofs.ddata_size); + dev_dbg(cd->dev, "%s: mdata_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.mdata_ofs, si->si_ofs.mdata_size); + + dev_dbg(cd->dev, "%s: cmd_ofs =%4Zd\n", __func__, + si->si_ofs.cmd_ofs); + dev_dbg(cd->dev, "%s: rep_ofs =%4Zd\n", __func__, + si->si_ofs.rep_ofs); + dev_dbg(cd->dev, "%s: rep_sz =%4Zd\n", __func__, + si->si_ofs.rep_sz); + dev_dbg(cd->dev, "%s: num_btns =%4Zd\n", __func__, + si->si_ofs.num_btns); + dev_dbg(cd->dev, "%s: num_btn_regs =%4Zd\n", __func__, + si->si_ofs.num_btn_regs); + dev_dbg(cd->dev, "%s: tt_stat_ofs =%4Zd\n", __func__, + si->si_ofs.tt_stat_ofs); + dev_dbg(cd->dev, "%s: tch_rec_size =%4Zd\n", __func__, + si->si_ofs.tch_rec_size); + dev_dbg(cd->dev, "%s: max_tchs =%4Zd\n", __func__, + si->si_ofs.max_tchs); + dev_dbg(cd->dev, "%s: mode_size =%4Zd\n", __func__, + si->si_ofs.mode_size); + dev_dbg(cd->dev, "%s: data_size =%4Zd\n", __func__, + si->si_ofs.data_size); + dev_dbg(cd->dev, "%s: map_sz =%4Zd\n", __func__, + si->si_ofs.map_sz); + + dev_dbg(cd->dev, "%s: btn_rec_size =%2Zd\n", __func__, + si->si_ofs.btn_rec_size); + dev_dbg(cd->dev, "%s: btn_diff_ofs =%2Zd\n", __func__, + si->si_ofs.btn_diff_ofs); + dev_dbg(cd->dev, "%s: btn_diff_size =%2Zd\n", __func__, + si->si_ofs.btn_diff_size); + + dev_dbg(cd->dev, "%s: max_x = 0x%04ZX (%Zd)\n", __func__, + si->si_ofs.max_x, si->si_ofs.max_x); + dev_dbg(cd->dev, "%s: x_origin = %Zd (%s)\n", __func__, + si->si_ofs.x_origin, + si->si_ofs.x_origin == CY_NORMAL_ORIGIN ? + "left corner" : "right corner"); + dev_dbg(cd->dev, "%s: max_y = 0x%04ZX (%Zd)\n", __func__, + si->si_ofs.max_y, si->si_ofs.max_y); + dev_dbg(cd->dev, "%s: y_origin = %Zd (%s)\n", __func__, + si->si_ofs.y_origin, + si->si_ofs.y_origin == CY_NORMAL_ORIGIN ? + "upper corner" : "lower corner"); + dev_dbg(cd->dev, "%s: max_p = 0x%04ZX (%Zd)\n", __func__, + si->si_ofs.max_p, si->si_ofs.max_p); + + dev_dbg(cd->dev, "%s: xy_mode=%p xy_data=%p\n", __func__, + si->xy_mode, si->xy_data); +} + +static int cyttsp4_get_sysinfo_regs(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int rc; + + rc = cyttsp4_si_data_offsets(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_cydata(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_test_data(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_pcfg_data(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_opcfg_data(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_ddata(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_mdata(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_btn_data(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_op_data_ptrs(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: failed to get_op_data\n", + __func__); + return rc; + } + + cyttsp4_si_put_log_data(cd); + + /* provide flow control handshake */ + rc = cyttsp4_handshake(cd, si->si_data.hst_mode); + if (rc < 0) + dev_err(cd->dev, "%s: handshake fail on sysinfo reg\n", + __func__); + + si->ready = true; + return rc; +} + +static void cyttsp4_queue_startup_(struct cyttsp4 *cd) +{ + if (cd->startup_state == STARTUP_NONE) { + cd->startup_state = STARTUP_QUEUED; + schedule_work(&cd->startup_work); + dev_dbg(cd->dev, "%s: cyttsp4_startup queued\n", __func__); + } else { + dev_dbg(cd->dev, "%s: startup_state = %d\n", __func__, + cd->startup_state); + } +} + +static void cyttsp4_report_slot_liftoff(struct cyttsp4_mt_data *md, + int max_slots) +{ + int t; + + if (md->num_prv_tch == 0) + return; + + for (t = 0; t < max_slots; t++) { + input_mt_slot(md->input, t); + input_mt_report_slot_state(md->input, + MT_TOOL_FINGER, false); + } +} + +static void cyttsp4_lift_all(struct cyttsp4_mt_data *md) +{ + if (!md->si) + return; + + if (md->num_prv_tch != 0) { + cyttsp4_report_slot_liftoff(md, + md->si->si_ofs.tch_abs[CY_TCH_T].max); + input_sync(md->input); + md->num_prv_tch = 0; + } +} + +static void cyttsp4_get_touch_axis(struct cyttsp4_mt_data *md, + int *axis, int size, int max, u8 *xy_data, int bofs) +{ + int nbyte; + int next; + + for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { + dev_vdbg(&md->input->dev, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p" + " xy_data[%d]=%02X(%d) bofs=%d\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next], bofs); + *axis = (*axis * 256) + (xy_data[next] >> bofs); + next++; + } + + *axis &= max - 1; + + dev_vdbg(&md->input->dev, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p" + " xy_data[%d]=%02X(%d)\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next]); +} + +static void cyttsp4_get_touch(struct cyttsp4_mt_data *md, + struct cyttsp4_touch *touch, u8 *xy_data) +{ + struct device *dev = &md->input->dev; + struct cyttsp4_sysinfo *si = md->si; + enum cyttsp4_tch_abs abs; + int tmp; + bool flipped; + + for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) { + cyttsp4_get_touch_axis(md, &touch->abs[abs], + si->si_ofs.tch_abs[abs].size, + si->si_ofs.tch_abs[abs].max, + xy_data + si->si_ofs.tch_abs[abs].ofs, + si->si_ofs.tch_abs[abs].bofs); + dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__, + cyttsp4_tch_abs_string[abs], + touch->abs[abs], touch->abs[abs]); + } + + if (md->pdata->flags & CY_FLAG_FLIP) { + tmp = touch->abs[CY_TCH_X]; + touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y]; + touch->abs[CY_TCH_Y] = tmp; + flipped = true; + } else + flipped = false; + + if (md->pdata->flags & CY_FLAG_INV_X) { + if (flipped) + touch->abs[CY_TCH_X] = md->si->si_ofs.max_y - + touch->abs[CY_TCH_X]; + else + touch->abs[CY_TCH_X] = md->si->si_ofs.max_x - + touch->abs[CY_TCH_X]; + } + if (md->pdata->flags & CY_FLAG_INV_Y) { + if (flipped) + touch->abs[CY_TCH_Y] = md->si->si_ofs.max_x - + touch->abs[CY_TCH_Y]; + else + touch->abs[CY_TCH_Y] = md->si->si_ofs.max_y - + touch->abs[CY_TCH_Y]; + } + + dev_vdbg(dev, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n", + __func__, flipped ? "true" : "false", + md->pdata->flags & CY_FLAG_INV_X ? "true" : "false", + md->pdata->flags & CY_FLAG_INV_Y ? "true" : "false", + touch->abs[CY_TCH_X], touch->abs[CY_TCH_X], + touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]); +} + +static void cyttsp4_final_sync(struct input_dev *input, int max_slots, int *ids) +{ + int t; + + for (t = 0; t < max_slots; t++) { + if (ids[t]) + continue; + input_mt_slot(input, t); + input_mt_report_slot_state(input, MT_TOOL_FINGER, false); + } + + input_sync(input); +} + +static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch) +{ + struct device *dev = &md->input->dev; + struct cyttsp4_sysinfo *si = md->si; + struct cyttsp4_touch tch; + int sig; + int i, j, t = 0; + int ids[max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)]; + + memset(ids, 0, si->si_ofs.tch_abs[CY_TCH_T].max * sizeof(int)); + for (i = 0; i < num_cur_tch; i++) { + cyttsp4_get_touch(md, &tch, si->xy_data + + (i * si->si_ofs.tch_rec_size)); + if ((tch.abs[CY_TCH_T] < md->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]) || + (tch.abs[CY_TCH_T] > md->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MAX_OST])) { + dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n", + __func__, i, tch.abs[CY_TCH_T], + md->pdata->frmwrk->abs[(CY_ABS_ID_OST * + CY_NUM_ABS_SET) + CY_MAX_OST]); + continue; + } + + /* use 0 based track id's */ + sig = md->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + 0]; + if (sig != CY_IGNORE_VALUE) { + t = tch.abs[CY_TCH_T] - md->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]; + if (tch.abs[CY_TCH_E] == CY_EV_LIFTOFF) { + dev_dbg(dev, "%s: t=%d e=%d lift-off\n", + __func__, t, tch.abs[CY_TCH_E]); + goto cyttsp4_get_mt_touches_pr_tch; + } + input_mt_slot(md->input, t); + input_mt_report_slot_state(md->input, MT_TOOL_FINGER, + true); + ids[t] = true; + } + + /* all devices: position and pressure fields */ + for (j = 0; j <= CY_ABS_W_OST; j++) { + sig = md->pdata->frmwrk->abs[((CY_ABS_X_OST + j) * + CY_NUM_ABS_SET) + 0]; + if (sig != CY_IGNORE_VALUE) + input_report_abs(md->input, sig, + tch.abs[CY_TCH_X + j]); + } + if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) { + /* + * TMA400 size and orientation fields: + * if pressure is non-zero and major touch + * signal is zero, then set major and minor touch + * signals to minimum non-zero value + */ + if (tch.abs[CY_TCH_P] > 0 && tch.abs[CY_TCH_MAJ] == 0) + tch.abs[CY_TCH_MAJ] = tch.abs[CY_TCH_MIN] = 1; + + /* Get the extended touch fields */ + for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) { + sig = md->pdata->frmwrk->abs + [((CY_ABS_MAJ_OST + j) * + CY_NUM_ABS_SET) + 0]; + if (sig != CY_IGNORE_VALUE) + input_report_abs(md->input, sig, + tch.abs[CY_TCH_MAJ + j]); + } + } + +cyttsp4_get_mt_touches_pr_tch: + if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) + dev_dbg(dev, + "%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d\n", + __func__, t, + tch.abs[CY_TCH_X], + tch.abs[CY_TCH_Y], + tch.abs[CY_TCH_P], + tch.abs[CY_TCH_MAJ], + tch.abs[CY_TCH_MIN], + tch.abs[CY_TCH_OR], + tch.abs[CY_TCH_E]); + else + dev_dbg(dev, + "%s: t=%d x=%d y=%d z=%d e=%d\n", __func__, + t, + tch.abs[CY_TCH_X], + tch.abs[CY_TCH_Y], + tch.abs[CY_TCH_P], + tch.abs[CY_TCH_E]); + } + + cyttsp4_final_sync(md->input, si->si_ofs.tch_abs[CY_TCH_T].max, ids); + + md->num_prv_tch = num_cur_tch; + + return; +} + +/* read xy_data for all current touches */ +static int cyttsp4_xy_worker(struct cyttsp4 *cd) +{ + struct cyttsp4_mt_data *md = &cd->md; + struct device *dev = &md->input->dev; + struct cyttsp4_sysinfo *si = md->si; + u8 num_cur_tch; + u8 hst_mode; + u8 rep_len; + u8 rep_stat; + u8 tt_stat; + int rc = 0; + + /* + * Get event data from cyttsp4 device. + * The event data includes all data + * for all active touches. + * Event data also includes button data + */ + /* + * Use 2 reads: + * 1st read to get mode + button bytes + touch count (core) + * 2nd read (optional) to get touch 1 - touch n data + */ + hst_mode = si->xy_mode[CY_REG_BASE]; + rep_len = si->xy_mode[si->si_ofs.rep_ofs]; + rep_stat = si->xy_mode[si->si_ofs.rep_ofs + 1]; + tt_stat = si->xy_mode[si->si_ofs.tt_stat_ofs]; + dev_vdbg(dev, "%s: %s%02X %s%d %s%02X %s%02X\n", __func__, + "hst_mode=", hst_mode, "rep_len=", rep_len, + "rep_stat=", rep_stat, "tt_stat=", tt_stat); + + num_cur_tch = GET_NUM_TOUCHES(tt_stat); + dev_vdbg(dev, "%s: num_cur_tch=%d\n", __func__, num_cur_tch); + + if (rep_len == 0 && num_cur_tch > 0) { + dev_err(dev, "%s: report length error rep_len=%d num_tch=%d\n", + __func__, rep_len, num_cur_tch); + goto cyttsp4_xy_worker_exit; + } + + /* read touches */ + if (num_cur_tch > 0) { + rc = cyttsp4_adap_read(cd, si->si_ofs.tt_stat_ofs + 1, + num_cur_tch * si->si_ofs.tch_rec_size, + si->xy_data); + if (rc < 0) { + dev_err(dev, "%s: read fail on touch regs r=%d\n", + __func__, rc); + goto cyttsp4_xy_worker_exit; + } + } + + /* print xy data */ + cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_data, num_cur_tch * + si->si_ofs.tch_rec_size, "xy_data"); + + /* check any error conditions */ + if (IS_BAD_PKT(rep_stat)) { + dev_dbg(dev, "%s: Invalid buffer detected\n", __func__); + rc = 0; + goto cyttsp4_xy_worker_exit; + } + + if (IS_LARGE_AREA(tt_stat)) + dev_dbg(dev, "%s: Large area detected\n", __func__); + + if (num_cur_tch > si->si_ofs.max_tchs) { + dev_err(dev, "%s: too many tch; set to max tch (n=%d c=%Zd)\n", + __func__, num_cur_tch, si->si_ofs.max_tchs); + num_cur_tch = si->si_ofs.max_tchs; + } + + /* extract xy_data for all currently reported touches */ + dev_vdbg(dev, "%s: extract data num_cur_tch=%d\n", __func__, + num_cur_tch); + if (num_cur_tch) + cyttsp4_get_mt_touches(md, num_cur_tch); + else + cyttsp4_lift_all(md); + + rc = 0; + +cyttsp4_xy_worker_exit: + return rc; +} + +static int cyttsp4_mt_attention(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + struct cyttsp4_mt_data *md = &cd->md; + int rc = 0; + + if (!md->si) + return 0; + + mutex_lock(&md->report_lock); + if (!md->is_suspended) { + /* core handles handshake */ + rc = cyttsp4_xy_worker(cd); + } else { + dev_vdbg(dev, "%s: Ignoring report while suspended\n", + __func__); + } + mutex_unlock(&md->report_lock); + if (rc < 0) + dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc); + + return rc; +} + +static irqreturn_t cyttsp4_irq(int irq, void *handle) +{ + struct cyttsp4 *cd = handle; + struct device *dev = cd->dev; + enum cyttsp4_mode cur_mode; + u8 cmd_ofs = cd->sysinfo.si_ofs.cmd_ofs; + u8 mode[3]; + int rc; + + /* + * Check whether this IRQ should be ignored (external) + * This should be the very first thing to check since + * ignore_irq may be set for a very short period of time + */ + if (atomic_read(&cd->ignore_irq)) { + dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__); + return IRQ_HANDLED; + } + + dev_dbg(dev, "%s int:0x%x\n", __func__, cd->int_status); + + mutex_lock(&cd->system_lock); + + /* Just to debug */ + if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING) + dev_vdbg(dev, "%s: Received IRQ while in sleep\n", __func__); + + rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), mode); + if (rc) { + dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc); + goto cyttsp4_irq_exit; + } + dev_vdbg(dev, "%s mode[0-2]:0x%X 0x%X 0x%X\n", __func__, + mode[0], mode[1], mode[2]); + + if (IS_BOOTLOADER(mode[0], mode[1])) { + cur_mode = CY_MODE_BOOTLOADER; + dev_vdbg(dev, "%s: bl running\n", __func__); + if (cd->mode == CY_MODE_BOOTLOADER) { + /* Signal bootloader heartbeat heard */ + wake_up(&cd->wait_q); + goto cyttsp4_irq_exit; + } + + /* switch to bootloader */ + dev_dbg(dev, "%s: restart switch to bl m=%d -> m=%d\n", + __func__, cd->mode, cur_mode); + + /* catch operation->bl glitch */ + if (cd->mode != CY_MODE_UNKNOWN) { + /* Incase startup_state do not let startup_() */ + cd->mode = CY_MODE_UNKNOWN; + cyttsp4_queue_startup_(cd); + goto cyttsp4_irq_exit; + } + + /* + * do not wake thread on this switch since + * it is possible to get an early heartbeat + * prior to performing the reset + */ + cd->mode = cur_mode; + + goto cyttsp4_irq_exit; + } + + switch (mode[0] & CY_HST_MODE) { + case CY_HST_OPERATE: + cur_mode = CY_MODE_OPERATIONAL; + dev_vdbg(dev, "%s: operational\n", __func__); + break; + case CY_HST_CAT: + cur_mode = CY_MODE_CAT; + dev_vdbg(dev, "%s: CaT\n", __func__); + break; + case CY_HST_SYSINFO: + cur_mode = CY_MODE_SYSINFO; + dev_vdbg(dev, "%s: sysinfo\n", __func__); + break; + default: + cur_mode = CY_MODE_UNKNOWN; + dev_err(dev, "%s: unknown HST mode 0x%02X\n", __func__, + mode[0]); + break; + } + + /* Check whether this IRQ should be ignored (internal) */ + if (cd->int_status & CY_INT_IGNORE) { + dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__); + goto cyttsp4_irq_exit; + } + + /* Check for wake up interrupt */ + if (cd->int_status & CY_INT_AWAKE) { + cd->int_status &= ~CY_INT_AWAKE; + wake_up(&cd->wait_q); + dev_vdbg(dev, "%s: Received wake up interrupt\n", __func__); + goto cyttsp4_irq_handshake; + } + + /* Expecting mode change interrupt */ + if ((cd->int_status & CY_INT_MODE_CHANGE) + && (mode[0] & CY_HST_MODE_CHANGE) == 0) { + cd->int_status &= ~CY_INT_MODE_CHANGE; + dev_dbg(dev, "%s: finish mode switch m=%d -> m=%d\n", + __func__, cd->mode, cur_mode); + cd->mode = cur_mode; + wake_up(&cd->wait_q); + goto cyttsp4_irq_handshake; + } + + /* compare current core mode to current device mode */ + dev_vdbg(dev, "%s: cd->mode=%d cur_mode=%d\n", + __func__, cd->mode, cur_mode); + if ((mode[0] & CY_HST_MODE_CHANGE) == 0 && cd->mode != cur_mode) { + /* Unexpected mode change occurred */ + dev_err(dev, "%s %d->%d 0x%x\n", __func__, cd->mode, + cur_mode, cd->int_status); + dev_dbg(dev, "%s: Unexpected mode change, startup\n", + __func__); + cyttsp4_queue_startup_(cd); + goto cyttsp4_irq_exit; + } + + /* Expecting command complete interrupt */ + dev_vdbg(dev, "%s: command byte:0x%x\n", __func__, mode[cmd_ofs]); + if ((cd->int_status & CY_INT_EXEC_CMD) + && mode[cmd_ofs] & CY_CMD_COMPLETE) { + cd->int_status &= ~CY_INT_EXEC_CMD; + dev_vdbg(dev, "%s: Received command complete interrupt\n", + __func__); + wake_up(&cd->wait_q); + /* + * It is possible to receive a single interrupt for + * command complete and touch/button status report. + * Continue processing for a possible status report. + */ + } + + /* This should be status report, read status regs */ + if (cd->mode == CY_MODE_OPERATIONAL) { + dev_vdbg(dev, "%s: Read status registers\n", __func__); + rc = cyttsp4_load_status_regs(cd); + if (rc < 0) + dev_err(dev, "%s: fail read mode regs r=%d\n", + __func__, rc); + } + + cyttsp4_mt_attention(cd); + +cyttsp4_irq_handshake: + /* handshake the event */ + dev_vdbg(dev, "%s: Handshake mode=0x%02X r=%d\n", + __func__, mode[0], rc); + rc = cyttsp4_handshake(cd, mode[0]); + if (rc < 0) + dev_err(dev, "%s: Fail handshake mode=0x%02X r=%d\n", + __func__, mode[0], rc); + + /* + * a non-zero udelay period is required for using + * IRQF_TRIGGER_LOW in order to delay until the + * device completes isr deassert + */ + udelay(cd->cpdata->level_irq_udelay); + +cyttsp4_irq_exit: + mutex_unlock(&cd->system_lock); + return IRQ_HANDLED; +} + +static void cyttsp4_start_wd_timer(struct cyttsp4 *cd) +{ + if (!CY_WATCHDOG_TIMEOUT) + return; + + mod_timer(&cd->watchdog_timer, jiffies + + msecs_to_jiffies(CY_WATCHDOG_TIMEOUT)); +} + +static void cyttsp4_stop_wd_timer(struct cyttsp4 *cd) +{ + if (!CY_WATCHDOG_TIMEOUT) + return; + + /* + * Ensure we wait until the watchdog timer + * running on a different CPU finishes + */ + del_timer_sync(&cd->watchdog_timer); + cancel_work_sync(&cd->watchdog_work); + del_timer_sync(&cd->watchdog_timer); +} + +static void cyttsp4_watchdog_timer(unsigned long handle) +{ + struct cyttsp4 *cd = (struct cyttsp4 *)handle; + + dev_vdbg(cd->dev, "%s: Watchdog timer triggered\n", __func__); + + if (!cd) + return; + + if (!work_pending(&cd->watchdog_work)) + schedule_work(&cd->watchdog_work); + + return; +} + +static int cyttsp4_request_exclusive(struct cyttsp4 *cd, void *ownptr, + int timeout_ms) +{ + int t = msecs_to_jiffies(timeout_ms); + bool with_timeout = (timeout_ms != 0); + + mutex_lock(&cd->system_lock); + if (!cd->exclusive_dev && cd->exclusive_waits == 0) { + cd->exclusive_dev = ownptr; + goto exit; + } + + cd->exclusive_waits++; +wait: + mutex_unlock(&cd->system_lock); + if (with_timeout) { + t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: tmo waiting exclusive access\n", + __func__); + mutex_lock(&cd->system_lock); + cd->exclusive_waits--; + mutex_unlock(&cd->system_lock); + return -ETIME; + } + } else { + wait_event(cd->wait_q, !cd->exclusive_dev); + } + mutex_lock(&cd->system_lock); + if (cd->exclusive_dev) + goto wait; + cd->exclusive_dev = ownptr; + cd->exclusive_waits--; +exit: + mutex_unlock(&cd->system_lock); + + return 0; +} + +/* + * returns error if was not owned + */ +static int cyttsp4_release_exclusive(struct cyttsp4 *cd, void *ownptr) +{ + mutex_lock(&cd->system_lock); + if (cd->exclusive_dev != ownptr) { + mutex_unlock(&cd->system_lock); + return -EINVAL; + } + + dev_vdbg(cd->dev, "%s: exclusive_dev %p freed\n", + __func__, cd->exclusive_dev); + cd->exclusive_dev = NULL; + wake_up(&cd->wait_q); + mutex_unlock(&cd->system_lock); + return 0; +} + +static int cyttsp4_wait_bl_heartbeat(struct cyttsp4 *cd) +{ + long t; + int rc = 0; + + /* wait heartbeat */ + dev_vdbg(cd->dev, "%s: wait heartbeat...\n", __func__); + t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_BOOTLOADER, + msecs_to_jiffies(CY_CORE_RESET_AND_WAIT_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: tmo waiting bl heartbeat cd->mode=%d\n", + __func__, cd->mode); + rc = -ETIME; + } + + return rc; +} + +static int cyttsp4_wait_sysinfo_mode(struct cyttsp4 *cd) +{ + long t; + + dev_vdbg(cd->dev, "%s: wait sysinfo...\n", __func__); + + t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_SYSINFO, + msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: tmo waiting exit bl cd->mode=%d\n", + __func__, cd->mode); + mutex_lock(&cd->system_lock); + cd->int_status &= ~CY_INT_MODE_CHANGE; + mutex_unlock(&cd->system_lock); + return -ETIME; + } + + return 0; +} + +static int cyttsp4_reset_and_wait(struct cyttsp4 *cd) +{ + int rc; + + /* reset hardware */ + mutex_lock(&cd->system_lock); + dev_dbg(cd->dev, "%s: reset hw...\n", __func__); + rc = cyttsp4_hw_reset(cd); + cd->mode = CY_MODE_UNKNOWN; + mutex_unlock(&cd->system_lock); + if (rc < 0) { + dev_err(cd->dev, "%s:Fail hw reset r=%d\n", __func__, rc); + return rc; + } + + return cyttsp4_wait_bl_heartbeat(cd); +} + +/* + * returns err if refused or timeout; block until mode change complete + * bit is set (mode change interrupt) + */ +static int cyttsp4_set_mode(struct cyttsp4 *cd, int new_mode) +{ + u8 new_dev_mode; + u8 mode; + long t; + int rc; + + switch (new_mode) { + case CY_MODE_OPERATIONAL: + new_dev_mode = CY_HST_OPERATE; + break; + case CY_MODE_SYSINFO: + new_dev_mode = CY_HST_SYSINFO; + break; + case CY_MODE_CAT: + new_dev_mode = CY_HST_CAT; + break; + default: + dev_err(cd->dev, "%s: invalid mode: %02X(%d)\n", + __func__, new_mode, new_mode); + return -EINVAL; + } + + /* change mode */ + dev_dbg(cd->dev, "%s: %s=%p new_dev_mode=%02X new_mode=%d\n", + __func__, "have exclusive", cd->exclusive_dev, + new_dev_mode, new_mode); + + mutex_lock(&cd->system_lock); + rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); + if (rc < 0) { + mutex_unlock(&cd->system_lock); + dev_err(cd->dev, "%s: Fail read mode r=%d\n", + __func__, rc); + goto exit; + } + + /* Clear device mode bits and set to new mode */ + mode &= ~CY_HST_MODE; + mode |= new_dev_mode | CY_HST_MODE_CHANGE; + + cd->int_status |= CY_INT_MODE_CHANGE; + rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode), &mode); + mutex_unlock(&cd->system_lock); + if (rc < 0) { + dev_err(cd->dev, "%s: Fail write mode change r=%d\n", + __func__, rc); + goto exit; + } + + /* wait for mode change done interrupt */ + t = wait_event_timeout(cd->wait_q, + (cd->int_status & CY_INT_MODE_CHANGE) == 0, + msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT)); + dev_dbg(cd->dev, "%s: back from wait t=%ld cd->mode=%d\n", + __func__, t, cd->mode); + + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: %s\n", __func__, + "tmo waiting mode change"); + mutex_lock(&cd->system_lock); + cd->int_status &= ~CY_INT_MODE_CHANGE; + mutex_unlock(&cd->system_lock); + rc = -EINVAL; + } + +exit: + return rc; +} + +static void cyttsp4_watchdog_work(struct work_struct *work) +{ + struct cyttsp4 *cd = + container_of(work, struct cyttsp4, watchdog_work); + u8 *mode; + int retval; + + if (cd == NULL) { + dev_err(cd->dev, "%s: NULL context pointer\n", __func__); + return; + } + + mutex_lock(&cd->system_lock); + retval = cyttsp4_load_status_regs(cd); + if (retval < 0) { + dev_err(cd->dev, + "%s: failed to access device in watchdog timer r=%d\n", + __func__, retval); + cyttsp4_queue_startup_(cd); + goto cyttsp4_timer_watchdog_exit_error; + } + mode = &cd->sysinfo.xy_mode[CY_REG_BASE]; + if (IS_BOOTLOADER(mode[0], mode[1])) { + dev_err(cd->dev, + "%s: device found in bootloader mode when operational mode\n", + __func__); + cyttsp4_queue_startup_(cd); + goto cyttsp4_timer_watchdog_exit_error; + } + + cyttsp4_start_wd_timer(cd); +cyttsp4_timer_watchdog_exit_error: + mutex_unlock(&cd->system_lock); + return; +} + +static int cyttsp4_core_sleep_(struct cyttsp4 *cd) +{ + enum cyttsp4_sleep_state ss = SS_SLEEP_ON; + enum cyttsp4_int_state int_status = CY_INT_IGNORE; + int rc = 0; + u8 mode[2]; + + /* Already in sleep mode? */ + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_ON) { + mutex_unlock(&cd->system_lock); + return 0; + } + cd->sleep_state = SS_SLEEPING; + mutex_unlock(&cd->system_lock); + + cyttsp4_stop_wd_timer(cd); + + /* Wait until currently running IRQ handler exits and disable IRQ */ + disable_irq(cd->irq); + + dev_vdbg(cd->dev, "%s: write DEEP SLEEP...\n", __func__); + mutex_lock(&cd->system_lock); + rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); + if (rc) { + mutex_unlock(&cd->system_lock); + dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc); + goto error; + } + + if (IS_BOOTLOADER(mode[0], mode[1])) { + mutex_unlock(&cd->system_lock); + dev_err(cd->dev, "%s: Device in BOOTLADER mode.\n", __func__); + rc = -EINVAL; + goto error; + } + + mode[0] |= CY_HST_SLEEP; + rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode[0]), &mode[0]); + mutex_unlock(&cd->system_lock); + if (rc) { + dev_err(cd->dev, "%s: Fail write adapter r=%d\n", __func__, rc); + goto error; + } + dev_vdbg(cd->dev, "%s: write DEEP SLEEP succeeded\n", __func__); + + if (cd->cpdata->power) { + dev_dbg(cd->dev, "%s: Power down HW\n", __func__); + rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, &cd->ignore_irq); + } else { + dev_dbg(cd->dev, "%s: No power function\n", __func__); + rc = 0; + } + if (rc < 0) { + dev_err(cd->dev, "%s: HW Power down fails r=%d\n", + __func__, rc); + goto error; + } + + /* Give time to FW to sleep */ + msleep(50); + + goto exit; + +error: + ss = SS_SLEEP_OFF; + int_status = CY_INT_NONE; + cyttsp4_start_wd_timer(cd); + +exit: + mutex_lock(&cd->system_lock); + cd->sleep_state = ss; + cd->int_status |= int_status; + mutex_unlock(&cd->system_lock); + enable_irq(cd->irq); + return rc; +} + +static int cyttsp4_core_sleep(struct cyttsp4 *cd) +{ + int rc; + + rc = cyttsp4_request_exclusive(cd, cd->dev, + CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return 0; + } + + rc = cyttsp4_core_sleep_(cd); + + if (cyttsp4_release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); + + return rc; +} + +static int cyttsp4_core_wake_(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + int rc; + u8 mode; + int t; + + /* Already woken? */ + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_OFF) { + mutex_unlock(&cd->system_lock); + return 0; + } + cd->int_status &= ~CY_INT_IGNORE; + cd->int_status |= CY_INT_AWAKE; + cd->sleep_state = SS_WAKING; + + if (cd->cpdata->power) { + dev_dbg(dev, "%s: Power up HW\n", __func__); + rc = cd->cpdata->power(cd->cpdata, 1, dev, &cd->ignore_irq); + } else { + dev_dbg(dev, "%s: No power function\n", __func__); + rc = -ENOSYS; + } + if (rc < 0) { + dev_err(dev, "%s: HW Power up fails r=%d\n", + __func__, rc); + + /* Initiate a read transaction to wake up */ + cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); + } else + dev_vdbg(cd->dev, "%s: HW power up succeeds\n", + __func__); + mutex_unlock(&cd->system_lock); + + t = wait_event_timeout(cd->wait_q, + (cd->int_status & CY_INT_AWAKE) == 0, + msecs_to_jiffies(CY_CORE_WAKEUP_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(dev, "%s: TMO waiting for wakeup\n", __func__); + mutex_lock(&cd->system_lock); + cd->int_status &= ~CY_INT_AWAKE; + /* Try starting up */ + cyttsp4_queue_startup_(cd); + mutex_unlock(&cd->system_lock); + } + + mutex_lock(&cd->system_lock); + cd->sleep_state = SS_SLEEP_OFF; + mutex_unlock(&cd->system_lock); + + cyttsp4_start_wd_timer(cd); + + return 0; +} + +static int cyttsp4_core_wake(struct cyttsp4 *cd) +{ + int rc; + + rc = cyttsp4_request_exclusive(cd, cd->dev, + CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return 0; + } + + rc = cyttsp4_core_wake_(cd); + + if (cyttsp4_release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); + + return rc; +} + +static int cyttsp4_startup_(struct cyttsp4 *cd) +{ + int retry = CY_CORE_STARTUP_RETRY_COUNT; + int rc; + + cyttsp4_stop_wd_timer(cd); + +reset: + if (retry != CY_CORE_STARTUP_RETRY_COUNT) + dev_dbg(cd->dev, "%s: Retry %d\n", __func__, + CY_CORE_STARTUP_RETRY_COUNT - retry); + + /* reset hardware and wait for heartbeat */ + rc = cyttsp4_reset_and_wait(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + /* exit bl into sysinfo mode */ + dev_vdbg(cd->dev, "%s: write exit ldr...\n", __func__); + mutex_lock(&cd->system_lock); + cd->int_status &= ~CY_INT_IGNORE; + cd->int_status |= CY_INT_MODE_CHANGE; + + rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(ldr_exit), + (u8 *)ldr_exit); + mutex_unlock(&cd->system_lock); + if (rc < 0) { + dev_err(cd->dev, "%s: Fail write r=%d\n", __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + rc = cyttsp4_wait_sysinfo_mode(cd); + if (rc < 0) { + u8 buf[sizeof(ldr_err_app)]; + int rc1; + + /* Check for invalid/corrupted touch application */ + rc1 = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(ldr_err_app), + buf); + if (rc1) { + dev_err(cd->dev, "%s: Fail read r=%d\n", __func__, rc1); + } else if (!memcmp(buf, ldr_err_app, sizeof(ldr_err_app))) { + dev_err(cd->dev, "%s: Error launching touch application\n", + __func__); + mutex_lock(&cd->system_lock); + cd->invalid_touch_app = true; + mutex_unlock(&cd->system_lock); + goto exit_no_wd; + } + + if (retry--) + goto reset; + goto exit; + } + + mutex_lock(&cd->system_lock); + cd->invalid_touch_app = false; + mutex_unlock(&cd->system_lock); + + /* read sysinfo data */ + dev_vdbg(cd->dev, "%s: get sysinfo regs..\n", __func__); + rc = cyttsp4_get_sysinfo_regs(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: failed to get sysinfo regs rc=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + rc = cyttsp4_set_mode(cd, CY_MODE_OPERATIONAL); + if (rc < 0) { + dev_err(cd->dev, "%s: failed to set mode to operational rc=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + cyttsp4_lift_all(&cd->md); + + /* restore to sleep if was suspended */ + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_ON) { + cd->sleep_state = SS_SLEEP_OFF; + mutex_unlock(&cd->system_lock); + cyttsp4_core_sleep_(cd); + goto exit_no_wd; + } + mutex_unlock(&cd->system_lock); + +exit: + cyttsp4_start_wd_timer(cd); +exit_no_wd: + return rc; +} + +static int cyttsp4_startup(struct cyttsp4 *cd) +{ + int rc; + + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_RUNNING; + mutex_unlock(&cd->system_lock); + + rc = cyttsp4_request_exclusive(cd, cd->dev, + CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + goto exit; + } + + rc = cyttsp4_startup_(cd); + + if (cyttsp4_release_exclusive(cd, cd->dev) < 0) + /* Don't return fail code, mode is already changed. */ + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); + +exit: + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_NONE; + mutex_unlock(&cd->system_lock); + + /* Wake the waiters for end of startup */ + wake_up(&cd->wait_q); + + return rc; +} + +static void cyttsp4_startup_work_function(struct work_struct *work) +{ + struct cyttsp4 *cd = container_of(work, struct cyttsp4, startup_work); + int rc; + + rc = cyttsp4_startup(cd); + if (rc < 0) + dev_err(cd->dev, "%s: Fail queued startup r=%d\n", + __func__, rc); +} + +static void cyttsp4_free_si_ptrs(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + + if (!si) + return; + + kfree(si->si_ptrs.cydata); + kfree(si->si_ptrs.test); + kfree(si->si_ptrs.pcfg); + kfree(si->si_ptrs.opcfg); + kfree(si->si_ptrs.ddata); + kfree(si->si_ptrs.mdata); + kfree(si->btn); + kfree(si->xy_mode); + kfree(si->xy_data); + kfree(si->btn_rec_data); +} + +#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME) +static int cyttsp4_core_suspend(struct device *dev) +{ + struct cyttsp4 *cd = dev_get_drvdata(dev); + struct cyttsp4_mt_data *md = &cd->md; + int rc; + + md->is_suspended = true; + + rc = cyttsp4_core_sleep(cd); + if (rc < 0) { + dev_err(dev, "%s: Error on sleep\n", __func__); + return -EAGAIN; + } + return 0; +} + +static int cyttsp4_core_resume(struct device *dev) +{ + struct cyttsp4 *cd = dev_get_drvdata(dev); + struct cyttsp4_mt_data *md = &cd->md; + int rc; + + md->is_suspended = false; + + rc = cyttsp4_core_wake(cd); + if (rc < 0) { + dev_err(dev, "%s: Error on wake\n", __func__); + return -EAGAIN; + } + + return 0; +} +#endif + +const struct dev_pm_ops cyttsp4_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume) + SET_RUNTIME_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume, NULL) +}; +EXPORT_SYMBOL_GPL(cyttsp4_pm_ops); + +static int cyttsp4_mt_open(struct input_dev *input) +{ + pm_runtime_get(input->dev.parent); + return 0; +} + +static void cyttsp4_mt_close(struct input_dev *input) +{ + struct cyttsp4_mt_data *md = input_get_drvdata(input); + mutex_lock(&md->report_lock); + if (!md->is_suspended) + pm_runtime_put(input->dev.parent); + mutex_unlock(&md->report_lock); +} + + +static int cyttsp4_setup_input_device(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + struct cyttsp4_mt_data *md = &cd->md; + int signal = CY_IGNORE_VALUE; + int max_x, max_y, max_p, min, max; + int max_x_tmp, max_y_tmp; + int i; + int rc; + + dev_vdbg(dev, "%s: Initialize event signals\n", __func__); + __set_bit(EV_ABS, md->input->evbit); + __set_bit(EV_REL, md->input->evbit); + __set_bit(EV_KEY, md->input->evbit); + + max_x_tmp = md->si->si_ofs.max_x; + max_y_tmp = md->si->si_ofs.max_y; + + /* get maximum values from the sysinfo data */ + if (md->pdata->flags & CY_FLAG_FLIP) { + max_x = max_y_tmp - 1; + max_y = max_x_tmp - 1; + } else { + max_x = max_x_tmp - 1; + max_y = max_y_tmp - 1; + } + max_p = md->si->si_ofs.max_p; + + /* set event signal capabilities */ + for (i = 0; i < (md->pdata->frmwrk->size / CY_NUM_ABS_SET); i++) { + signal = md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_SIGNAL_OST]; + if (signal != CY_IGNORE_VALUE) { + __set_bit(signal, md->input->absbit); + min = md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_MIN_OST]; + max = md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_MAX_OST]; + if (i == CY_ABS_ID_OST) { + /* shift track ids down to start at 0 */ + max = max - min; + min = min - min; + } else if (i == CY_ABS_X_OST) + max = max_x; + else if (i == CY_ABS_Y_OST) + max = max_y; + else if (i == CY_ABS_P_OST) + max = max_p; + input_set_abs_params(md->input, signal, min, max, + md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_FUZZ_OST], + md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_FLAT_OST]); + dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n", + __func__, signal, min, max); + if ((i == CY_ABS_ID_OST) && + (md->si->si_ofs.tch_rec_size < + CY_TMA4XX_TCH_REC_SIZE)) + break; + } + } + + input_mt_init_slots(md->input, md->si->si_ofs.tch_abs[CY_TCH_T].max, + INPUT_MT_DIRECT); + rc = input_register_device(md->input); + if (rc < 0) + dev_err(dev, "%s: Error, failed register input device r=%d\n", + __func__, rc); + return rc; +} + +static int cyttsp4_mt_probe(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + struct cyttsp4_mt_data *md = &cd->md; + struct cyttsp4_mt_platform_data *pdata = cd->pdata->mt_pdata; + int rc = 0; + + mutex_init(&md->report_lock); + md->pdata = pdata; + /* Create the input device and register it. */ + dev_vdbg(dev, "%s: Create the input device and register it\n", + __func__); + md->input = input_allocate_device(); + if (md->input == NULL) { + dev_err(dev, "%s: Error, failed to allocate input device\n", + __func__); + rc = -ENOSYS; + goto error_alloc_failed; + } + + md->input->name = pdata->inp_dev_name; + scnprintf(md->phys, sizeof(md->phys)-1, "%s", dev_name(dev)); + md->input->phys = md->phys; + md->input->id.bustype = cd->bus_ops->bustype; + md->input->dev.parent = dev; + md->input->open = cyttsp4_mt_open; + md->input->close = cyttsp4_mt_close; + input_set_drvdata(md->input, md); + + /* get sysinfo */ + md->si = &cd->sysinfo; + if (!md->si) { + dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n", + __func__, md->si); + goto error_get_sysinfo; + } + + rc = cyttsp4_setup_input_device(cd); + if (rc) + goto error_init_input; + + return 0; + +error_init_input: + input_free_device(md->input); +error_get_sysinfo: + input_set_drvdata(md->input, NULL); +error_alloc_failed: + dev_err(dev, "%s failed.\n", __func__); + return rc; +} + +struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + struct device *dev, u16 irq, size_t xfer_buf_size) +{ + struct cyttsp4 *cd; + struct cyttsp4_platform_data *pdata = dev_get_platdata(dev); + unsigned long irq_flags; + int rc = 0; + + if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) { + dev_err(dev, "%s: Missing platform data\n", __func__); + rc = -ENODEV; + goto error_no_pdata; + } + + cd = kzalloc(sizeof(*cd), GFP_KERNEL); + if (!cd) { + dev_err(dev, "%s: Error, kzalloc\n", __func__); + rc = -ENOMEM; + goto error_alloc_data; + } + + cd->xfer_buf = kzalloc(xfer_buf_size, GFP_KERNEL); + if (!cd->xfer_buf) { + dev_err(dev, "%s: Error, kzalloc\n", __func__); + rc = -ENOMEM; + goto error_alloc_data; + } + + /* Initialize device info */ + cd->dev = dev; + cd->pdata = pdata; + cd->cpdata = pdata->core_pdata; + cd->bus_ops = ops; + + /* Initialize mutexes and spinlocks */ + mutex_init(&cd->system_lock); + mutex_init(&cd->adap_lock); + + /* Initialize wait queue */ + init_waitqueue_head(&cd->wait_q); + + /* Initialize works */ + INIT_WORK(&cd->startup_work, cyttsp4_startup_work_function); + INIT_WORK(&cd->watchdog_work, cyttsp4_watchdog_work); + + /* Initialize IRQ */ + cd->irq = gpio_to_irq(cd->cpdata->irq_gpio); + if (cd->irq < 0) { + rc = -EINVAL; + goto error_gpio_irq; + } + + dev_set_drvdata(dev, cd); + + /* Call platform init function */ + if (cd->cpdata->init) { + dev_dbg(cd->dev, "%s: Init HW\n", __func__); + rc = cd->cpdata->init(cd->cpdata, 1, cd->dev); + } else { + dev_dbg(cd->dev, "%s: No HW INIT function\n", __func__); + rc = 0; + } + if (rc < 0) + dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc); + + dev_dbg(dev, "%s: initialize threaded irq=%d\n", __func__, cd->irq); + if (cd->cpdata->level_irq_udelay > 0) + /* use level triggered interrupts */ + irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + else + /* use edge triggered interrupts */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + + rc = request_threaded_irq(cd->irq, NULL, cyttsp4_irq, irq_flags, + dev_name(dev), cd); + if (rc < 0) { + dev_err(dev, "%s: Error, could not request irq\n", __func__); + goto error_request_irq; + } + + /* Setup watchdog timer */ + setup_timer(&cd->watchdog_timer, cyttsp4_watchdog_timer, + (unsigned long)cd); + + /* + * call startup directly to ensure that the device + * is tested before leaving the probe + */ + rc = cyttsp4_startup(cd); + + /* Do not fail probe if startup fails but the device is detected */ + if (rc < 0 && cd->mode == CY_MODE_UNKNOWN) { + dev_err(cd->dev, "%s: Fail initial startup r=%d\n", + __func__, rc); + goto error_startup; + } + + rc = cyttsp4_mt_probe(cd); + if (rc < 0) { + dev_err(dev, "%s: Error, fail mt probe\n", __func__); + goto error_startup; + } + + pm_runtime_enable(dev); + + return cd; + +error_startup: + cancel_work_sync(&cd->startup_work); + cyttsp4_stop_wd_timer(cd); + pm_runtime_disable(dev); + cyttsp4_free_si_ptrs(cd); + free_irq(cd->irq, cd); +error_request_irq: + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, 0, dev); + dev_set_drvdata(dev, NULL); +error_gpio_irq: + kfree(cd); +error_alloc_data: +error_no_pdata: + dev_err(dev, "%s failed.\n", __func__); + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(cyttsp4_probe); + +static void cyttsp4_mt_release(struct cyttsp4_mt_data *md) +{ + input_unregister_device(md->input); + input_set_drvdata(md->input, NULL); +} + +int cyttsp4_remove(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + + cyttsp4_mt_release(&cd->md); + + /* + * Suspend the device before freeing the startup_work and stopping + * the watchdog since sleep function restarts watchdog on failure + */ + pm_runtime_suspend(dev); + pm_runtime_disable(dev); + + cancel_work_sync(&cd->startup_work); + + cyttsp4_stop_wd_timer(cd); + + free_irq(cd->irq, cd); + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, 0, dev); + dev_set_drvdata(dev, NULL); + cyttsp4_free_si_ptrs(cd); + kfree(cd); + return 0; +} +EXPORT_SYMBOL_GPL(cyttsp4_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen core driver"); +MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp4_core.h b/drivers/input/touchscreen/cyttsp4_core.h new file mode 100644 index 000000000000..86a254354136 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_core.h @@ -0,0 +1,472 @@ +/* + * cyttsp4_core.h + * Cypress TrueTouch(TM) Standard Product V4 Core driver module. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2012 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#ifndef _LINUX_CYTTSP4_CORE_H +#define _LINUX_CYTTSP4_CORE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CY_REG_BASE 0x00 + +#define CY_POST_CODEL_WDG_RST 0x01 +#define CY_POST_CODEL_CFG_DATA_CRC_FAIL 0x02 +#define CY_POST_CODEL_PANEL_TEST_FAIL 0x04 + +#define CY_NUM_BTN_PER_REG 4 + +/* touch record system information offset masks and shifts */ +#define CY_BYTE_OFS_MASK 0x1F +#define CY_BOFS_MASK 0xE0 +#define CY_BOFS_SHIFT 5 + +#define CY_TMA1036_TCH_REC_SIZE 6 +#define CY_TMA4XX_TCH_REC_SIZE 9 +#define CY_TMA1036_MAX_TCH 0x0E +#define CY_TMA4XX_MAX_TCH 0x1E + +#define CY_NORMAL_ORIGIN 0 /* upper, left corner */ +#define CY_INVERT_ORIGIN 1 /* lower, right corner */ + +/* helpers */ +#define GET_NUM_TOUCHES(x) ((x) & 0x1F) +#define IS_LARGE_AREA(x) ((x) & 0x20) +#define IS_BAD_PKT(x) ((x) & 0x20) +#define IS_BOOTLOADER(hst_mode, reset_detect) \ + ((hst_mode) & 0x01 || (reset_detect) != 0) +#define IS_TMO(t) ((t) == 0) + + +enum cyttsp_cmd_bits { + CY_CMD_COMPLETE = (1 << 6), +}; + +/* Timeout in ms. */ +#define CY_WATCHDOG_TIMEOUT 1000 + +#define CY_MAX_PRINT_SIZE 512 +#ifdef VERBOSE_DEBUG +#define CY_MAX_PRBUF_SIZE PIPE_BUF +#define CY_PR_TRUNCATED " truncated..." +#endif + +enum cyttsp4_ic_grpnum { + CY_IC_GRPNUM_RESERVED, + CY_IC_GRPNUM_CMD_REGS, + CY_IC_GRPNUM_TCH_REP, + CY_IC_GRPNUM_DATA_REC, + CY_IC_GRPNUM_TEST_REC, + CY_IC_GRPNUM_PCFG_REC, + CY_IC_GRPNUM_TCH_PARM_VAL, + CY_IC_GRPNUM_TCH_PARM_SIZE, + CY_IC_GRPNUM_RESERVED1, + CY_IC_GRPNUM_RESERVED2, + CY_IC_GRPNUM_OPCFG_REC, + CY_IC_GRPNUM_DDATA_REC, + CY_IC_GRPNUM_MDATA_REC, + CY_IC_GRPNUM_TEST_REGS, + CY_IC_GRPNUM_BTN_KEYS, + CY_IC_GRPNUM_TTHE_REGS, + CY_IC_GRPNUM_NUM +}; + +enum cyttsp4_int_state { + CY_INT_NONE, + CY_INT_IGNORE = (1 << 0), + CY_INT_MODE_CHANGE = (1 << 1), + CY_INT_EXEC_CMD = (1 << 2), + CY_INT_AWAKE = (1 << 3), +}; + +enum cyttsp4_mode { + CY_MODE_UNKNOWN, + CY_MODE_BOOTLOADER = (1 << 1), + CY_MODE_OPERATIONAL = (1 << 2), + CY_MODE_SYSINFO = (1 << 3), + CY_MODE_CAT = (1 << 4), + CY_MODE_STARTUP = (1 << 5), + CY_MODE_LOADER = (1 << 6), + CY_MODE_CHANGE_MODE = (1 << 7), + CY_MODE_CHANGED = (1 << 8), + CY_MODE_CMD_COMPLETE = (1 << 9), +}; + +enum cyttsp4_sleep_state { + SS_SLEEP_OFF, + SS_SLEEP_ON, + SS_SLEEPING, + SS_WAKING, +}; + +enum cyttsp4_startup_state { + STARTUP_NONE, + STARTUP_QUEUED, + STARTUP_RUNNING, +}; + +#define CY_NUM_REVCTRL 8 +struct cyttsp4_cydata { + u8 ttpidh; + u8 ttpidl; + u8 fw_ver_major; + u8 fw_ver_minor; + u8 revctrl[CY_NUM_REVCTRL]; + u8 blver_major; + u8 blver_minor; + u8 jtag_si_id3; + u8 jtag_si_id2; + u8 jtag_si_id1; + u8 jtag_si_id0; + u8 mfgid_sz; + u8 cyito_idh; + u8 cyito_idl; + u8 cyito_verh; + u8 cyito_verl; + u8 ttsp_ver_major; + u8 ttsp_ver_minor; + u8 device_info; + u8 mfg_id[]; +} __packed; + +struct cyttsp4_test { + u8 post_codeh; + u8 post_codel; +} __packed; + +struct cyttsp4_pcfg { + u8 electrodes_x; + u8 electrodes_y; + u8 len_xh; + u8 len_xl; + u8 len_yh; + u8 len_yl; + u8 res_xh; + u8 res_xl; + u8 res_yh; + u8 res_yl; + u8 max_zh; + u8 max_zl; + u8 panel_info0; +} __packed; + +struct cyttsp4_tch_rec_params { + u8 loc; + u8 size; +} __packed; + +#define CY_NUM_TCH_FIELDS 7 +#define CY_NUM_EXT_TCH_FIELDS 3 +struct cyttsp4_opcfg { + u8 cmd_ofs; + u8 rep_ofs; + u8 rep_szh; + u8 rep_szl; + u8 num_btns; + u8 tt_stat_ofs; + u8 obj_cfg0; + u8 max_tchs; + u8 tch_rec_size; + struct cyttsp4_tch_rec_params tch_rec_old[CY_NUM_TCH_FIELDS]; + u8 btn_rec_size; /* btn record size (in bytes) */ + u8 btn_diff_ofs; /* btn data loc, diff counts */ + u8 btn_diff_size; /* btn size of diff counts (in bits) */ + struct cyttsp4_tch_rec_params tch_rec_new[CY_NUM_EXT_TCH_FIELDS]; +} __packed; + +struct cyttsp4_sysinfo_ptr { + struct cyttsp4_cydata *cydata; + struct cyttsp4_test *test; + struct cyttsp4_pcfg *pcfg; + struct cyttsp4_opcfg *opcfg; + struct cyttsp4_ddata *ddata; + struct cyttsp4_mdata *mdata; +} __packed; + +struct cyttsp4_sysinfo_data { + u8 hst_mode; + u8 reserved; + u8 map_szh; + u8 map_szl; + u8 cydata_ofsh; + u8 cydata_ofsl; + u8 test_ofsh; + u8 test_ofsl; + u8 pcfg_ofsh; + u8 pcfg_ofsl; + u8 opcfg_ofsh; + u8 opcfg_ofsl; + u8 ddata_ofsh; + u8 ddata_ofsl; + u8 mdata_ofsh; + u8 mdata_ofsl; +} __packed; + +enum cyttsp4_tch_abs { /* for ordering within the extracted touch data array */ + CY_TCH_X, /* X */ + CY_TCH_Y, /* Y */ + CY_TCH_P, /* P (Z) */ + CY_TCH_T, /* TOUCH ID */ + CY_TCH_E, /* EVENT ID */ + CY_TCH_O, /* OBJECT ID */ + CY_TCH_W, /* SIZE */ + CY_TCH_MAJ, /* TOUCH_MAJOR */ + CY_TCH_MIN, /* TOUCH_MINOR */ + CY_TCH_OR, /* ORIENTATION */ + CY_TCH_NUM_ABS +}; + +static const char * const cyttsp4_tch_abs_string[] = { + [CY_TCH_X] = "X", + [CY_TCH_Y] = "Y", + [CY_TCH_P] = "P", + [CY_TCH_T] = "T", + [CY_TCH_E] = "E", + [CY_TCH_O] = "O", + [CY_TCH_W] = "W", + [CY_TCH_MAJ] = "MAJ", + [CY_TCH_MIN] = "MIN", + [CY_TCH_OR] = "OR", + [CY_TCH_NUM_ABS] = "INVALID" +}; + +struct cyttsp4_touch { + int abs[CY_TCH_NUM_ABS]; +}; + +struct cyttsp4_tch_abs_params { + size_t ofs; /* abs byte offset */ + size_t size; /* size in bits */ + size_t max; /* max value */ + size_t bofs; /* bit offset */ +}; + +struct cyttsp4_sysinfo_ofs { + size_t chip_type; + size_t cmd_ofs; + size_t rep_ofs; + size_t rep_sz; + size_t num_btns; + size_t num_btn_regs; /* ceil(num_btns/4) */ + size_t tt_stat_ofs; + size_t tch_rec_size; + size_t obj_cfg0; + size_t max_tchs; + size_t mode_size; + size_t data_size; + size_t map_sz; + size_t max_x; + size_t x_origin; /* left or right corner */ + size_t max_y; + size_t y_origin; /* upper or lower corner */ + size_t max_p; + size_t cydata_ofs; + size_t test_ofs; + size_t pcfg_ofs; + size_t opcfg_ofs; + size_t ddata_ofs; + size_t mdata_ofs; + size_t cydata_size; + size_t test_size; + size_t pcfg_size; + size_t opcfg_size; + size_t ddata_size; + size_t mdata_size; + size_t btn_keys_size; + struct cyttsp4_tch_abs_params tch_abs[CY_TCH_NUM_ABS]; + size_t btn_rec_size; /* btn record size (in bytes) */ + size_t btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */ + size_t btn_diff_size;/* btn size of diff counts (in bits) */ +}; + +enum cyttsp4_btn_state { + CY_BTN_RELEASED, + CY_BTN_PRESSED, + CY_BTN_NUM_STATE +}; + +struct cyttsp4_btn { + bool enabled; + int state; /* CY_BTN_PRESSED, CY_BTN_RELEASED */ + int key_code; +}; + +struct cyttsp4_sysinfo { + bool ready; + struct cyttsp4_sysinfo_data si_data; + struct cyttsp4_sysinfo_ptr si_ptrs; + struct cyttsp4_sysinfo_ofs si_ofs; + struct cyttsp4_btn *btn; /* button states */ + u8 *btn_rec_data; /* button diff count data */ + u8 *xy_mode; /* operational mode and status regs */ + u8 *xy_data; /* operational touch regs */ +}; + +struct cyttsp4_mt_data { + struct cyttsp4_mt_platform_data *pdata; + struct cyttsp4_sysinfo *si; + struct input_dev *input; + struct mutex report_lock; + bool is_suspended; + char phys[NAME_MAX]; + int num_prv_tch; +}; + +struct cyttsp4 { + struct device *dev; + struct mutex system_lock; + struct mutex adap_lock; + enum cyttsp4_mode mode; + enum cyttsp4_sleep_state sleep_state; + enum cyttsp4_startup_state startup_state; + int int_status; + wait_queue_head_t wait_q; + int irq; + struct work_struct startup_work; + struct work_struct watchdog_work; + struct timer_list watchdog_timer; + struct cyttsp4_sysinfo sysinfo; + void *exclusive_dev; + int exclusive_waits; + atomic_t ignore_irq; + bool invalid_touch_app; + struct cyttsp4_mt_data md; + struct cyttsp4_platform_data *pdata; + struct cyttsp4_core_platform_data *cpdata; + const struct cyttsp4_bus_ops *bus_ops; + u8 *xfer_buf; +#ifdef VERBOSE_DEBUG + u8 pr_buf[CY_MAX_PRBUF_SIZE]; +#endif +}; + +struct cyttsp4_bus_ops { + u16 bustype; + int (*write)(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, + const void *values); + int (*read)(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, + void *values); +}; + +enum cyttsp4_hst_mode_bits { + CY_HST_TOGGLE = (1 << 7), + CY_HST_MODE_CHANGE = (1 << 3), + CY_HST_MODE = (7 << 4), + CY_HST_OPERATE = (0 << 4), + CY_HST_SYSINFO = (1 << 4), + CY_HST_CAT = (2 << 4), + CY_HST_LOWPOW = (1 << 2), + CY_HST_SLEEP = (1 << 1), + CY_HST_RESET = (1 << 0), +}; + +/* abs settings */ +#define CY_IGNORE_VALUE 0xFFFF + +/* abs signal capabilities offsets in the frameworks array */ +enum cyttsp4_sig_caps { + CY_SIGNAL_OST, + CY_MIN_OST, + CY_MAX_OST, + CY_FUZZ_OST, + CY_FLAT_OST, + CY_NUM_ABS_SET /* number of signal capability fields */ +}; + +/* abs axis signal offsets in the framworks array */ +enum cyttsp4_sig_ost { + CY_ABS_X_OST, + CY_ABS_Y_OST, + CY_ABS_P_OST, + CY_ABS_W_OST, + CY_ABS_ID_OST, + CY_ABS_MAJ_OST, + CY_ABS_MIN_OST, + CY_ABS_OR_OST, + CY_NUM_ABS_OST /* number of abs signals */ +}; + +enum cyttsp4_flags { + CY_FLAG_NONE = 0x00, + CY_FLAG_HOVER = 0x04, + CY_FLAG_FLIP = 0x08, + CY_FLAG_INV_X = 0x10, + CY_FLAG_INV_Y = 0x20, + CY_FLAG_VKEYS = 0x40, +}; + +enum cyttsp4_object_id { + CY_OBJ_STANDARD_FINGER, + CY_OBJ_LARGE_OBJECT, + CY_OBJ_STYLUS, + CY_OBJ_HOVER, +}; + +enum cyttsp4_event_id { + CY_EV_NO_EVENT, + CY_EV_TOUCHDOWN, + CY_EV_MOVE, /* significant displacement (> act dist) */ + CY_EV_LIFTOFF, /* record reports last position */ +}; + +/* x-axis resolution of panel in pixels */ +#define CY_PCFG_RESOLUTION_X_MASK 0x7F + +/* y-axis resolution of panel in pixels */ +#define CY_PCFG_RESOLUTION_Y_MASK 0x7F + +/* x-axis, 0:origin is on left side of panel, 1: right */ +#define CY_PCFG_ORIGIN_X_MASK 0x80 + +/* y-axis, 0:origin is on top side of panel, 1: bottom */ +#define CY_PCFG_ORIGIN_Y_MASK 0x80 + +static inline int cyttsp4_adap_read(struct cyttsp4 *ts, u8 addr, int size, + void *buf) +{ + return ts->bus_ops->read(ts->dev, ts->xfer_buf, addr, size, buf); +} + +static inline int cyttsp4_adap_write(struct cyttsp4 *ts, u8 addr, int size, + const void *buf) +{ + return ts->bus_ops->write(ts->dev, ts->xfer_buf, addr, size, buf); +} + +extern struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + struct device *dev, u16 irq, size_t xfer_buf_size); +extern int cyttsp4_remove(struct cyttsp4 *ts); +int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u8 addr, + u8 length, const void *values); +int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u8 addr, + u8 length, void *values); +extern const struct dev_pm_ops cyttsp4_pm_ops; + +#endif /* _LINUX_CYTTSP4_CORE_H */ diff --git a/include/linux/platform_data/cyttsp4.h b/include/linux/platform_data/cyttsp4.h new file mode 100644 index 000000000000..6eba54aff1dc --- /dev/null +++ b/include/linux/platform_data/cyttsp4.h @@ -0,0 +1,76 @@ +/* + * Header file for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com (kev@cypress.com) + * + */ +#ifndef _CYTTSP4_H_ +#define _CYTTSP4_H_ + +#define CYTTSP4_MT_NAME "cyttsp4_mt" +#define CYTTSP4_I2C_NAME "cyttsp4_i2c_adapter" +#define CYTTSP4_SPI_NAME "cyttsp4_spi_adapter" + +#define CY_TOUCH_SETTINGS_MAX 32 + +struct touch_framework { + const uint16_t *abs; + uint8_t size; + uint8_t enable_vkeys; +} __packed; + +struct cyttsp4_mt_platform_data { + struct touch_framework *frmwrk; + unsigned short flags; + char const *inp_dev_name; +}; + +struct touch_settings { + const uint8_t *data; + uint32_t size; + uint8_t tag; +} __packed; + +struct cyttsp4_core_platform_data { + int irq_gpio; + int rst_gpio; + int level_irq_udelay; + int (*xres)(struct cyttsp4_core_platform_data *pdata, + struct device *dev); + int (*init)(struct cyttsp4_core_platform_data *pdata, + int on, struct device *dev); + int (*power)(struct cyttsp4_core_platform_data *pdata, + int on, struct device *dev, atomic_t *ignore_irq); + int (*irq_stat)(struct cyttsp4_core_platform_data *pdata, + struct device *dev); + struct touch_settings *sett[CY_TOUCH_SETTINGS_MAX]; +}; + +struct cyttsp4_platform_data { + struct cyttsp4_core_platform_data *core_pdata; + struct cyttsp4_mt_platform_data *mt_pdata; +}; + +#endif /* _CYTTSP4_H_ */ -- cgit v1.3-18-gd494 From e4760363ea14b7b707ba68ab780c8ecf98a84c15 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Mon, 1 Jul 2013 15:30:51 +0300 Subject: remoteproc/omap: fix a sparse warning This patch fixes a sparse warning in the omap remoteproc code when OMAP_REMOTEPROC is disabled. include/linux/platform_data/remoteproc-omap.h:76:13: warning: symbol 'omap_rproc_reserve_cma' was not declared. Should it be static? Signed-off-by: Suman Anna Signed-off-by: Ohad Ben-Cohen --- include/linux/platform_data/remoteproc-omap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/remoteproc-omap.h b/include/linux/platform_data/remoteproc-omap.h index 3c1c6444ec4b..bfbd12b41162 100644 --- a/include/linux/platform_data/remoteproc-omap.h +++ b/include/linux/platform_data/remoteproc-omap.h @@ -50,7 +50,7 @@ void __init omap_rproc_reserve_cma(void); #else -void __init omap_rproc_reserve_cma(void) +static inline void __init omap_rproc_reserve_cma(void) { } -- cgit v1.3-18-gd494 From 290ad0f9d954b445788bf26652b239c59cec2060 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sun, 26 May 2013 11:53:20 +0200 Subject: dma: imx-dma: Add oftree support Adding devicetree support for imx-dma driver. Use driver name for function 'imx_dma_is_general_purpose' because the devicename for devicetree initialized devices is different. Signed-off-by: Markus Pargmann Reviewed-by: Arnd Bergmann Reviewed-by: Shawn Guo Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- .../devicetree/bindings/dma/fsl-imx-dma.txt | 48 ++++++++++++++ drivers/dma/imx-dma.c | 75 ++++++++++++++++++++++ include/linux/platform_data/dma-imx.h | 6 +- 3 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/dma/fsl-imx-dma.txt (limited to 'include/linux/platform_data') diff --git a/Documentation/devicetree/bindings/dma/fsl-imx-dma.txt b/Documentation/devicetree/bindings/dma/fsl-imx-dma.txt new file mode 100644 index 000000000000..2717ecb47db9 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/fsl-imx-dma.txt @@ -0,0 +1,48 @@ +* Freescale Direct Memory Access (DMA) Controller for i.MX + +This document will only describe differences to the generic DMA Controller and +DMA request bindings as described in dma/dma.txt . + +* DMA controller + +Required properties: +- compatible : Should be "fsl,-dma". chip can be imx1, imx21 or imx27 +- reg : Should contain DMA registers location and length +- interrupts : First item should be DMA interrupt, second one is optional and + should contain DMA Error interrupt +- #dma-cells : Has to be 1. imx-dma does not support anything else. + +Optional properties: +- #dma-channels : Number of DMA channels supported. Should be 16. +- #dma-requests : Number of DMA requests supported. + +Example: + + dma: dma@10001000 { + compatible = "fsl,imx27-dma"; + reg = <0x10001000 0x1000>; + interrupts = <32 33>; + #dma-cells = <1>; + #dma-channels = <16>; + }; + + +* DMA client + +Clients have to specify the DMA requests with phandles in a list. + +Required properties: +- dmas: List of one or more DMA request specifiers. One DMA request specifier + consists of a phandle to the DMA controller followed by the integer + specifiying the request line. +- dma-names: List of string identifiers for the DMA requests. For the correct + names, have a look at the specific client driver. + +Example: + + sdhci1: sdhci@10013000 { + ... + dmas = <&dma 7>; + dma-names = "rx-tx"; + ... + }; diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index f28583370d00..34c54cf08231 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -186,6 +188,11 @@ struct imxdma_engine { enum imx_dma_type devtype; }; +struct imxdma_filter_data { + struct imxdma_engine *imxdma; + int request; +}; + static struct platform_device_id imx_dma_devtype[] = { { .name = "imx1-dma", @@ -202,6 +209,22 @@ static struct platform_device_id imx_dma_devtype[] = { }; MODULE_DEVICE_TABLE(platform, imx_dma_devtype); +static const struct of_device_id imx_dma_of_dev_id[] = { + { + .compatible = "fsl,imx1-dma", + .data = &imx_dma_devtype[IMX1_DMA], + }, { + .compatible = "fsl,imx21-dma", + .data = &imx_dma_devtype[IMX21_DMA], + }, { + .compatible = "fsl,imx27-dma", + .data = &imx_dma_devtype[IMX27_DMA], + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, imx_dma_of_dev_id); + static inline int is_imx1_dma(struct imxdma_engine *imxdma) { return imxdma->devtype == IMX1_DMA; @@ -996,13 +1019,50 @@ static void imxdma_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&imxdma->lock, flags); } +static bool imxdma_filter_fn(struct dma_chan *chan, void *param) +{ + struct imxdma_filter_data *fdata = param; + struct imxdma_channel *imxdma_chan = to_imxdma_chan(chan); + + if (chan->device->dev != fdata->imxdma->dev) + return false; + + imxdma_chan->dma_request = fdata->request; + chan->private = NULL; + + return true; +} + +static struct dma_chan *imxdma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + int count = dma_spec->args_count; + struct imxdma_engine *imxdma = ofdma->of_dma_data; + struct imxdma_filter_data fdata = { + .imxdma = imxdma, + }; + + if (count != 1) + return NULL; + + fdata.request = dma_spec->args[0]; + + return dma_request_channel(imxdma->dma_device.cap_mask, + imxdma_filter_fn, &fdata); +} + static int __init imxdma_probe(struct platform_device *pdev) { struct imxdma_engine *imxdma; struct resource *res; + const struct of_device_id *of_id; int ret, i; int irq, irq_err; + of_id = of_match_device(imx_dma_of_dev_id, &pdev->dev); + if (of_id) + pdev->id_entry = of_id->data; + imxdma = devm_kzalloc(&pdev->dev, sizeof(*imxdma), GFP_KERNEL); if (!imxdma) return -ENOMEM; @@ -1136,8 +1196,19 @@ static int __init imxdma_probe(struct platform_device *pdev) goto err; } + if (pdev->dev.of_node) { + ret = of_dma_controller_register(pdev->dev.of_node, + imxdma_xlate, imxdma); + if (ret) { + dev_err(&pdev->dev, "unable to register of_dma_controller\n"); + goto err_of_dma_controller; + } + } + return 0; +err_of_dma_controller: + dma_async_device_unregister(&imxdma->dma_device); err: clk_disable_unprepare(imxdma->dma_ipg); clk_disable_unprepare(imxdma->dma_ahb); @@ -1150,6 +1221,9 @@ static int imxdma_remove(struct platform_device *pdev) dma_async_device_unregister(&imxdma->dma_device); + if (pdev->dev.of_node) + of_dma_controller_free(pdev->dev.of_node); + clk_disable_unprepare(imxdma->dma_ipg); clk_disable_unprepare(imxdma->dma_ahb); @@ -1159,6 +1233,7 @@ static int imxdma_remove(struct platform_device *pdev) static struct platform_driver imxdma_driver = { .driver = { .name = "imx-dma", + .of_match_table = imx_dma_of_dev_id, }, .id_table = imx_dma_devtype, .remove = imxdma_remove, diff --git a/include/linux/platform_data/dma-imx.h b/include/linux/platform_data/dma-imx.h index f6d30cc1cb77..beac6b8b6a7b 100644 --- a/include/linux/platform_data/dma-imx.h +++ b/include/linux/platform_data/dma-imx.h @@ -60,10 +60,8 @@ static inline int imx_dma_is_ipu(struct dma_chan *chan) static inline int imx_dma_is_general_purpose(struct dma_chan *chan) { - return strstr(dev_name(chan->device->dev), "sdma") || - !strcmp(dev_name(chan->device->dev), "imx1-dma") || - !strcmp(dev_name(chan->device->dev), "imx21-dma") || - !strcmp(dev_name(chan->device->dev), "imx27-dma"); + return !strcmp(chan->device->dev->driver->name, "imx-sdma") || + !strcmp(chan->device->dev->driver->name, "imx-dma"); } #endif -- cgit v1.3-18-gd494 From 72ae6e4b31e40397eaa81007b39a1074638a6798 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 10 May 2013 15:19:14 +0200 Subject: dmaengine: at_hdmac: extend hardware handshaking interface identification Peripheral handshaking identification numbers can be bigger than 15, so new fields have been created in the CFG register. Add macros to take this modification into account and use them in at_dma_xlate() function. Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 2 ++ include/linux/platform_data/dma-atmel.h | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'include/linux/platform_data') diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index cd494209352a..78c3fb4b4e40 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1230,6 +1230,8 @@ static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec, per_id = dma_spec->args[1]; atslave->cfg = ATC_FIFOCFG_HALFFIFO | ATC_DST_H2SEL_HW | ATC_SRC_H2SEL_HW | ATC_DST_PER(per_id) + | ATC_DST_PER_MSB(per_id) + | ATC_SRC_PER_MSB(per_id) | ATC_SRC_PER(per_id); atslave->dma_dev = &dmac_pdev->dev; diff --git a/include/linux/platform_data/dma-atmel.h b/include/linux/platform_data/dma-atmel.h index cab0997be3de..e95f19c65873 100644 --- a/include/linux/platform_data/dma-atmel.h +++ b/include/linux/platform_data/dma-atmel.h @@ -35,16 +35,20 @@ struct at_dma_slave { /* Platform-configurable bits in CFG */ +#define ATC_PER_MSB(h) ((0x30U & (h)) >> 4) /* Extract most significant bits of a handshaking identifier */ + #define ATC_SRC_PER(h) (0xFU & (h)) /* Channel src rq associated with periph handshaking ifc h */ #define ATC_DST_PER(h) ((0xFU & (h)) << 4) /* Channel dst rq associated with periph handshaking ifc h */ #define ATC_SRC_REP (0x1 << 8) /* Source Replay Mod */ #define ATC_SRC_H2SEL (0x1 << 9) /* Source Handshaking Mod */ #define ATC_SRC_H2SEL_SW (0x0 << 9) #define ATC_SRC_H2SEL_HW (0x1 << 9) +#define ATC_SRC_PER_MSB(h) (ATC_PER_MSB(h) << 10) /* Channel src rq (most significant bits) */ #define ATC_DST_REP (0x1 << 12) /* Destination Replay Mod */ #define ATC_DST_H2SEL (0x1 << 13) /* Destination Handshaking Mod */ #define ATC_DST_H2SEL_SW (0x0 << 13) #define ATC_DST_H2SEL_HW (0x1 << 13) +#define ATC_DST_PER_MSB(h) (ATC_PER_MSB(h) << 14) /* Channel dst rq (most significant bits) */ #define ATC_SOD (0x1 << 16) /* Stop On Done */ #define ATC_LOCK_IF (0x1 << 20) /* Interface Lock */ #define ATC_LOCK_B (0x1 << 21) /* AHB Bus Lock */ -- cgit v1.3-18-gd494 From a829abf8daa2dcf8223a9284b76d221e61130e13 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 5 Jul 2013 17:51:20 +0200 Subject: ARM: pxa: propagate errors from regulator_enable() to pxamci The em_x270_mci_setpower() and em_x270_usb_hub_init() functions call regulator_enable(), which may return an error that must be checked. This changes the em_x270_usb_hub_init() function to bail out if it fails, and changes the pxamci_platform_data->setpower callback so that the a failed em_x270_mci_setpower call can be propagated by the pxamci driver into the mmc core. Signed-off-by: Arnd Bergmann Cc: Mike Rapoport Cc: Paul Gortmaker Cc: Mark Brown Cc: Haojian Zhuang Acked-by: Chris Ball [olof: fixed order of regulator_enable() and test in em_x270_usb_hub_init] Signed-off-by: Olof Johansson --- arch/arm/mach-pxa/em-x270.c | 17 +++++++++++++---- arch/arm/mach-pxa/mainstone.c | 3 ++- arch/arm/mach-pxa/pcm990-baseboard.c | 3 ++- arch/arm/mach-pxa/poodle.c | 4 +++- arch/arm/mach-pxa/spitz.c | 4 +++- arch/arm/mach-pxa/stargate2.c | 3 ++- drivers/mmc/host/pxamci.c | 2 +- include/linux/platform_data/mmc-pxamci.h | 2 +- 8 files changed, 27 insertions(+), 11 deletions(-) (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-pxa/em-x270.c b/arch/arm/mach-pxa/em-x270.c index f6726bb4eb95..3a3362fa793e 100644 --- a/arch/arm/mach-pxa/em-x270.c +++ b/arch/arm/mach-pxa/em-x270.c @@ -477,16 +477,24 @@ static int em_x270_usb_hub_init(void) /* USB Hub power-on and reset */ gpio_direction_output(usb_hub_reset, 1); gpio_direction_output(GPIO9_USB_VBUS_EN, 0); - regulator_enable(em_x270_usb_ldo); + err = regulator_enable(em_x270_usb_ldo); + if (err) + goto err_free_rst_gpio; + gpio_set_value(usb_hub_reset, 0); gpio_set_value(usb_hub_reset, 1); regulator_disable(em_x270_usb_ldo); - regulator_enable(em_x270_usb_ldo); + err = regulator_enable(em_x270_usb_ldo); + if (err) + goto err_free_rst_gpio; + gpio_set_value(usb_hub_reset, 0); gpio_set_value(GPIO9_USB_VBUS_EN, 1); return 0; +err_free_rst_gpio: + gpio_free(usb_hub_reset); err_free_vbus_gpio: gpio_free(GPIO9_USB_VBUS_EN); err_free_usb_ldo: @@ -592,7 +600,7 @@ err_irq: return err; } -static void em_x270_mci_setpower(struct device *dev, unsigned int vdd) +static int em_x270_mci_setpower(struct device *dev, unsigned int vdd) { struct pxamci_platform_data* p_d = dev->platform_data; @@ -600,10 +608,11 @@ static void em_x270_mci_setpower(struct device *dev, unsigned int vdd) int vdd_uV = (2000 + (vdd - __ffs(MMC_VDD_20_21)) * 100) * 1000; regulator_set_voltage(em_x270_sdio_ldo, vdd_uV, vdd_uV); - regulator_enable(em_x270_sdio_ldo); + return regulator_enable(em_x270_sdio_ldo); } else { regulator_disable(em_x270_sdio_ldo); } + return 0; } static void em_x270_mci_exit(struct device *dev, void *data) diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c index d2c652318376..dd70343c8708 100644 --- a/arch/arm/mach-pxa/mainstone.c +++ b/arch/arm/mach-pxa/mainstone.c @@ -408,7 +408,7 @@ static int mainstone_mci_init(struct device *dev, irq_handler_t mstone_detect_in return err; } -static void mainstone_mci_setpower(struct device *dev, unsigned int vdd) +static int mainstone_mci_setpower(struct device *dev, unsigned int vdd) { struct pxamci_platform_data* p_d = dev->platform_data; @@ -420,6 +420,7 @@ static void mainstone_mci_setpower(struct device *dev, unsigned int vdd) printk(KERN_DEBUG "%s: off\n", __func__); MST_MSCWR1 &= ~MST_MSCWR1_MMC_ON; } + return 0; } static void mainstone_mci_exit(struct device *dev, void *data) diff --git a/arch/arm/mach-pxa/pcm990-baseboard.c b/arch/arm/mach-pxa/pcm990-baseboard.c index fb7f1d1627dc..13e5b00eae90 100644 --- a/arch/arm/mach-pxa/pcm990-baseboard.c +++ b/arch/arm/mach-pxa/pcm990-baseboard.c @@ -335,7 +335,7 @@ static int pcm990_mci_init(struct device *dev, irq_handler_t mci_detect_int, return err; } -static void pcm990_mci_setpower(struct device *dev, unsigned int vdd) +static int pcm990_mci_setpower(struct device *dev, unsigned int vdd) { struct pxamci_platform_data *p_d = dev->platform_data; u8 val; @@ -348,6 +348,7 @@ static void pcm990_mci_setpower(struct device *dev, unsigned int vdd) val &= ~PCM990_CTRL_MMC2PWR; pcm990_cpld_writeb(PCM990_CTRL_MMC2PWR, PCM990_CTRL_REG5); + return 0; } static void pcm990_mci_exit(struct device *dev, void *data) diff --git a/arch/arm/mach-pxa/poodle.c b/arch/arm/mach-pxa/poodle.c index 711d37e26bd8..aedf053a1de5 100644 --- a/arch/arm/mach-pxa/poodle.c +++ b/arch/arm/mach-pxa/poodle.c @@ -258,7 +258,7 @@ err_free_2: return err; } -static void poodle_mci_setpower(struct device *dev, unsigned int vdd) +static int poodle_mci_setpower(struct device *dev, unsigned int vdd) { struct pxamci_platform_data* p_d = dev->platform_data; @@ -270,6 +270,8 @@ static void poodle_mci_setpower(struct device *dev, unsigned int vdd) gpio_set_value(POODLE_GPIO_SD_PWR1, 0); gpio_set_value(POODLE_GPIO_SD_PWR, 0); } + + return 0; } static void poodle_mci_exit(struct device *dev, void *data) diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c index 2125df0444e7..4c29173026e8 100644 --- a/arch/arm/mach-pxa/spitz.c +++ b/arch/arm/mach-pxa/spitz.c @@ -598,7 +598,7 @@ static inline void spitz_spi_init(void) {} * NOTE: The card detect interrupt isn't debounced so we delay it by 250ms to * give the card a chance to fully insert/eject. */ -static void spitz_mci_setpower(struct device *dev, unsigned int vdd) +static int spitz_mci_setpower(struct device *dev, unsigned int vdd) { struct pxamci_platform_data* p_d = dev->platform_data; @@ -606,6 +606,8 @@ static void spitz_mci_setpower(struct device *dev, unsigned int vdd) spitz_card_pwr_ctrl(SCOOP_CPR_SD_3V, SCOOP_CPR_SD_3V); else spitz_card_pwr_ctrl(SCOOP_CPR_SD_3V, 0x0); + + return 0; } static struct pxamci_platform_data spitz_mci_platform_data = { diff --git a/arch/arm/mach-pxa/stargate2.c b/arch/arm/mach-pxa/stargate2.c index 88fde43c948c..62aea3e835f3 100644 --- a/arch/arm/mach-pxa/stargate2.c +++ b/arch/arm/mach-pxa/stargate2.c @@ -734,9 +734,10 @@ static int stargate2_mci_init(struct device *dev, * * Very simple control. Either it is on or off and is controlled by * a gpio pin */ -static void stargate2_mci_setpower(struct device *dev, unsigned int vdd) +static int stargate2_mci_setpower(struct device *dev, unsigned int vdd) { gpio_set_value(SG2_SD_POWER_ENABLE, !!vdd); + return 0; } static void stargate2_mci_exit(struct device *dev, void *data) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 847b1996ce8e..2c5a91bb8ec3 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -128,7 +128,7 @@ static inline int pxamci_set_power(struct pxamci_host *host, !!on ^ host->pdata->gpio_power_invert); } if (!host->vcc && host->pdata && host->pdata->setpower) - host->pdata->setpower(mmc_dev(host->mmc), vdd); + return host->pdata->setpower(mmc_dev(host->mmc), vdd); return 0; } diff --git a/include/linux/platform_data/mmc-pxamci.h b/include/linux/platform_data/mmc-pxamci.h index 9eb515bb799d..1706b3597ce0 100644 --- a/include/linux/platform_data/mmc-pxamci.h +++ b/include/linux/platform_data/mmc-pxamci.h @@ -12,7 +12,7 @@ struct pxamci_platform_data { unsigned long detect_delay_ms; /* delay in millisecond before detecting cards after interrupt */ int (*init)(struct device *, irq_handler_t , void *); int (*get_ro)(struct device *); - void (*setpower)(struct device *, unsigned int); + int (*setpower)(struct device *, unsigned int); void (*exit)(struct device *, void *); int gpio_card_detect; /* gpio detecting card insertion */ int gpio_card_ro; /* gpio detecting read only toggle */ -- cgit v1.3-18-gd494