#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_misc.h>
#include <dev/ofw/fdt.h>
#include <sys/audioio.h>
#include <dev/audio_if.h>
#include <arm64/dev/aplmca.h>
struct aplaudio_softc {
struct device sc_dev;
struct dai_device *sc_dai_cpu;
struct dai_device *sc_dai_codec[6];
};
void aplaudio_set_format(struct aplaudio_softc *, uint32_t,
uint32_t, uint32_t);
void aplaudio_set_tdm_slots(struct aplaudio_softc *);
int aplaudio_open(void *, int);
void aplaudio_close(void *);
int aplaudio_set_params(void *, int, int,
struct audio_params *, struct audio_params *);
void *aplaudio_allocm(void *, int, size_t, int, int);
void aplaudio_freem(void *, void *, int);
int aplaudio_set_port(void *, mixer_ctrl_t *);
int aplaudio_get_port(void *, mixer_ctrl_t *);
int aplaudio_query_devinfo(void *, mixer_devinfo_t *);
int aplaudio_round_blocksize(void *, int);
size_t aplaudio_round_buffersize(void *, int, size_t);
int aplaudio_trigger_output(void *, void *, void *, int,
void (*)(void *), void *, struct audio_params *);
int aplaudio_trigger_input(void *, void *, void *, int,
void (*)(void *), void *, struct audio_params *);
int aplaudio_halt_output(void *);
int aplaudio_halt_input(void *);
const struct audio_hw_if aplaudio_hw_if = {
.open = aplaudio_open,
.close = aplaudio_close,
.set_params = aplaudio_set_params,
.allocm = aplaudio_allocm,
.freem = aplaudio_freem,
.set_port = aplaudio_set_port,
.get_port = aplaudio_get_port,
.query_devinfo = aplaudio_query_devinfo,
.round_blocksize = aplaudio_round_blocksize,
.round_buffersize = aplaudio_round_buffersize,
.trigger_output = aplaudio_trigger_output,
.trigger_input = aplaudio_trigger_input,
.halt_output = aplaudio_halt_output,
.halt_input = aplaudio_halt_input,
};
int aplaudio_match(struct device *, void *, void *);
void aplaudio_attach(struct device *, struct device *, void *);
const struct cfattach aplaudio_ca = {
sizeof (struct aplaudio_softc), aplaudio_match, aplaudio_attach
};
struct cfdriver aplaudio_cd = {
NULL, "aplaudio", DV_DULL
};
int
aplaudio_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "apple,macaudio");
}
void
aplaudio_attach(struct device *parent, struct device *self, void *aux)
{
struct aplaudio_softc *sc = (struct aplaudio_softc *)self;
struct fdt_attach_args *faa = aux;
uint32_t fmt, pol, clk;
uint32_t node, cpu, codec;
uint32_t *dais;
int count = 0;
int i, ncells;
int len;
printf("\n");
for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
if (!OF_is_enabled(node))
continue;
cpu = OF_getnodebyname(node, "cpu");
if (cpu == 0)
continue;
sc->sc_dai_cpu = aplmca_alloc_cluster(cpu);
if (sc->sc_dai_cpu == NULL)
continue;
codec = OF_getnodebyname(node, "codec");
if (codec == 0)
continue;
len = OF_getproplen(codec, "sound-dai");
if (len < 0)
continue;
dais = malloc(len, M_TEMP, M_WAITOK);
OF_getpropintarray(codec, "sound-dai", dais, len);
ncells = len / sizeof(uint32_t);
ncells = MIN(ncells, nitems(sc->sc_dai_codec));
for (i = 0; i < ncells; i++) {
sc->sc_dai_codec[i] = dai_byphandle(dais[i]);
if (sc->sc_dai_codec[i] == NULL)
continue;
count++;
}
free(dais, M_TEMP, len);
if (count == 0)
continue;
if (count > 1)
aplaudio_set_tdm_slots(sc);
fmt = DAI_FORMAT_LJ;
pol = 0;
clk = DAI_CLOCK_CFM | DAI_CLOCK_CBM;
aplaudio_set_format(sc, fmt, pol, clk);
audio_attach_mi(&aplaudio_hw_if, sc, NULL, self);
return;
}
}
void
aplaudio_set_format(struct aplaudio_softc *sc, uint32_t fmt, uint32_t pol,
uint32_t clk)
{
struct dai_device *dai;
int i;
if (sc->sc_dai_cpu->dd_set_format)
sc->sc_dai_cpu->dd_set_format(sc->sc_dai_cpu->dd_cookie,
fmt, pol, clk);
for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
dai = sc->sc_dai_codec[i];
if (dai == NULL)
continue;
if (dai->dd_set_format)
dai->dd_set_format(dai->dd_cookie, fmt, pol, clk);
}
}
void
aplaudio_set_tdm_slots(struct aplaudio_softc *sc)
{
struct dai_device *dai;
int i;
for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
dai = sc->sc_dai_codec[i];
if (dai == NULL)
continue;
if (dai->dd_set_tdm_slot) {
char prefix[8];
int slot = 0;
if (OF_getprop(dai->dd_node, "sound-name-prefix",
prefix, sizeof(prefix)) > 0) {
if (strncmp(prefix, "Right", 5) == 0)
slot = 1;
}
dai->dd_set_tdm_slot(dai->dd_cookie, slot);
}
}
}
int
aplaudio_open(void *cookie, int flags)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai;
const struct audio_hw_if *hwif;
int error;
int i;
dai = sc->sc_dai_cpu;
hwif = dai->dd_hw_if;
if (hwif->open) {
error = hwif->open(dai->dd_cookie, flags);
if (error) {
aplaudio_close(cookie);
return error;
}
}
for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
dai = sc->sc_dai_codec[i];
if (dai == NULL)
continue;
hwif = dai->dd_hw_if;
if (hwif->open) {
error = hwif->open(dai->dd_cookie, flags);
if (error) {
aplaudio_close(cookie);
return error;
}
}
}
return 0;
}
void
aplaudio_close(void *cookie)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai;
const struct audio_hw_if *hwif;
int i;
for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
dai = sc->sc_dai_codec[i];
if (dai == NULL)
continue;
hwif = dai->dd_hw_if;
if (hwif->close)
hwif->close(dai->dd_cookie);
}
dai = sc->sc_dai_cpu;
hwif = dai->dd_hw_if;
if (hwif->close)
hwif->close(dai->dd_cookie);
}
int
aplaudio_set_params(void *cookie, int setmode, int usemode,
struct audio_params *play, struct audio_params *rec)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai;
const struct audio_hw_if *hwif;
uint32_t rate;
int error;
int i;
dai = sc->sc_dai_cpu;
hwif = dai->dd_hw_if;
if (hwif->set_params) {
error = hwif->set_params(dai->dd_cookie,
setmode, usemode, play, rec);
if (error)
return error;
}
if (setmode & AUMODE_PLAY)
rate = play->sample_rate * play->channels * play->bps * 8;
else
rate = rec->sample_rate * rec->channels * rec->bps * 8;
for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
dai = sc->sc_dai_codec[i];
if (dai == NULL)
continue;
if (dai->dd_set_sysclk) {
error = dai->dd_set_sysclk(dai->dd_cookie, rate);
if (error)
return error;
}
}
dai = sc->sc_dai_cpu;
if (dai->dd_set_sysclk) {
error = dai->dd_set_sysclk(dai->dd_cookie, rate);
if (error)
return error;
}
return 0;
}
void *
aplaudio_allocm(void *cookie, int direction, size_t size, int type,
int flags)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai = sc->sc_dai_cpu;
const struct audio_hw_if *hwif = dai->dd_hw_if;
if (hwif->allocm)
return hwif->allocm(dai->dd_cookie,
direction, size, type, flags);
return NULL;
}
void
aplaudio_freem(void *cookie, void *addr, int type)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai = sc->sc_dai_cpu;
const struct audio_hw_if *hwif = dai->dd_hw_if;
if (hwif->freem)
hwif->freem(dai->dd_cookie, addr, type);
}
int
aplaudio_set_port(void *cookie, mixer_ctrl_t *cp)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai;
const struct audio_hw_if *hwif;
int error = ENXIO;
int i;
for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
dai = sc->sc_dai_codec[i];
if (dai == NULL)
continue;
hwif = dai->dd_hw_if;
if (hwif->set_port)
error = hwif->set_port(dai->dd_cookie, cp);
}
return error;
}
int
aplaudio_get_port(void *cookie, mixer_ctrl_t *cp)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai;
const struct audio_hw_if *hwif;
int error = ENXIO;
int i;
for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
dai = sc->sc_dai_codec[i];
if (dai == NULL)
continue;
hwif = dai->dd_hw_if;
if (hwif->get_port)
error = hwif->get_port(dai->dd_cookie, cp);
}
return error;
}
int
aplaudio_query_devinfo(void *cookie, mixer_devinfo_t *dip)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai;
const struct audio_hw_if *hwif;
int i;
for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
dai = sc->sc_dai_codec[i];
if (dai == NULL)
continue;
hwif = dai->dd_hw_if;
if (hwif->query_devinfo)
return hwif->query_devinfo(dai->dd_cookie, dip);
}
return ENXIO;
}
int
aplaudio_round_blocksize(void *cookie, int block)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai = sc->sc_dai_cpu;
const struct audio_hw_if *hwif = dai->dd_hw_if;
if (hwif->round_blocksize)
return hwif->round_blocksize(dai->dd_cookie, block);
return block;
}
size_t
aplaudio_round_buffersize(void *cookie, int direction, size_t bufsize)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai = sc->sc_dai_cpu;
const struct audio_hw_if *hwif = dai->dd_hw_if;
if (hwif->round_buffersize)
return hwif->round_buffersize(dai->dd_cookie,
direction, bufsize);
return bufsize;
}
int
aplaudio_trigger_output(void *cookie, void *start, void *end, int blksize,
void (*intr)(void *), void *arg, struct audio_params *param)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai;
const struct audio_hw_if *hwif;
int error;
int i;
for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
dai = sc->sc_dai_codec[i];
if (dai == NULL)
continue;
hwif = dai->dd_hw_if;
if (hwif->trigger_output) {
error = hwif->trigger_output(dai->dd_cookie,
start, end, blksize, intr, arg, param);
if (error) {
aplaudio_halt_output(cookie);
return error;
}
}
}
dai = sc->sc_dai_cpu;
hwif = dai->dd_hw_if;
if (hwif->trigger_output) {
error = hwif->trigger_output(dai->dd_cookie,
start, end, blksize, intr, arg, param);
if (error) {
aplaudio_halt_output(cookie);
return error;
}
}
return 0;
}
int
aplaudio_trigger_input(void *cookie, void *start, void *end, int blksize,
void (*intr)(void *), void *arg, struct audio_params *param)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai;
const struct audio_hw_if *hwif;
int error;
int i;
for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
dai = sc->sc_dai_codec[i];
if (dai == NULL)
continue;
hwif = dai->dd_hw_if;
if (hwif->trigger_input) {
error = hwif->trigger_input(dai->dd_cookie,
start, end, blksize, intr, arg, param);
if (error) {
aplaudio_halt_input(cookie);
return error;
}
}
}
dai = sc->sc_dai_cpu;
hwif = dai->dd_hw_if;
if (hwif->trigger_input) {
error = hwif->trigger_input(dai->dd_cookie,
start, end, blksize, intr, arg, param);
if (error) {
aplaudio_halt_input(cookie);
return error;
}
}
return 0;
}
int
aplaudio_halt_output(void *cookie)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai;
const struct audio_hw_if *hwif;
int i;
for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
dai = sc->sc_dai_codec[i];
if (dai == NULL)
continue;
hwif = dai->dd_hw_if;
if (hwif->halt_output)
hwif->halt_output(dai->dd_cookie);
}
dai = sc->sc_dai_cpu;
hwif = dai->dd_hw_if;
if (hwif->halt_output)
hwif->halt_output(dai->dd_cookie);
return 0;
}
int
aplaudio_halt_input(void *cookie)
{
struct aplaudio_softc *sc = cookie;
struct dai_device *dai;
const struct audio_hw_if *hwif;
int i;
for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
dai = sc->sc_dai_codec[i];
if (dai == NULL)
continue;
hwif = dai->dd_hw_if;
if (hwif->halt_input)
hwif->halt_input(dai->dd_cookie);
}
dai = sc->sc_dai_cpu;
hwif = dai->dd_hw_if;
if (hwif->halt_input)
hwif->halt_input(dai->dd_cookie);
return 0;
}