123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827 |
- /* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 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.
- */
- /* Resource management for the SPS device driver. */
- #include <linux/types.h> /* u32 */
- #include <linux/kernel.h> /* pr_info() */
- #include <linux/mutex.h> /* mutex */
- #include <linux/list.h> /* list_head */
- #include <linux/slab.h> /* kzalloc() */
- #include <linux/memory.h> /* memset */
- #include <linux/interrupt.h>
- #include "spsi.h"
- #include "sps_core.h"
- /* Max BAM FIFO sizes */
- #define SPSRM_MAX_DESC_FIFO_SIZE 0xffff
- #define SPSRM_MAX_DATA_FIFO_SIZE 0xffff
- /* Connection control struct pointer */
- static struct sps_rm *sps_rm;
- /**
- * Initialize resource manager module
- */
- int sps_rm_init(struct sps_rm *rm, u32 options)
- {
- /* Set the resource manager state struct pointer */
- sps_rm = rm;
- /* Initialize the state struct */
- INIT_LIST_HEAD(&sps_rm->connections_q);
- mutex_init(&sps_rm->lock);
- return 0;
- }
- /**
- * Initialize client state context
- *
- */
- void sps_rm_config_init(struct sps_connect *connect)
- {
- memset(connect, SPSRM_CLEAR, sizeof(*connect));
- }
- /**
- * Remove reference to connection mapping
- *
- * This function removes a reference from a connection mapping struct.
- *
- * @map - pointer to connection mapping struct
- *
- */
- static void sps_rm_remove_ref(struct sps_connection *map)
- {
- /* Free this connection */
- map->refs--;
- if (map->refs <= 0) {
- if (map->client_src != NULL || map->client_dest != NULL)
- SPS_ERR("sps:Failed to allocate connection struct");
- list_del(&map->list);
- kfree(map);
- }
- }
- /**
- * Compare map to connect parameters
- *
- * This function compares client connect parameters to an allocated
- * connection mapping.
- *
- * @pipe - client context for SPS connection end point
- *
- * @return - true if match, false otherwise
- *
- */
- static int sps_rm_map_match(const struct sps_connect *cfg,
- const struct sps_connection *map)
- {
- if (cfg->source != map->src.dev ||
- cfg->destination != map->dest.dev)
- return false;
- if (cfg->src_pipe_index != SPSRM_CLEAR &&
- cfg->src_pipe_index != map->src.pipe_index)
- return false;
- if (cfg->dest_pipe_index != SPSRM_CLEAR &&
- cfg->dest_pipe_index != map->dest.pipe_index)
- return false;
- if (cfg->config != map->config)
- return false;
- if (cfg->desc.size != SPSRM_CLEAR) {
- if (cfg->desc.size != map->desc.size)
- return false;
- if (cfg->desc.phys_base != (SPSRM_CLEAR|SPSRM_ADDR_CLR) &&
- cfg->desc.base != (void *)(SPSRM_CLEAR|SPSRM_ADDR_CLR) &&
- (cfg->desc.phys_base != map->desc.phys_base ||
- cfg->desc.base != map->desc.base)) {
- return false;
- }
- }
- if (cfg->data.size != SPSRM_CLEAR) {
- if (cfg->data.size != map->data.size)
- return false;
- if (cfg->data.phys_base != (SPSRM_CLEAR|SPSRM_ADDR_CLR) &&
- cfg->data.base != (void *)(SPSRM_CLEAR|SPSRM_ADDR_CLR) &&
- (cfg->data.phys_base != map->data.phys_base ||
- cfg->data.base != map->data.base))
- return false;
- }
- return true;
- }
- /**
- * Find unconnected mapping
- *
- * This function finds an allocated a connection mapping.
- *
- * @pipe - client context for SPS connection end point
- *
- * @return - pointer to allocated connection mapping, or NULL if not found
- *
- */
- static struct sps_connection *find_unconnected(struct sps_pipe *pipe)
- {
- struct sps_connect *cfg = &pipe->connect;
- struct sps_connection *map;
- /* Has this connection already been allocated? */
- list_for_each_entry(map, &sps_rm->connections_q, list) {
- if (sps_rm_map_match(cfg, map))
- if ((cfg->mode == SPS_MODE_SRC
- && map->client_src == NULL)
- || (cfg->mode != SPS_MODE_SRC
- && map->client_dest == NULL))
- return map; /* Found */
- }
- return NULL; /* Not Found */
- }
- /**
- * Assign connection to client
- *
- * This function assigns a connection to a client.
- *
- * @pipe - client context for SPS connection end point
- *
- * @map - connection mapping
- *
- * @return 0 on success, negative value on error
- *
- */
- static int sps_rm_assign(struct sps_pipe *pipe,
- struct sps_connection *map)
- {
- struct sps_connect *cfg = &pipe->connect;
- /* Check ownership and BAM */
- if ((cfg->mode == SPS_MODE_SRC && map->client_src != NULL) ||
- (cfg->mode != SPS_MODE_SRC && map->client_dest != NULL)) {
- SPS_ERR("sps:The end point is already connected.\n");
- return SPS_ERROR;
- }
- /* Check whether this end point is a BAM (not memory) */
- if ((cfg->mode == SPS_MODE_SRC && map->src.bam == NULL) ||
- (cfg->mode != SPS_MODE_SRC && map->dest.bam == NULL)) {
- SPS_ERR("sps:The end point is empty.\n");
- return SPS_ERROR;
- }
- /* Record the connection assignment */
- if (cfg->mode == SPS_MODE_SRC) {
- map->client_src = pipe;
- pipe->bam = map->src.bam;
- pipe->pipe_index = map->src.pipe_index;
- if (pipe->connect.event_thresh != SPSRM_CLEAR)
- map->src.event_threshold = pipe->connect.event_thresh;
- if (pipe->connect.lock_group != SPSRM_CLEAR)
- map->src.lock_group = pipe->connect.lock_group;
- } else {
- map->client_dest = pipe;
- pipe->bam = map->dest.bam;
- pipe->pipe_index = map->dest.pipe_index;
- if (pipe->connect.event_thresh != SPSRM_CLEAR)
- map->dest.event_threshold =
- pipe->connect.event_thresh;
- if (pipe->connect.lock_group != SPSRM_CLEAR)
- map->dest.lock_group = pipe->connect.lock_group;
- }
- pipe->map = map;
- SPS_DBG("sps:sps_rm_assign.bam %pa.pipe_index=%d\n",
- BAM_ID(pipe->bam), pipe->pipe_index);
- /* Copy parameters to client connect state */
- pipe->connect.src_pipe_index = map->src.pipe_index;
- pipe->connect.dest_pipe_index = map->dest.pipe_index;
- pipe->connect.desc = map->desc;
- pipe->connect.data = map->data;
- pipe->client_state = SPS_STATE_ALLOCATE;
- return 0;
- }
- /**
- * Free connection mapping resources
- *
- * This function frees a connection mapping resources.
- *
- * @pipe - client context for SPS connection end point
- *
- */
- static void sps_rm_free_map_rsrc(struct sps_connection *map)
- {
- struct sps_bam *bam;
- if (map->client_src != NULL || map->client_dest != NULL)
- return;
- if (map->alloc_src_pipe != SPS_BAM_PIPE_INVALID) {
- bam = map->src.bam;
- sps_bam_pipe_free(bam, map->src.pipe_index);
- /* Is this a BAM-DMA pipe? */
- #ifdef CONFIG_SPS_SUPPORT_BAMDMA
- if ((bam->props.options & SPS_BAM_OPT_BAMDMA))
- /* Deallocate and free the BAM-DMA channel */
- sps_dma_pipe_free(bam, map->src.pipe_index);
- #endif
- map->alloc_src_pipe = SPS_BAM_PIPE_INVALID;
- map->src.pipe_index = SPS_BAM_PIPE_INVALID;
- }
- if (map->alloc_dest_pipe != SPS_BAM_PIPE_INVALID) {
- bam = map->dest.bam;
- sps_bam_pipe_free(bam, map->dest.pipe_index);
- /* Is this a BAM-DMA pipe? */
- #ifdef CONFIG_SPS_SUPPORT_BAMDMA
- if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
- /* Deallocate the BAM-DMA channel */
- sps_dma_pipe_free(bam, map->dest.pipe_index);
- }
- #endif
- map->alloc_dest_pipe = SPS_BAM_PIPE_INVALID;
- map->dest.pipe_index = SPS_BAM_PIPE_INVALID;
- }
- if (map->alloc_desc_base != SPS_ADDR_INVALID) {
- sps_mem_free_io(map->alloc_desc_base, map->desc.size);
- map->alloc_desc_base = SPS_ADDR_INVALID;
- map->desc.phys_base = SPS_ADDR_INVALID;
- }
- if (map->alloc_data_base != SPS_ADDR_INVALID) {
- sps_mem_free_io(map->alloc_data_base, map->data.size);
- map->alloc_data_base = SPS_ADDR_INVALID;
- map->data.phys_base = SPS_ADDR_INVALID;
- }
- }
- /**
- * Init connection mapping from client connect
- *
- * This function initializes a connection mapping from the client's
- * connect parameters.
- *
- * @map - connection mapping struct
- *
- * @cfg - client connect parameters
- *
- * @return - pointer to allocated connection mapping, or NULL on error
- *
- */
- static void sps_rm_init_map(struct sps_connection *map,
- const struct sps_connect *cfg)
- {
- /* Clear the connection mapping struct */
- memset(map, 0, sizeof(*map));
- map->desc.phys_base = SPS_ADDR_INVALID;
- map->data.phys_base = SPS_ADDR_INVALID;
- map->alloc_desc_base = SPS_ADDR_INVALID;
- map->alloc_data_base = SPS_ADDR_INVALID;
- map->alloc_src_pipe = SPS_BAM_PIPE_INVALID;
- map->alloc_dest_pipe = SPS_BAM_PIPE_INVALID;
- /* Copy client required parameters */
- map->src.dev = cfg->source;
- map->dest.dev = cfg->destination;
- map->desc.size = cfg->desc.size;
- map->data.size = cfg->data.size;
- map->config = cfg->config;
- /* Did client specify descriptor FIFO? */
- if (map->desc.size != SPSRM_CLEAR &&
- cfg->desc.phys_base != (SPSRM_CLEAR|SPSRM_ADDR_CLR) &&
- cfg->desc.base != (void *)(SPSRM_CLEAR|SPSRM_ADDR_CLR))
- map->desc = cfg->desc;
- /* Did client specify data FIFO? */
- if (map->data.size != SPSRM_CLEAR &&
- cfg->data.phys_base != (SPSRM_CLEAR|SPSRM_ADDR_CLR) &&
- cfg->data.base != (void *)(SPSRM_CLEAR|SPSRM_ADDR_CLR))
- map->data = cfg->data;
- /* Did client specify source pipe? */
- if (cfg->src_pipe_index != SPSRM_CLEAR)
- map->src.pipe_index = cfg->src_pipe_index;
- else
- map->src.pipe_index = SPS_BAM_PIPE_INVALID;
- /* Did client specify destination pipe? */
- if (cfg->dest_pipe_index != SPSRM_CLEAR)
- map->dest.pipe_index = cfg->dest_pipe_index;
- else
- map->dest.pipe_index = SPS_BAM_PIPE_INVALID;
- }
- /**
- * Create a new connection mapping
- *
- * This function creates a new connection mapping.
- *
- * @pipe - client context for SPS connection end point
- *
- * @return - pointer to allocated connection mapping, or NULL on error
- *
- */
- static struct sps_connection *sps_rm_create(struct sps_pipe *pipe)
- {
- struct sps_connection *map;
- struct sps_bam *bam;
- u32 desc_size;
- u32 data_size;
- enum sps_mode dir;
- int success = false;
- /* Allocate new connection */
- map = kzalloc(sizeof(*map), GFP_KERNEL);
- if (map == NULL) {
- SPS_ERR("sps:Failed to allocate connection struct");
- return NULL;
- }
- /* Initialize connection struct */
- sps_rm_init_map(map, &pipe->connect);
- dir = pipe->connect.mode;
- /* Use a do/while() loop to avoid a "goto" */
- success = false;
- /* Get BAMs */
- map->src.bam = sps_h2bam(map->src.dev);
- if (map->src.bam == NULL) {
- if (map->src.dev != SPS_DEV_HANDLE_MEM) {
- SPS_ERR("sps:Invalid BAM handle: %pa", &map->src.dev);
- goto exit_err;
- }
- map->src.pipe_index = SPS_BAM_PIPE_INVALID;
- }
- map->dest.bam = sps_h2bam(map->dest.dev);
- if (map->dest.bam == NULL) {
- if (map->dest.dev != SPS_DEV_HANDLE_MEM) {
- SPS_ERR("sps:Invalid BAM handle: %pa", &map->dest.dev);
- goto exit_err;
- }
- map->dest.pipe_index = SPS_BAM_PIPE_INVALID;
- }
- /* Check the BAM device for the pipe */
- if ((dir == SPS_MODE_SRC && map->src.bam == NULL) ||
- (dir != SPS_MODE_SRC && map->dest.bam == NULL)) {
- SPS_ERR("sps:Invalid BAM endpt: dir %d src %pa dest %pa",
- dir, &map->src.dev, &map->dest.dev);
- goto exit_err;
- }
- /* Allocate pipes and copy BAM parameters */
- if (map->src.bam != NULL) {
- /* Allocate the pipe */
- bam = map->src.bam;
- map->alloc_src_pipe = sps_bam_pipe_alloc(bam,
- map->src.pipe_index);
- if (map->alloc_src_pipe == SPS_BAM_PIPE_INVALID)
- goto exit_err;
- map->src.pipe_index = map->alloc_src_pipe;
- /* Is this a BAM-DMA pipe? */
- #ifdef CONFIG_SPS_SUPPORT_BAMDMA
- if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
- int rc;
- /* Allocate the BAM-DMA channel */
- rc = sps_dma_pipe_alloc(bam, map->src.pipe_index,
- SPS_MODE_SRC);
- if (rc) {
- SPS_ERR("sps:Failed to alloc BAM-DMA pipe: %d",
- map->src.pipe_index);
- goto exit_err;
- }
- }
- #endif
- map->src.bam_phys = bam->props.phys_addr;
- map->src.event_threshold = bam->props.event_threshold;
- }
- if (map->dest.bam != NULL) {
- /* Allocate the pipe */
- bam = map->dest.bam;
- map->alloc_dest_pipe = sps_bam_pipe_alloc(bam,
- map->dest.pipe_index);
- if (map->alloc_dest_pipe == SPS_BAM_PIPE_INVALID)
- goto exit_err;
- map->dest.pipe_index = map->alloc_dest_pipe;
- /* Is this a BAM-DMA pipe? */
- #ifdef CONFIG_SPS_SUPPORT_BAMDMA
- if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
- int rc;
- /* Allocate the BAM-DMA channel */
- rc = sps_dma_pipe_alloc(bam, map->dest.pipe_index,
- SPS_MODE_DEST);
- if (rc) {
- SPS_ERR("sps:Failed to alloc BAM-DMA pipe: %d",
- map->dest.pipe_index);
- goto exit_err;
- }
- }
- #endif
- map->dest.bam_phys = bam->props.phys_addr;
- map->dest.event_threshold =
- bam->props.event_threshold;
- }
- /* Get default FIFO sizes */
- desc_size = 0;
- data_size = 0;
- if (map->src.bam != NULL) {
- bam = map->src.bam;
- desc_size = bam->props.desc_size;
- data_size = bam->props.data_size;
- }
- if (map->dest.bam != NULL) {
- bam = map->dest.bam;
- if (bam->props.desc_size > desc_size)
- desc_size = bam->props.desc_size;
- if (bam->props.data_size > data_size)
- data_size = bam->props.data_size;
- }
- /* Set FIFO sizes */
- if (map->desc.size == SPSRM_CLEAR)
- map->desc.size = desc_size;
- if (map->src.bam != NULL && map->dest.bam != NULL) {
- /* BAM-to-BAM requires data FIFO */
- if (map->data.size == SPSRM_CLEAR)
- map->data.size = data_size;
- } else {
- map->data.size = 0;
- }
- if (map->desc.size > SPSRM_MAX_DESC_FIFO_SIZE) {
- SPS_ERR("sps:Invalid desc FIFO size: 0x%x", map->desc.size);
- goto exit_err;
- }
- if (map->src.bam != NULL && map->dest.bam != NULL &&
- map->data.size > SPSRM_MAX_DATA_FIFO_SIZE) {
- SPS_ERR("sps:Invalid data FIFO size: 0x%x", map->data.size);
- goto exit_err;
- }
- /* Allocate descriptor FIFO if necessary */
- if (map->desc.size && map->desc.phys_base == SPS_ADDR_INVALID) {
- map->alloc_desc_base = sps_mem_alloc_io(map->desc.size);
- if (map->alloc_desc_base == SPS_ADDR_INVALID) {
- SPS_ERR("sps:I/O memory allocation failure:0x%x",
- map->desc.size);
- goto exit_err;
- }
- map->desc.phys_base = map->alloc_desc_base;
- map->desc.base = spsi_get_mem_ptr(map->desc.phys_base);
- if (map->desc.base == NULL) {
- SPS_ERR("sps:Cannot get virt addr for I/O buffer:%pa",
- &map->desc.phys_base);
- goto exit_err;
- }
- }
- /* Allocate data FIFO if necessary */
- if (map->data.size && map->data.phys_base == SPS_ADDR_INVALID) {
- map->alloc_data_base = sps_mem_alloc_io(map->data.size);
- if (map->alloc_data_base == SPS_ADDR_INVALID) {
- SPS_ERR("sps:I/O memory allocation failure:0x%x",
- map->data.size);
- goto exit_err;
- }
- map->data.phys_base = map->alloc_data_base;
- map->data.base = spsi_get_mem_ptr(map->data.phys_base);
- if (map->data.base == NULL) {
- SPS_ERR("sps:Cannot get virt addr for I/O buffer:%pa",
- &map->data.phys_base);
- goto exit_err;
- }
- }
- /* Attempt to assign this connection to the client */
- if (sps_rm_assign(pipe, map)) {
- SPS_ERR("sps:failed to assign a connection to the client.\n");
- goto exit_err;
- }
- /* Initialization was successful */
- success = true;
- exit_err:
- /* If initialization failed, free resources */
- if (!success) {
- sps_rm_free_map_rsrc(map);
- kfree(map);
- return NULL;
- }
- return map;
- }
- /**
- * Free connection mapping
- *
- * This function frees a connection mapping.
- *
- * @pipe - client context for SPS connection end point
- *
- * @return 0 on success, negative value on error
- *
- */
- static int sps_rm_free(struct sps_pipe *pipe)
- {
- struct sps_connection *map = (void *)pipe->map;
- struct sps_connect *cfg = &pipe->connect;
- mutex_lock(&sps_rm->lock);
- /* Free this connection */
- if (cfg->mode == SPS_MODE_SRC)
- map->client_src = NULL;
- else
- map->client_dest = NULL;
- pipe->map = NULL;
- pipe->client_state = SPS_STATE_DISCONNECT;
- sps_rm_free_map_rsrc(map);
- sps_rm_remove_ref(map);
- mutex_unlock(&sps_rm->lock);
- return 0;
- }
- /**
- * Allocate an SPS connection end point
- *
- * This function allocates resources and initializes a BAM connection.
- *
- * @pipe - client context for SPS connection end point
- *
- * @return 0 on success, negative value on error
- *
- */
- static int sps_rm_alloc(struct sps_pipe *pipe)
- {
- struct sps_connection *map;
- int result = SPS_ERROR;
- if (pipe->connect.sps_reserved != SPSRM_CLEAR) {
- /*
- * Client did not call sps_get_config() to init
- * struct sps_connect, so only use legacy members.
- */
- unsigned long source = pipe->connect.source;
- unsigned long destination = pipe->connect.destination;
- enum sps_mode mode = pipe->connect.mode;
- u32 config = pipe->connect.config;
- memset(&pipe->connect, SPSRM_CLEAR,
- sizeof(pipe->connect));
- pipe->connect.source = source;
- pipe->connect.destination = destination;
- pipe->connect.mode = mode;
- pipe->connect.config = config;
- }
- if (pipe->connect.config == SPSRM_CLEAR)
- pipe->connect.config = SPS_CONFIG_DEFAULT;
- /*
- * If configuration is not default, then client is specifying a
- * connection mapping. Find a matching mapping, or fail.
- * If a match is found, the client's Connect struct will be updated
- * with all the mapping's values.
- */
- if (pipe->connect.config != SPS_CONFIG_DEFAULT) {
- if (sps_map_find(&pipe->connect)) {
- SPS_ERR("sps:Failed to find connection mapping");
- return SPS_ERROR;
- }
- }
- mutex_lock(&sps_rm->lock);
- /* Check client state */
- if (IS_SPS_STATE_OK(pipe)) {
- SPS_ERR("sps:Client connection already allocated");
- goto exit_err;
- }
- /* Are the connection resources already allocated? */
- map = find_unconnected(pipe);
- if (map != NULL) {
- /* Attempt to assign this connection to the client */
- if (sps_rm_assign(pipe, map))
- /* Assignment failed, so must allocate new */
- map = NULL;
- }
- /* Allocate a new connection if necessary */
- if (map == NULL) {
- map = sps_rm_create(pipe);
- if (map == NULL) {
- SPS_ERR("sps:Failed to allocate connection");
- goto exit_err;
- }
- list_add_tail(&map->list, &sps_rm->connections_q);
- }
- /* Add the connection to the allocated queue */
- map->refs++;
- /* Initialization was successful */
- result = 0;
- exit_err:
- mutex_unlock(&sps_rm->lock);
- if (result)
- return SPS_ERROR;
- return 0;
- }
- /**
- * Disconnect an SPS connection end point
- *
- * This function frees resources and de-initializes a BAM connection.
- *
- * @pipe - client context for SPS connection end point
- *
- * @return 0 on success, negative value on error
- *
- */
- static int sps_rm_disconnect(struct sps_pipe *pipe)
- {
- sps_rm_free(pipe);
- return 0;
- }
- /**
- * Process connection state change
- *
- * This function processes a connection state change.
- *
- * @pipe - pointer to client context
- *
- * @state - new state for connection
- *
- * @return 0 on success, negative value on error
- *
- */
- int sps_rm_state_change(struct sps_pipe *pipe, u32 state)
- {
- int auto_enable = false;
- int result;
- /* Allocate the pipe */
- if (pipe->client_state == SPS_STATE_DISCONNECT &&
- state == SPS_STATE_ALLOCATE) {
- if (sps_rm_alloc(pipe)) {
- SPS_ERR(
- "sps:Fail to allocate resource for"
- " BAM 0x%p pipe %d.\n",
- pipe->bam, pipe->pipe_index);
- return SPS_ERROR;
- }
- }
- /* Configure the pipe */
- if (pipe->client_state == SPS_STATE_ALLOCATE &&
- state == SPS_STATE_CONNECT) {
- /* Connect the BAM pipe */
- struct sps_bam_connect_param params;
- memset(¶ms, 0, sizeof(params));
- params.mode = pipe->connect.mode;
- if (pipe->connect.options != SPSRM_CLEAR) {
- params.options = pipe->connect.options;
- params.irq_gen_addr = pipe->connect.irq_gen_addr;
- params.irq_gen_data = pipe->connect.irq_gen_data;
- }
- result = sps_bam_pipe_connect(pipe, ¶ms);
- if (result) {
- SPS_ERR("sps:Failed to connect BAM 0x%p pipe %d",
- pipe->bam, pipe->pipe_index);
- return SPS_ERROR;
- }
- pipe->client_state = SPS_STATE_CONNECT;
- /* Set auto-enable for system-mode connections */
- if (pipe->connect.source == SPS_DEV_HANDLE_MEM ||
- pipe->connect.destination == SPS_DEV_HANDLE_MEM) {
- if (pipe->map->desc.size != 0 &&
- pipe->map->desc.phys_base != SPS_ADDR_INVALID)
- auto_enable = true;
- }
- }
- /* Enable the pipe data flow */
- if (pipe->client_state == SPS_STATE_CONNECT &&
- !(state == SPS_STATE_DISABLE
- || state == SPS_STATE_DISCONNECT)
- && (state == SPS_STATE_ENABLE || auto_enable
- || (pipe->connect.options & SPS_O_AUTO_ENABLE))) {
- result = sps_bam_pipe_enable(pipe->bam, pipe->pipe_index);
- if (result) {
- SPS_ERR("sps:Failed to set BAM %pa pipe %d flow on",
- &pipe->bam->props.phys_addr,
- pipe->pipe_index);
- return SPS_ERROR;
- }
- /* Is this a BAM-DMA pipe? */
- #ifdef CONFIG_SPS_SUPPORT_BAMDMA
- if ((pipe->bam->props.options & SPS_BAM_OPT_BAMDMA)) {
- /* Activate the BAM-DMA channel */
- result = sps_dma_pipe_enable(pipe->bam,
- pipe->pipe_index);
- if (result) {
- SPS_ERR("sps:Failed to activate BAM-DMA"
- " pipe: %d", pipe->pipe_index);
- return SPS_ERROR;
- }
- }
- #endif
- pipe->client_state = SPS_STATE_ENABLE;
- }
- /* Disable the pipe data flow */
- if (pipe->client_state == SPS_STATE_ENABLE &&
- (state == SPS_STATE_DISABLE || state == SPS_STATE_DISCONNECT)) {
- result = sps_bam_pipe_disable(pipe->bam, pipe->pipe_index);
- if (result) {
- SPS_ERR("sps:Failed to set BAM %pa pipe %d flow off",
- &pipe->bam->props.phys_addr,
- pipe->pipe_index);
- return SPS_ERROR;
- }
- pipe->client_state = SPS_STATE_CONNECT;
- }
- /* Disconnect the BAM pipe */
- if (pipe->client_state == SPS_STATE_CONNECT &&
- state == SPS_STATE_DISCONNECT) {
- struct sps_connection *map;
- struct sps_bam *bam = pipe->bam;
- unsigned long flags;
- u32 pipe_index;
- if (pipe->connect.mode == SPS_MODE_SRC)
- pipe_index = pipe->map->src.pipe_index;
- else
- pipe_index = pipe->map->dest.pipe_index;
- if (bam->props.irq > 0)
- synchronize_irq(bam->props.irq);
- spin_lock_irqsave(&bam->isr_lock, flags);
- pipe->disconnecting = true;
- spin_unlock_irqrestore(&bam->isr_lock, flags);
- result = sps_bam_pipe_disconnect(pipe->bam, pipe_index);
- if (result) {
- SPS_ERR("sps:Failed to disconnect BAM %pa pipe %d",
- &pipe->bam->props.phys_addr,
- pipe->pipe_index);
- return SPS_ERROR;
- }
- /* Clear map state */
- map = (void *)pipe->map;
- if (pipe->connect.mode == SPS_MODE_SRC)
- map->client_src = NULL;
- else if (pipe->connect.mode == SPS_MODE_DEST)
- map->client_dest = NULL;
- sps_rm_disconnect(pipe);
- /* Clear the client state */
- pipe->map = NULL;
- pipe->bam = NULL;
- pipe->client_state = SPS_STATE_DISCONNECT;
- }
- return 0;
- }
|