|  | /** | 
|  | * 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	ctamixer.c | 
|  | * | 
|  | * @Brief | 
|  | * This file contains the implementation of the Audio Mixer | 
|  | * resource management object. | 
|  | * | 
|  | * @Author	Liu Chun | 
|  | * @Date 	May 21 2008 | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include "ctamixer.h" | 
|  | #include "cthardware.h" | 
|  | #include <linux/slab.h> | 
|  |  | 
|  | #define AMIXER_RESOURCE_NUM	256 | 
|  | #define SUM_RESOURCE_NUM	256 | 
|  |  | 
|  | #define AMIXER_Y_IMMEDIATE	1 | 
|  |  | 
|  | #define BLANK_SLOT		4094 | 
|  |  | 
|  | static int amixer_master(struct rsc *rsc) | 
|  | { | 
|  | rsc->conj = 0; | 
|  | return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0]; | 
|  | } | 
|  |  | 
|  | static int amixer_next_conj(struct rsc *rsc) | 
|  | { | 
|  | rsc->conj++; | 
|  | return container_of(rsc, struct amixer, rsc)->idx[rsc->conj]; | 
|  | } | 
|  |  | 
|  | static int amixer_index(const struct rsc *rsc) | 
|  | { | 
|  | return container_of(rsc, struct amixer, rsc)->idx[rsc->conj]; | 
|  | } | 
|  |  | 
|  | static int amixer_output_slot(const struct rsc *rsc) | 
|  | { | 
|  | return (amixer_index(rsc) << 4) + 0x4; | 
|  | } | 
|  |  | 
|  | static struct rsc_ops amixer_basic_rsc_ops = { | 
|  | .master		= amixer_master, | 
|  | .next_conj	= amixer_next_conj, | 
|  | .index		= amixer_index, | 
|  | .output_slot	= amixer_output_slot, | 
|  | }; | 
|  |  | 
|  | static int amixer_set_input(struct amixer *amixer, struct rsc *rsc) | 
|  | { | 
|  | struct hw *hw; | 
|  |  | 
|  | hw = amixer->rsc.hw; | 
|  | hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE); | 
|  | amixer->input = rsc; | 
|  | if (!rsc) | 
|  | hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT); | 
|  | else | 
|  | hw->amixer_set_x(amixer->rsc.ctrl_blk, | 
|  | rsc->ops->output_slot(rsc)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* y is a 14-bit immediate constant */ | 
|  | static int amixer_set_y(struct amixer *amixer, unsigned int y) | 
|  | { | 
|  | struct hw *hw; | 
|  |  | 
|  | hw = amixer->rsc.hw; | 
|  | hw->amixer_set_y(amixer->rsc.ctrl_blk, y); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv) | 
|  | { | 
|  | struct hw *hw; | 
|  |  | 
|  | hw = amixer->rsc.hw; | 
|  | hw->amixer_set_iv(amixer->rsc.ctrl_blk, iv); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int amixer_set_sum(struct amixer *amixer, struct sum *sum) | 
|  | { | 
|  | struct hw *hw; | 
|  |  | 
|  | hw = amixer->rsc.hw; | 
|  | amixer->sum = sum; | 
|  | if (!sum) { | 
|  | hw->amixer_set_se(amixer->rsc.ctrl_blk, 0); | 
|  | } else { | 
|  | hw->amixer_set_se(amixer->rsc.ctrl_blk, 1); | 
|  | hw->amixer_set_sadr(amixer->rsc.ctrl_blk, | 
|  | sum->rsc.ops->index(&sum->rsc)); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int amixer_commit_write(struct amixer *amixer) | 
|  | { | 
|  | struct hw *hw; | 
|  | unsigned int index; | 
|  | int i; | 
|  | struct rsc *input; | 
|  | struct sum *sum; | 
|  |  | 
|  | hw = amixer->rsc.hw; | 
|  | input = amixer->input; | 
|  | sum = amixer->sum; | 
|  |  | 
|  | /* Program master and conjugate resources */ | 
|  | amixer->rsc.ops->master(&amixer->rsc); | 
|  | if (input) | 
|  | input->ops->master(input); | 
|  |  | 
|  | if (sum) | 
|  | sum->rsc.ops->master(&sum->rsc); | 
|  |  | 
|  | for (i = 0; i < amixer->rsc.msr; i++) { | 
|  | hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk); | 
|  | if (input) { | 
|  | hw->amixer_set_x(amixer->rsc.ctrl_blk, | 
|  | input->ops->output_slot(input)); | 
|  | input->ops->next_conj(input); | 
|  | } | 
|  | if (sum) { | 
|  | hw->amixer_set_sadr(amixer->rsc.ctrl_blk, | 
|  | sum->rsc.ops->index(&sum->rsc)); | 
|  | sum->rsc.ops->next_conj(&sum->rsc); | 
|  | } | 
|  | index = amixer->rsc.ops->output_slot(&amixer->rsc); | 
|  | hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk); | 
|  | amixer->rsc.ops->next_conj(&amixer->rsc); | 
|  | } | 
|  | amixer->rsc.ops->master(&amixer->rsc); | 
|  | if (input) | 
|  | input->ops->master(input); | 
|  |  | 
|  | if (sum) | 
|  | sum->rsc.ops->master(&sum->rsc); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int amixer_commit_raw_write(struct amixer *amixer) | 
|  | { | 
|  | struct hw *hw; | 
|  | unsigned int index; | 
|  |  | 
|  | hw = amixer->rsc.hw; | 
|  | index = amixer->rsc.ops->output_slot(&amixer->rsc); | 
|  | hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int amixer_get_y(struct amixer *amixer) | 
|  | { | 
|  | struct hw *hw; | 
|  |  | 
|  | hw = amixer->rsc.hw; | 
|  | return hw->amixer_get_y(amixer->rsc.ctrl_blk); | 
|  | } | 
|  |  | 
|  | static int amixer_setup(struct amixer *amixer, struct rsc *input, | 
|  | unsigned int scale, struct sum *sum) | 
|  | { | 
|  | amixer_set_input(amixer, input); | 
|  | amixer_set_y(amixer, scale); | 
|  | amixer_set_sum(amixer, sum); | 
|  | amixer_commit_write(amixer); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct amixer_rsc_ops amixer_ops = { | 
|  | .set_input		= amixer_set_input, | 
|  | .set_invalid_squash	= amixer_set_invalid_squash, | 
|  | .set_scale		= amixer_set_y, | 
|  | .set_sum		= amixer_set_sum, | 
|  | .commit_write		= amixer_commit_write, | 
|  | .commit_raw_write	= amixer_commit_raw_write, | 
|  | .setup			= amixer_setup, | 
|  | .get_scale		= amixer_get_y, | 
|  | }; | 
|  |  | 
|  | static int amixer_rsc_init(struct amixer *amixer, | 
|  | const struct amixer_desc *desc, | 
|  | struct amixer_mgr *mgr) | 
|  | { | 
|  | int err; | 
|  |  | 
|  | err = rsc_init(&amixer->rsc, amixer->idx[0], | 
|  | AMIXER, desc->msr, mgr->mgr.hw); | 
|  | if (err) | 
|  | return err; | 
|  |  | 
|  | /* Set amixer specific operations */ | 
|  | amixer->rsc.ops = &amixer_basic_rsc_ops; | 
|  | amixer->ops = &amixer_ops; | 
|  | amixer->input = NULL; | 
|  | amixer->sum = NULL; | 
|  |  | 
|  | amixer_setup(amixer, NULL, 0, NULL); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int amixer_rsc_uninit(struct amixer *amixer) | 
|  | { | 
|  | amixer_setup(amixer, NULL, 0, NULL); | 
|  | rsc_uninit(&amixer->rsc); | 
|  | amixer->ops = NULL; | 
|  | amixer->input = NULL; | 
|  | amixer->sum = NULL; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int get_amixer_rsc(struct amixer_mgr *mgr, | 
|  | const struct amixer_desc *desc, | 
|  | struct amixer **ramixer) | 
|  | { | 
|  | int err, i; | 
|  | unsigned int idx; | 
|  | struct amixer *amixer; | 
|  | unsigned long flags; | 
|  |  | 
|  | *ramixer = NULL; | 
|  |  | 
|  | /* Allocate mem for amixer resource */ | 
|  | amixer = kzalloc(sizeof(*amixer), GFP_KERNEL); | 
|  | if (!amixer) | 
|  | return -ENOMEM; | 
|  |  | 
|  | /* Check whether there are sufficient | 
|  | * amixer resources to meet request. */ | 
|  | err = 0; | 
|  | spin_lock_irqsave(&mgr->mgr_lock, flags); | 
|  | for (i = 0; i < desc->msr; i++) { | 
|  | err = mgr_get_resource(&mgr->mgr, 1, &idx); | 
|  | if (err) | 
|  | break; | 
|  |  | 
|  | amixer->idx[i] = idx; | 
|  | } | 
|  | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | 
|  | if (err) { | 
|  | printk(KERN_ERR "ctxfi: Can't meet AMIXER resource request!\n"); | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | err = amixer_rsc_init(amixer, desc, mgr); | 
|  | if (err) | 
|  | goto error; | 
|  |  | 
|  | *ramixer = amixer; | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | error: | 
|  | spin_lock_irqsave(&mgr->mgr_lock, flags); | 
|  | for (i--; i >= 0; i--) | 
|  | mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]); | 
|  |  | 
|  | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | 
|  | kfree(amixer); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) | 
|  | { | 
|  | unsigned long flags; | 
|  | int i; | 
|  |  | 
|  | spin_lock_irqsave(&mgr->mgr_lock, flags); | 
|  | for (i = 0; i < amixer->rsc.msr; i++) | 
|  | mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]); | 
|  |  | 
|  | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | 
|  | amixer_rsc_uninit(amixer); | 
|  | kfree(amixer); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr) | 
|  | { | 
|  | int err; | 
|  | struct amixer_mgr *amixer_mgr; | 
|  |  | 
|  | *ramixer_mgr = NULL; | 
|  | amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL); | 
|  | if (!amixer_mgr) | 
|  | return -ENOMEM; | 
|  |  | 
|  | err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw); | 
|  | if (err) | 
|  | goto error; | 
|  |  | 
|  | spin_lock_init(&amixer_mgr->mgr_lock); | 
|  |  | 
|  | amixer_mgr->get_amixer = get_amixer_rsc; | 
|  | amixer_mgr->put_amixer = put_amixer_rsc; | 
|  |  | 
|  | *ramixer_mgr = amixer_mgr; | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | error: | 
|  | kfree(amixer_mgr); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr) | 
|  | { | 
|  | rsc_mgr_uninit(&amixer_mgr->mgr); | 
|  | kfree(amixer_mgr); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* SUM resource management */ | 
|  |  | 
|  | static int sum_master(struct rsc *rsc) | 
|  | { | 
|  | rsc->conj = 0; | 
|  | return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0]; | 
|  | } | 
|  |  | 
|  | static int sum_next_conj(struct rsc *rsc) | 
|  | { | 
|  | rsc->conj++; | 
|  | return container_of(rsc, struct sum, rsc)->idx[rsc->conj]; | 
|  | } | 
|  |  | 
|  | static int sum_index(const struct rsc *rsc) | 
|  | { | 
|  | return container_of(rsc, struct sum, rsc)->idx[rsc->conj]; | 
|  | } | 
|  |  | 
|  | static int sum_output_slot(const struct rsc *rsc) | 
|  | { | 
|  | return (sum_index(rsc) << 4) + 0xc; | 
|  | } | 
|  |  | 
|  | static struct rsc_ops sum_basic_rsc_ops = { | 
|  | .master		= sum_master, | 
|  | .next_conj	= sum_next_conj, | 
|  | .index		= sum_index, | 
|  | .output_slot	= sum_output_slot, | 
|  | }; | 
|  |  | 
|  | static int sum_rsc_init(struct sum *sum, | 
|  | const struct sum_desc *desc, | 
|  | struct sum_mgr *mgr) | 
|  | { | 
|  | int err; | 
|  |  | 
|  | err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw); | 
|  | if (err) | 
|  | return err; | 
|  |  | 
|  | sum->rsc.ops = &sum_basic_rsc_ops; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int sum_rsc_uninit(struct sum *sum) | 
|  | { | 
|  | rsc_uninit(&sum->rsc); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int get_sum_rsc(struct sum_mgr *mgr, | 
|  | const struct sum_desc *desc, | 
|  | struct sum **rsum) | 
|  | { | 
|  | int err, i; | 
|  | unsigned int idx; | 
|  | struct sum *sum; | 
|  | unsigned long flags; | 
|  |  | 
|  | *rsum = NULL; | 
|  |  | 
|  | /* Allocate mem for sum resource */ | 
|  | sum = kzalloc(sizeof(*sum), GFP_KERNEL); | 
|  | if (!sum) | 
|  | return -ENOMEM; | 
|  |  | 
|  | /* Check whether there are sufficient sum resources to meet request. */ | 
|  | err = 0; | 
|  | spin_lock_irqsave(&mgr->mgr_lock, flags); | 
|  | for (i = 0; i < desc->msr; i++) { | 
|  | err = mgr_get_resource(&mgr->mgr, 1, &idx); | 
|  | if (err) | 
|  | break; | 
|  |  | 
|  | sum->idx[i] = idx; | 
|  | } | 
|  | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | 
|  | if (err) { | 
|  | printk(KERN_ERR "ctxfi: Can't meet SUM resource request!\n"); | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | err = sum_rsc_init(sum, desc, mgr); | 
|  | if (err) | 
|  | goto error; | 
|  |  | 
|  | *rsum = sum; | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | error: | 
|  | spin_lock_irqsave(&mgr->mgr_lock, flags); | 
|  | for (i--; i >= 0; i--) | 
|  | mgr_put_resource(&mgr->mgr, 1, sum->idx[i]); | 
|  |  | 
|  | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | 
|  | kfree(sum); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) | 
|  | { | 
|  | unsigned long flags; | 
|  | int i; | 
|  |  | 
|  | spin_lock_irqsave(&mgr->mgr_lock, flags); | 
|  | for (i = 0; i < sum->rsc.msr; i++) | 
|  | mgr_put_resource(&mgr->mgr, 1, sum->idx[i]); | 
|  |  | 
|  | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | 
|  | sum_rsc_uninit(sum); | 
|  | kfree(sum); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr) | 
|  | { | 
|  | int err; | 
|  | struct sum_mgr *sum_mgr; | 
|  |  | 
|  | *rsum_mgr = NULL; | 
|  | sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL); | 
|  | if (!sum_mgr) | 
|  | return -ENOMEM; | 
|  |  | 
|  | err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw); | 
|  | if (err) | 
|  | goto error; | 
|  |  | 
|  | spin_lock_init(&sum_mgr->mgr_lock); | 
|  |  | 
|  | sum_mgr->get_sum = get_sum_rsc; | 
|  | sum_mgr->put_sum = put_sum_rsc; | 
|  |  | 
|  | *rsum_mgr = sum_mgr; | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | error: | 
|  | kfree(sum_mgr); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | int sum_mgr_destroy(struct sum_mgr *sum_mgr) | 
|  | { | 
|  | rsc_mgr_uninit(&sum_mgr->mgr); | 
|  | kfree(sum_mgr); | 
|  | return 0; | 
|  | } | 
|  |  |