| /** | 
 |  * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | 
 |  * | 
 |  * This source file is released under GPL v2 license (no other versions). | 
 |  * See the COPYING file included in the main directory of this source | 
 |  * distribution for the license terms and conditions. | 
 |  * | 
 |  * @File	ctresource.c | 
 |  * | 
 |  * @Brief | 
 |  * This file contains the implementation of some generic helper functions. | 
 |  * | 
 |  * @Author	Liu Chun | 
 |  * @Date 	May 15 2008 | 
 |  * | 
 |  */ | 
 |  | 
 | #include "ctresource.h" | 
 | #include "cthardware.h" | 
 | #include <linux/err.h> | 
 | #include <linux/slab.h> | 
 |  | 
 | #define AUDIO_SLOT_BLOCK_NUM 	256 | 
 |  | 
 | /* Resource allocation based on bit-map management mechanism */ | 
 | static int | 
 | get_resource(u8 *rscs, unsigned int amount, | 
 | 	     unsigned int multi, unsigned int *ridx) | 
 | { | 
 | 	int i, j, k, n; | 
 |  | 
 | 	/* Check whether there are sufficient resources to meet request. */ | 
 | 	for (i = 0, n = multi; i < amount; i++) { | 
 | 		j = i / 8; | 
 | 		k = i % 8; | 
 | 		if (rscs[j] & ((u8)1 << k)) { | 
 | 			n = multi; | 
 | 			continue; | 
 | 		} | 
 | 		if (!(--n)) | 
 | 			break; /* found sufficient contiguous resources */ | 
 | 	} | 
 |  | 
 | 	if (i >= amount) { | 
 | 		/* Can not find sufficient contiguous resources */ | 
 | 		return -ENOENT; | 
 | 	} | 
 |  | 
 | 	/* Mark the contiguous bits in resource bit-map as used */ | 
 | 	for (n = multi; n > 0; n--) { | 
 | 		j = i / 8; | 
 | 		k = i % 8; | 
 | 		rscs[j] |= ((u8)1 << k); | 
 | 		i--; | 
 | 	} | 
 |  | 
 | 	*ridx = i + 1; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int put_resource(u8 *rscs, unsigned int multi, unsigned int idx) | 
 | { | 
 | 	unsigned int i, j, k, n; | 
 |  | 
 | 	/* Mark the contiguous bits in resource bit-map as used */ | 
 | 	for (n = multi, i = idx; n > 0; n--) { | 
 | 		j = i / 8; | 
 | 		k = i % 8; | 
 | 		rscs[j] &= ~((u8)1 << k); | 
 | 		i++; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx) | 
 | { | 
 | 	int err; | 
 |  | 
 | 	if (n > mgr->avail) | 
 | 		return -ENOENT; | 
 |  | 
 | 	err = get_resource(mgr->rscs, mgr->amount, n, ridx); | 
 | 	if (!err) | 
 | 		mgr->avail -= n; | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx) | 
 | { | 
 | 	put_resource(mgr->rscs, n, idx); | 
 | 	mgr->avail += n; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static unsigned char offset_in_audio_slot_block[NUM_RSCTYP] = { | 
 | 	/* SRC channel is at Audio Ring slot 1 every 16 slots. */ | 
 | 	[SRC]		= 0x1, | 
 | 	[AMIXER]	= 0x4, | 
 | 	[SUM]		= 0xc, | 
 | }; | 
 |  | 
 | static int rsc_index(const struct rsc *rsc) | 
 | { | 
 |     return rsc->conj; | 
 | } | 
 |  | 
 | static int audio_ring_slot(const struct rsc *rsc) | 
 | { | 
 |     return (rsc->conj << 4) + offset_in_audio_slot_block[rsc->type]; | 
 | } | 
 |  | 
 | static int rsc_next_conj(struct rsc *rsc) | 
 | { | 
 | 	unsigned int i; | 
 | 	for (i = 0; (i < 8) && (!(rsc->msr & (0x1 << i))); ) | 
 | 		i++; | 
 | 	rsc->conj += (AUDIO_SLOT_BLOCK_NUM >> i); | 
 | 	return rsc->conj; | 
 | } | 
 |  | 
 | static int rsc_master(struct rsc *rsc) | 
 | { | 
 | 	return rsc->conj = rsc->idx; | 
 | } | 
 |  | 
 | static struct rsc_ops rsc_generic_ops = { | 
 | 	.index		= rsc_index, | 
 | 	.output_slot	= audio_ring_slot, | 
 | 	.master		= rsc_master, | 
 | 	.next_conj	= rsc_next_conj, | 
 | }; | 
 |  | 
 | int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw) | 
 | { | 
 | 	int err = 0; | 
 |  | 
 | 	rsc->idx = idx; | 
 | 	rsc->conj = idx; | 
 | 	rsc->type = type; | 
 | 	rsc->msr = msr; | 
 | 	rsc->hw = hw; | 
 | 	rsc->ops = &rsc_generic_ops; | 
 | 	if (!hw) { | 
 | 		rsc->ctrl_blk = NULL; | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	switch (type) { | 
 | 	case SRC: | 
 | 		err = ((struct hw *)hw)->src_rsc_get_ctrl_blk(&rsc->ctrl_blk); | 
 | 		break; | 
 | 	case AMIXER: | 
 | 		err = ((struct hw *)hw)-> | 
 | 				amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk); | 
 | 		break; | 
 | 	case SRCIMP: | 
 | 	case SUM: | 
 | 	case DAIO: | 
 | 		break; | 
 | 	default: | 
 | 		printk(KERN_ERR | 
 | 		       "ctxfi: Invalid resource type value %d!\n", type); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	if (err) { | 
 | 		printk(KERN_ERR | 
 | 		       "ctxfi: Failed to get resource control block!\n"); | 
 | 		return err; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int rsc_uninit(struct rsc *rsc) | 
 | { | 
 | 	if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) { | 
 | 		switch (rsc->type) { | 
 | 		case SRC: | 
 | 			((struct hw *)rsc->hw)-> | 
 | 				src_rsc_put_ctrl_blk(rsc->ctrl_blk); | 
 | 			break; | 
 | 		case AMIXER: | 
 | 			((struct hw *)rsc->hw)-> | 
 | 				amixer_rsc_put_ctrl_blk(rsc->ctrl_blk); | 
 | 			break; | 
 | 		case SUM: | 
 | 		case DAIO: | 
 | 			break; | 
 | 		default: | 
 | 			printk(KERN_ERR "ctxfi: " | 
 | 			       "Invalid resource type value %d!\n", rsc->type); | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		rsc->hw = rsc->ctrl_blk = NULL; | 
 | 	} | 
 |  | 
 | 	rsc->idx = rsc->conj = 0; | 
 | 	rsc->type = NUM_RSCTYP; | 
 | 	rsc->msr = 0; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, | 
 | 		 unsigned int amount, void *hw_obj) | 
 | { | 
 | 	int err = 0; | 
 | 	struct hw *hw = hw_obj; | 
 |  | 
 | 	mgr->type = NUM_RSCTYP; | 
 |  | 
 | 	mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL); | 
 | 	if (!mgr->rscs) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	switch (type) { | 
 | 	case SRC: | 
 | 		err = hw->src_mgr_get_ctrl_blk(&mgr->ctrl_blk); | 
 | 		break; | 
 | 	case SRCIMP: | 
 | 		err = hw->srcimp_mgr_get_ctrl_blk(&mgr->ctrl_blk); | 
 | 		break; | 
 | 	case AMIXER: | 
 | 		err = hw->amixer_mgr_get_ctrl_blk(&mgr->ctrl_blk); | 
 | 		break; | 
 | 	case DAIO: | 
 | 		err = hw->daio_mgr_get_ctrl_blk(hw, &mgr->ctrl_blk); | 
 | 		break; | 
 | 	case SUM: | 
 | 		break; | 
 | 	default: | 
 | 		printk(KERN_ERR | 
 | 		       "ctxfi: Invalid resource type value %d!\n", type); | 
 | 		err = -EINVAL; | 
 | 		goto error; | 
 | 	} | 
 |  | 
 | 	if (err) { | 
 | 		printk(KERN_ERR | 
 | 		       "ctxfi: Failed to get manager control block!\n"); | 
 | 		goto error; | 
 | 	} | 
 |  | 
 | 	mgr->type = type; | 
 | 	mgr->avail = mgr->amount = amount; | 
 | 	mgr->hw = hw; | 
 |  | 
 | 	return 0; | 
 |  | 
 | error: | 
 | 	kfree(mgr->rscs); | 
 | 	return err; | 
 | } | 
 |  | 
 | int rsc_mgr_uninit(struct rsc_mgr *mgr) | 
 | { | 
 | 	if (NULL != mgr->rscs) { | 
 | 		kfree(mgr->rscs); | 
 | 		mgr->rscs = NULL; | 
 | 	} | 
 |  | 
 | 	if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) { | 
 | 		switch (mgr->type) { | 
 | 		case SRC: | 
 | 			((struct hw *)mgr->hw)-> | 
 | 				src_mgr_put_ctrl_blk(mgr->ctrl_blk); | 
 | 			break; | 
 | 		case SRCIMP: | 
 | 			((struct hw *)mgr->hw)-> | 
 | 				srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk); | 
 | 			break; | 
 | 		case AMIXER: | 
 | 			((struct hw *)mgr->hw)-> | 
 | 				amixer_mgr_put_ctrl_blk(mgr->ctrl_blk); | 
 | 			break; | 
 | 		case DAIO: | 
 | 			((struct hw *)mgr->hw)-> | 
 | 				daio_mgr_put_ctrl_blk(mgr->ctrl_blk); | 
 | 			break; | 
 | 		case SUM: | 
 | 			break; | 
 | 		default: | 
 | 			printk(KERN_ERR "ctxfi: " | 
 | 			       "Invalid resource type value %d!\n", mgr->type); | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		mgr->hw = mgr->ctrl_blk = NULL; | 
 | 	} | 
 |  | 
 | 	mgr->type = NUM_RSCTYP; | 
 | 	mgr->avail = mgr->amount = 0; | 
 |  | 
 | 	return 0; | 
 | } |