aabdedf4d2
This driver creates an abstraction for different components by casting function
pointers to slightly incompatible types for each one to get the correct
argument even when the caller does not know those types. This is a
bit unreliable and not allowed in combination with control flow integrity
(KCFI):
sound/pci/ctxfi/ctatc.c:115:25: error: cast from 'int (*)(struct hw *, struct src_mgr **)' to 'create_t' (aka 'int (*)(struct hw *, void **)') converts to incompatible function type [-Werror,-Wcast-function-type-strict]
115 | [SRC] = { .create = (create_t)src_mgr_create,
| ^~~~~~~~~~~~~~~~~~~~~~~~
sound/pci/ctxfi/ctatc.c:116:20: error: cast from 'int (*)(struct src_mgr *)' to 'destroy_t' (aka 'int (*)(void *)') converts to incompatible function type [-Werror,-Wcast-function-type-strict]
116 | .destroy = (destroy_t)src_mgr_destroy },
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
sound/pci/ctxfi/ctatc.c:117:27: error: cast from 'int (*)(struct hw *, struct srcimp_mgr **)' to 'create_t' (aka 'int (*)(struct hw *, void **)') converts to incompatible function type [-Werror,-Wcast-function-type-strict]
117 | [SRCIMP] = { .create = (create_t)srcimp_mgr_create,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
sound/pci/ctxfi/ctatc.c:118:20: error: cast from 'int (*)(struct srcimp_mgr *)' to 'destroy_t' (aka 'int (*)(void *)') converts to incompatible function type [-Werror,-Wcast-function-type-strict]
118 | .destroy = (destroy_t)srcimp_mgr_destroy },
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Change these to always pass void pointers and move the abstraction one level
down.
Fixes: 8cc7236148
("ALSA: SB X-Fi driver merge")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Link: https://lore.kernel.org/r/20240213101303.460008-1-arnd@kernel.org
Signed-off-by: Takashi Iwai <tiwai@suse.de>
487 lines
9.5 KiB
C
487 lines
9.5 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
|
*
|
|
* @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 void amixer_master(struct rsc *rsc)
|
|
{
|
|
rsc->conj = 0;
|
|
rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
|
|
}
|
|
|
|
static void amixer_next_conj(struct rsc *rsc)
|
|
{
|
|
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 const 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 const 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) {
|
|
dev_err(mgr->card->dev,
|
|
"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(struct hw *hw, void **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;
|
|
amixer_mgr->card = hw->card;
|
|
|
|
*ramixer_mgr = amixer_mgr;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
kfree(amixer_mgr);
|
|
return err;
|
|
}
|
|
|
|
int amixer_mgr_destroy(void *ptr)
|
|
{
|
|
struct amixer_mgr *amixer_mgr = ptr;
|
|
rsc_mgr_uninit(&amixer_mgr->mgr);
|
|
kfree(amixer_mgr);
|
|
return 0;
|
|
}
|
|
|
|
/* SUM resource management */
|
|
|
|
static void sum_master(struct rsc *rsc)
|
|
{
|
|
rsc->conj = 0;
|
|
rsc->idx = container_of(rsc, struct sum, rsc)->idx[0];
|
|
}
|
|
|
|
static void sum_next_conj(struct rsc *rsc)
|
|
{
|
|
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 const 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) {
|
|
dev_err(mgr->card->dev,
|
|
"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(struct hw *hw, void **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;
|
|
sum_mgr->card = hw->card;
|
|
|
|
*rsum_mgr = sum_mgr;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
kfree(sum_mgr);
|
|
return err;
|
|
}
|
|
|
|
int sum_mgr_destroy(void *ptr)
|
|
{
|
|
struct sum_mgr *sum_mgr = ptr;
|
|
rsc_mgr_uninit(&sum_mgr->mgr);
|
|
kfree(sum_mgr);
|
|
return 0;
|
|
}
|
|
|