audio: merge from mainline

PD#SWPL-15312

Change-Id: I827a1db0d6c948682812ea04a6db2c7b5757dbfb
Signed-off-by: Lei Qian <lei.qian@amlogic.com>
This commit is contained in:
Lei Qian
2019-10-28 11:28:15 +08:00
committed by Luke Go
parent 081b08ffbb
commit d14d77bafb
53 changed files with 5615 additions and 737 deletions

View File

@@ -142,6 +142,8 @@ source "drivers/amlogic/dolby_fw/Kconfig"
source "drivers/amlogic/ircut/Kconfig"
source "drivers/amlogic/hifi4dsp/Kconfig"
source "drivers/amlogic/pixel_probe/Kconfig"
source "drivers/amlogic/firmware/Kconfig"

View File

@@ -106,6 +106,8 @@ obj-$(CONFIG_AMLOGIC_BT_DEVICE) += bluetooth/
obj-$(CONFIG_AMLOGIC_WIFI) += wifi/
obj-$(CONFIG_AMLOGIC_HIFI4DSP) += hifi4dsp/
obj-$(CONFIG_AMLOGIC_POWER) += power/
obj-$(CONFIG_AMLOGIC_PCIE) += pci/

View File

@@ -0,0 +1,11 @@
# audio hifi4dsp configuration
#
menu "AMLOGIC HiFi4DSP process"
config AMLOGIC_HIFI4DSP
tristate "hifi4dsp control support"
default n
help
support the amlogic hifi4dsp;
endmenu

View File

@@ -0,0 +1,19 @@
# Makefile for tm2 hifi4dsp
hifi4dsp-objs = hifi4dsp_module.o \
hifi4dsp_firmware.o \
hifi4dsp_dsp.o \
hifi4dsp_ipc.o \
tm2_dsp_top.o
#audiodsp-objs += pcmenc_module.o pcmenc_stream.o
#audiodsp-objs += spdif_module.o
ifneq ($(KBUILD_SRC),)
TOP_KBUILD_SRC := $(KBUILD_SRC)/
endif
obj-$(CONFIG_AMLOGIC_HIFI4DSP) +=hifi4dsp.o
#EXTRA_CFLAGS = -DENABLE_WAIT_FORMAT

View File

@@ -0,0 +1,43 @@
/*
* drivers/amlogic/hifi4dsp/hifi4dsp_api.h
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#ifndef __AML_HIFI4DSP_API_H__
#define __AML_HIFI4DSP_API_H__
struct hifi4dsp_info_t {
char id; /*dsp_id 0,1,2...*/
char fw_id;
char fw_name[32]; /*name of firmware which used for dsp*/
long phy_addr; /*phy address of firmware wille be loaded on*/
};
#define HIFI4DSP_IOC_MAGIC 'H'
#define HIFI4DSP_LOAD _IOWR(HIFI4DSP_IOC_MAGIC, 1, struct hifi4dsp_info_t)
#define HIFI4DSP_RESET _IOWR(HIFI4DSP_IOC_MAGIC, 2, struct hifi4dsp_info_t)
#define HIFI4DSP_START _IOWR(HIFI4DSP_IOC_MAGIC, 3, struct hifi4dsp_info_t)
#define HIFI4DSP_STOP _IOWR(HIFI4DSP_IOC_MAGIC, 4, struct hifi4dsp_info_t)
#define HIFI4DSP_SLEEP _IOWR(HIFI4DSP_IOC_MAGIC, 5, struct hifi4dsp_info_t)
#define HIFI4DSP_WAKE _IOWR(HIFI4DSP_IOC_MAGIC, 6, struct hifi4dsp_info_t)
#define HIFI4DSP_GET_INFO _IOWR((HIFI4DSP_IOC_MAGIC), (18), \
struct hifi4dsp_info_t)
#define HIFI4DSP_TEST _IO(HIFI4DSP_IOC_MAGIC, 255)
#endif /*__AML_AUDIO_HIFI4DSP_API_H__ */

View File

@@ -0,0 +1,397 @@
/*
* drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#define DEBUG
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/firmware.h>
#include <linux/amlogic/major.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include "hifi4dsp_priv.h"
#include "hifi4dsp_firmware.h"
#include "hifi4dsp_dsp.h"
//static struct hifi4dsp_dsp *dsp_g;
/* Internal generic low-level hifi4dsp share memory write/read functions*/
void hifi4dsp_smem_write(void __iomem *addr, u32 offset, u32 value)
{
writel(value, addr + offset);
}
EXPORT_SYMBOL(hifi4dsp_smem_write);
u32 hifi4dsp_smem_read(void __iomem *addr, u32 offset)
{
return readl(addr + offset);
}
EXPORT_SYMBOL(hifi4dsp_smem_read);
void hifi4dsp_smem_write64(void __iomem *addr, u32 offset, u64 value)
{
memcpy_toio(addr + offset, &value, sizeof(value));
}
EXPORT_SYMBOL(hifi4dsp_smem_write64);
u64 hifi4dsp_smem_read64(void __iomem *addr, u32 offset)
{
u64 val;
memcpy_fromio(&val, addr + offset, sizeof(val));
return val;
}
EXPORT_SYMBOL(hifi4dsp_smem_read64);
static inline void _hifi4dsp_memcpy_toio_32(u32 __iomem *dest,
u32 *src, size_t bytes)
{
int i, words = bytes >> 2;
for (i = 0; i < words; i++)
writel(src[i], dest + i);
}
static inline void _hifi4dsp_memcpy_fromio_32(u32 *dest,
const __iomem u32 *src, size_t bytes)
{
int i, words = bytes >> 2;
for (i = 0; i < words; i++)
dest[i] = readl(src + i);
}
void hifi4dsp_memcpy_toio_32(struct hifi4dsp_dsp *dsp,
void __iomem *dest, void *src, size_t bytes)
{
_hifi4dsp_memcpy_toio_32(dest, src, bytes);
}
EXPORT_SYMBOL(hifi4dsp_memcpy_toio_32);
void hifi4dsp_memcpy_fromio_32(struct hifi4dsp_dsp *dsp, void *dest,
void __iomem *src, size_t bytes)
{
_hifi4dsp_memcpy_fromio_32(dest, src, bytes);
}
EXPORT_SYMBOL(hifi4dsp_memcpy_fromio_32);
/* Public API */
void hifi4dsp_dsp_smem_write(struct hifi4dsp_dsp *dsp, u32 offset, u32 value)
{
unsigned long flags;
spin_lock_irqsave(&dsp->spinlock, flags);
dsp->ops->write(dsp->addr.smem, offset, value);
spin_unlock_irqrestore(&dsp->spinlock, flags);
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_write);
u32 hifi4dsp_dsp_smem_read(struct hifi4dsp_dsp *dsp, u32 offset)
{
unsigned long flags;
u32 val;
spin_lock_irqsave(&dsp->spinlock, flags);
val = dsp->ops->read(dsp->addr.smem, offset);
spin_unlock_irqrestore(&dsp->spinlock, flags);
return val;
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_read);
void hifi4dsp_dsp_smem_write64(struct hifi4dsp_dsp *dsp, u32 offset, u64 value)
{
unsigned long flags;
spin_lock_irqsave(&dsp->spinlock, flags);
dsp->ops->write64(dsp->addr.smem, offset, value);
spin_unlock_irqrestore(&dsp->spinlock, flags);
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_write64);
u64 hifi4dsp_dsp_smem_read64(struct hifi4dsp_dsp *dsp, u32 offset)
{
unsigned long flags;
u64 val;
spin_lock_irqsave(&dsp->spinlock, flags);
val = dsp->ops->read64(dsp->addr.smem, offset);
spin_unlock_irqrestore(&dsp->spinlock, flags);
return val;
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_read64);
void hifi4dsp_dsp_smem_write_unlocked(struct hifi4dsp_dsp *dsp,
u32 offset, u32 value)
{
dsp->ops->write(dsp->addr.smem, offset, value);
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_write_unlocked);
u32 hifi4dsp_dsp_smem_read_unlocked(struct hifi4dsp_dsp *dsp,
u32 offset)
{
return dsp->ops->read(dsp->addr.smem, offset);
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_read_unlocked);
void hifi4dsp_dsp_smem_write64_unlocked(struct hifi4dsp_dsp *dsp,
u32 offset, u64 value)
{
dsp->ops->write64(dsp->addr.smem, offset, value);
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_write64_unlocked);
u64 hifi4dsp_dsp_smem_read64_unlocked(struct hifi4dsp_dsp *dsp,
u32 offset)
{
return dsp->ops->read64(dsp->addr.smem, offset);
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_read64_unlocked);
int hifi4dsp_dsp_smem_update_bits_unlocked(struct hifi4dsp_dsp *dsp,
u32 offset, u32 mask, u32 value)
{
bool change;
unsigned int old, new;
u32 ret;
ret = hifi4dsp_dsp_smem_read_unlocked(dsp, offset);
old = ret;
new = (old & (~mask)) | (value & mask);
change = (old != new);
if (change)
hifi4dsp_dsp_smem_write_unlocked(dsp, offset, new);
return change;
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits_unlocked);
void hifi4dsp_dsp_smem_update_bits_forced_unlocked(struct hifi4dsp_dsp *dsp,
u32 offset, u32 mask, u32 value)
{
unsigned int old, new;
u32 ret;
ret = hifi4dsp_dsp_smem_read_unlocked(dsp, offset);
old = ret;
new = (old & (~mask)) | (value & mask);
hifi4dsp_dsp_smem_write_unlocked(dsp, offset, new);
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits_forced_unlocked);
int hifi4dsp_dsp_smem_update_bits64_unlocked(struct hifi4dsp_dsp *dsp,
u32 offset, u64 mask, u64 value)
{
bool change;
u64 old, new;
old = hifi4dsp_dsp_smem_read64_unlocked(dsp, offset);
new = (old & (~mask)) | (value & mask);
change = (old != new);
if (change)
hifi4dsp_dsp_smem_write64_unlocked(dsp, offset, new);
return change;
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits64_unlocked);
int hifi4dsp_dsp_smem_update_bits(struct hifi4dsp_dsp *dsp, u32 offset,
u32 mask, u32 value)
{
unsigned long flags;
bool change;
spin_lock_irqsave(&dsp->spinlock, flags);
change = hifi4dsp_dsp_smem_update_bits_unlocked(dsp,
offset, mask, value);
spin_unlock_irqrestore(&dsp->spinlock, flags);
return change;
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits);
void hifi4dsp_dsp_smem_update_bits_forced(struct hifi4dsp_dsp *dsp,
u32 offset, u32 mask, u32 value)
{
unsigned long flags;
spin_lock_irqsave(&dsp->spinlock, flags);
hifi4dsp_dsp_smem_update_bits_forced_unlocked(dsp, offset, mask, value);
spin_unlock_irqrestore(&dsp->spinlock, flags);
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits_forced);
int hifi4dsp_dsp_smem_update_bits64(struct hifi4dsp_dsp *dsp,
u32 offset, u64 mask, u64 value)
{
unsigned long flags;
bool change;
spin_lock_irqsave(&dsp->spinlock, flags);
change = hifi4dsp_dsp_smem_update_bits64_unlocked(dsp,
offset, mask, value);
spin_unlock_irqrestore(&dsp->spinlock, flags);
return change;
}
EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits64);
int hifi4dsp_dsp_mailbox_init(struct hifi4dsp_dsp *dsp,
u32 outbox_offset, size_t outbox_size,
u32 inbox_offset, size_t inbox_size)
{
dsp->mailbox.out_base = dsp->addr.smem + outbox_offset;
dsp->mailbox.in_base = dsp->addr.smem + inbox_offset;
dsp->mailbox.out_size = outbox_size;
dsp->mailbox.in_size = inbox_size;
return 0;
}
EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_init);
void hifi4dsp_dsp_mailbox_outbox_write(struct hifi4dsp_dsp *dsp,
void *message, size_t bytes)
{
memcpy_toio(dsp->mailbox.out_base, message, bytes);
}
EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_outbox_write);
void hifi4dsp_dsp_mailbox_outbox_read(struct hifi4dsp_dsp *dsp,
void *message, size_t bytes)
{
memcpy_fromio(message, dsp->mailbox.out_base, bytes);
}
EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_outbox_read);
void hifi4dsp_dsp_mailbox_inbox_write(struct hifi4dsp_dsp *dsp,
void *message, size_t bytes)
{
memcpy_toio(dsp->mailbox.in_base, message, bytes);
}
EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_inbox_write);
void hifi4dsp_dsp_mailbox_inbox_read(struct hifi4dsp_dsp *dsp,
void *message, size_t bytes)
{
memcpy_fromio(message, dsp->mailbox.in_base, bytes);
}
EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_inbox_read);
int hifi4dsp_dsp_boot(struct hifi4dsp_dsp *dsp)
{
if (dsp->ops->boot)
dsp->ops->boot(dsp);
pr_debug("%s done\n", __func__);
return 0;
}
EXPORT_SYMBOL(hifi4dsp_dsp_boot);
void hifi4dsp_dsp_reset(struct hifi4dsp_dsp *dsp)
{
if (dsp->ops->reset)
dsp->ops->reset(dsp);
pr_debug("%s done\n", __func__);
}
EXPORT_SYMBOL(hifi4dsp_dsp_reset);
void hifi4dsp_dsp_sleep(struct hifi4dsp_dsp *dsp)
{
if (dsp->ops->sleep)
dsp->ops->sleep(dsp);
}
EXPORT_SYMBOL(hifi4dsp_dsp_sleep);
int hifi4dsp_dsp_wake(struct hifi4dsp_dsp *dsp)
{
if (dsp->ops->wake)
return dsp->ops->wake(dsp);
return 0;
}
EXPORT_SYMBOL(hifi4dsp_dsp_wake);
void hifi4dsp_dsp_dump(struct hifi4dsp_dsp *dsp)
{
if (dsp->ops->dump)
dsp->ops->dump(dsp);
}
EXPORT_SYMBOL(hifi4dsp_dsp_dump);
struct hifi4dsp_dsp *hifi4dsp_dsp_new(struct hifi4dsp_priv *priv,
struct hifi4dsp_pdata *pdata,
struct hifi4dsp_dsp_device *dsp_dev)
{
int err = 0;
struct hifi4dsp_dsp *dsp;
dsp = kzalloc(sizeof(struct hifi4dsp_dsp), GFP_KERNEL);
if (dsp == NULL)
goto dsp_malloc_error;
mutex_init(&dsp->mutex);
spin_lock_init(&dsp->spinlock);
spin_lock_init(&dsp->fw_spinlock);
INIT_LIST_HEAD(&dsp->fw_list);
dsp->id = pdata->id;
dsp->irq = pdata->irq;
dsp->major_id = MAJOR(priv->dev->devt);
dsp->dev = priv->dev;
dsp->pdata = pdata;
dsp->priv = priv;
dsp->ops = dsp_dev->ops;
/* Initialise Audio DSP */
if (dsp->ops->init) {
err = dsp->ops->init(dsp, pdata);
if (err < 0)
return NULL;
}
/*Register the ISR here if necessary*/
/*
* err = request_threaded_irq(dsp->irq, dsp->ops->irq_handler,
* dsp_dev->thread, IRQF_SHARED, "HIFI4DSP", dsp);
* if (err)
* goto irq_err;
*/
goto dsp_new_done;
/*
* irq_err:
* if (dsp->ops->free)
* dsp->ops->free(dsp);
*/
dsp_malloc_error:
dsp_new_done:
return dsp;
}
EXPORT_SYMBOL(hifi4dsp_dsp_new);
MODULE_AUTHOR("Shuyu Li");
MODULE_DESCRIPTION("HiFi DSP Driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,228 @@
/*
* drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#ifndef _HIFI4DSP_DSP_H
#define _HIFI4DSP_DSP_H
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/firmware.h>
#include <linux/irqreturn.h>
#include "hifi4dsp_firmware.h"
struct firmware;
struct hifi4dsp_pdata;
struct hifi4dsp_dsp;
/*
* DSP memory offsets and addresses.
*/
struct hifi4dsp_addr {
u32 smem_paddr;
u32 smem_size;
u32 reg_paddr;
u32 reg_size;
u32 fw_paddr;
u32 fw_size;
void __iomem *smem;
void __iomem *reg;
void __iomem *fw;
};
/*
* DSP Mailbox configuration.
*/
struct hifi4dsp_mailbox {
void __iomem *in_base;
void __iomem *out_base;
size_t in_size;
size_t out_size;
};
/*
* DSP Operations exported by platform Audio DSP driver.
*/
struct hifi4dsp_ops {
int (*boot)(struct hifi4dsp_dsp *);
int (*reset)(struct hifi4dsp_dsp *);
int (*wake)(struct hifi4dsp_dsp *);
int (*sleep)(struct hifi4dsp_dsp *);
/* Shim IO */
void (*write)(void __iomem *addr, u32 offset, u32 value);
u32 (*read)(void __iomem *addr, u32 offset);
/* DSP I/DRAM IO */
void (*ram_read)(struct hifi4dsp_dsp *dsp,
void *dest, void __iomem *src, size_t bytes);
void (*ram_write)(struct hifi4dsp_dsp *dsp,
void __iomem *dest, void *src, size_t bytes);
void (*write64)(void __iomem *addr, u32 offset, u64 value);
u64 (*read64)(void __iomem *addr, u32 offset);
void (*dump)(struct hifi4dsp_dsp *);
/* IRQ handlers */
irqreturn_t (*irq_handler)(int irq, void *context);
/* hifi4dsp init and free */
int (*init)(struct hifi4dsp_dsp *dsp, struct hifi4dsp_pdata *pdata);
void (*free)(struct hifi4dsp_dsp *dsp);
/* FW module parser/loader */
int (*parse_fw)(struct hifi4dsp_firmware *dsp_fw, void *pinfo);
};
/*
* hifi4dsp dsp device.
*/
struct hifi4dsp_dsp_device {
struct hifi4dsp_ops *ops;
irqreturn_t (*thread)(int irq, void *thread_context);
void *thread_context;
};
/*
* hifi4dsp Platform Data.
*/
struct hifi4dsp_pdata {
int id;
const char *name;
int irq;
unsigned int clk_freq;
phys_addr_t reg_paddr;
unsigned int reg_size;
void *reg;
/* Share memory */
phys_addr_t smem_paddr;
unsigned int smem_size;
/* Firmware */
char fw_name[32];
phys_addr_t fw_paddr; /*physical address of fw data*/
void *fw_buf;
int fw_size;
int fw_fmt;
int fw_max_size;
void *ops;
void *dsp; /*pointer to dsp*/
void *priv; /*pointer to priv*/
};
struct hifi4dsp_dsp {
u32 id;
int irq;
int freq;
/* runtime */
spinlock_t spinlock; /* used for IPC */
struct mutex mutex; /* used for fw */
struct device *dev;
struct device *dma_dev;
u32 major_id;
void *thread_context;
struct hifi4dsp_dsp_device *dsp_dev;
/* operations */
struct hifi4dsp_ops *ops;
/* base addresses */
struct hifi4dsp_addr addr;
/* mailbox */
struct hifi4dsp_mailbox mailbox;
/* platform data */
struct hifi4dsp_pdata *pdata;
/*fw support*/
struct hifi4dsp_firmware *dsp_fw;/*def fw*/
u32 fw_cnt;
spinlock_t fw_spinlock;
struct list_head fw_list;
u32 intr_status;
struct firmware *fw;
struct clk *dsp_clk;
struct clk *dsp_gate;
void *info;
void *priv;
};
/* Internal generic low-level hifi4dsp share memory write/read functions*/
extern void hifi4dsp_smem_write(void __iomem *addr, u32 offset, u32 value);
extern u32 hifi4dsp_smem_read(void __iomem *addr, u32 offset);
extern void hifi4dsp_smem_write64(void __iomem *addr, u32 offset, u64 value);
extern u64 hifi4dsp_smem_read64(void __iomem *addr, u32 offset);
extern void hifi4dsp_memcpy_toio_32(struct hifi4dsp_dsp *dsp,
void __iomem *dest, void *src, size_t bytes);
extern void hifi4dsp_memcpy_fromio_32(struct hifi4dsp_dsp *dsp,
void *dest, void __iomem *src, size_t bytes);
extern void hifi4dsp_dsp_smem_write(struct hifi4dsp_dsp *dsp,
u32 offset, u32 value);
extern u32 hifi4dsp_dsp_smem_read(struct hifi4dsp_dsp *dsp,
u32 offset);
extern void hifi4dsp_dsp_smem_write64(struct hifi4dsp_dsp *dsp,
u32 offset, u64 value);
extern u64 hifi4dsp_dsp_smem_read64(struct hifi4dsp_dsp *dsp,
u32 offset);
extern void hifi4dsp_dsp_smem_write_unlocked(struct hifi4dsp_dsp *dsp,
u32 offset, u32 value);
extern u32 hifi4dsp_dsp_smem_read_unlocked(struct hifi4dsp_dsp *dsp,
u32 offset);
extern void hifi4dsp_dsp_smem_write64_unlocked(struct hifi4dsp_dsp *dsp,
u32 offset, u64 value);
extern u64 hifi4dsp_dsp_smem_read64_unlocked(struct hifi4dsp_dsp *dsp,
u32 offset);
extern int hifi4dsp_dsp_smem_update_bits_unlocked(
struct hifi4dsp_dsp *dsp, u32 offset, u32 mask, u32 value);
extern void hifi4dsp_dsp_smem_update_bits_forced_unlocked(
struct hifi4dsp_dsp *dsp, u32 offset, u32 mask, u32 value);
extern int hifi4dsp_dsp_smem_update_bits64_unlocked(
struct hifi4dsp_dsp *dsp, u32 offset, u64 mask, u64 value);
extern int hifi4dsp_dsp_smem_update_bits(struct hifi4dsp_dsp *dsp,
u32 offset, u32 mask, u32 value);
extern void hifi4dsp_dsp_smem_update_bits_forced(struct hifi4dsp_dsp *dsp,
u32 offset, u32 mask, u32 value);
extern int hifi4dsp_dsp_smem_update_bits64(struct hifi4dsp_dsp *dsp,
u32 offset, u64 mask, u64 value);
extern int hifi4dsp_dsp_mailbox_init(struct hifi4dsp_dsp *dsp,
u32 outbox_offset, size_t outbox_size,
u32 inbox_offset, size_t inbox_size);
extern void hifi4dsp_dsp_mailbox_outbox_write(struct hifi4dsp_dsp *dsp,
void *message, size_t bytes);
extern void hifi4dsp_dsp_mailbox_outbox_read(struct hifi4dsp_dsp *dsp,
void *message, size_t bytes);
extern void hifi4dsp_dsp_mailbox_inbox_write(struct hifi4dsp_dsp *dsp,
void *message, size_t bytes);
extern void hifi4dsp_dsp_mailbox_inbox_read(struct hifi4dsp_dsp *dsp,
void *message, size_t bytes);
extern int hifi4dsp_dsp_boot(struct hifi4dsp_dsp *dsp);
extern void hifi4dsp_dsp_reset(struct hifi4dsp_dsp *dsp);
extern void hifi4dsp_dsp_sleep(struct hifi4dsp_dsp *dsp);
extern int hifi4dsp_dsp_wake(struct hifi4dsp_dsp *dsp);
extern void hifi4dsp_dsp_dump(struct hifi4dsp_dsp *dsp);
//extern struct hifi4dsp_dsp * hifi4dsp_dsp_new(struct hifi4dsp_priv *priv,
// struct hifi4dsp_pdata *pdata, struct hifi4dsp_ops *ops);
extern struct hifi4dsp_dsp *hifi4dsp_dsp_new(struct hifi4dsp_priv *priv,
struct hifi4dsp_pdata *pdata, struct hifi4dsp_dsp_device *dsp_dev);
#endif /*_HIFI4DSP_DSP_H*/

View File

@@ -0,0 +1,259 @@
/*
* drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/firmware.h>
#include <linux/amlogic/major.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include "hifi4dsp_priv.h"
#include "hifi4dsp_firmware.h"
#include "hifi4dsp_dsp.h"
static inline void hifi4dsp_fw_memcpy(void __iomem *dest,
void *src, u32 bytes)
{
memcpy_toio(dest, src, bytes);
}
/* general a new hifi4dsp_firmware object, called by hi-level functions */
struct hifi4dsp_firmware *hifi4dsp_fw_new(struct hifi4dsp_dsp *dsp,
const struct firmware *fw, void *private)
{
struct hifi4dsp_firmware *dsp_fw;
dsp_fw = kzalloc(sizeof(struct hifi4dsp_firmware), GFP_KERNEL);
if (dsp_fw == NULL)
goto dsp_fw_malloc_err;
dsp_fw->dsp = dsp;
dsp_fw->priv = dsp->priv;
if (private != NULL)
dsp_fw->private = private;
if (fw != NULL)
dsp_fw->size = fw->size;
pr_debug("%s done\n", __func__);
dsp_fw_malloc_err:
return dsp_fw;
}
/*dsp_fw must be initied before this operation,
*special the *dsp pointer must not be null
*/
int hifi4dsp_fw_add(struct hifi4dsp_firmware *dsp_fw)
{
unsigned long flags;
struct hifi4dsp_dsp *dsp;
if ((dsp_fw == NULL) || (dsp_fw->dsp == NULL))
return -1;
dsp = dsp_fw->dsp;
spin_lock_irqsave(&dsp->fw_spinlock, flags);
list_add_tail(&dsp_fw->list, &dsp->fw_list);
dsp->fw_cnt++;
dsp_fw->id = dsp->fw_cnt;
spin_unlock_irqrestore(&dsp->fw_spinlock, flags);
pr_debug("%s done\n", __func__);
return 0;
}
static struct hifi4dsp_firmware *hifi4dsp_fw_search_by_name(
struct hifi4dsp_dsp *dsp, char *name)
{
struct hifi4dsp_firmware *dsp_fw = NULL;
struct hifi4dsp_firmware *pfw = NULL;
unsigned long flags;
spin_lock_irqsave(&dsp->fw_spinlock, flags);
if (list_empty(&dsp->fw_list)) {
pfw = NULL;
pr_err("Firmware list is empty\n");
}
list_for_each_entry(dsp_fw, &dsp->fw_list, list) {
if (memcmp(dsp_fw->name, name, strlen(name)) == 0) {
pfw = dsp_fw;
break;
}
}
spin_unlock_irqrestore(&dsp->fw_spinlock, flags);
return pfw;
}
int hifi4dsp_dump_memory(const void *buf, unsigned int bytes, int col)
{
int i = 0, n = 0, size = 0;
const u8 *pdata;
char str[1024];
char a_str[24];
pdata = (u8 *)buf;
size = bytes;
memset(str, '\0', sizeof(a_str));
memset(str, '\0', sizeof(str));
while (n < size) {
sprintf(a_str, "%p: ", pdata);
strcat(str, a_str);
col = ((size-n) > col)?col:(size-n);
for (i = 0; i < col; i++) {
sprintf(a_str, "%02x ", *(pdata+i));
strcat(str, a_str);
}
pr_info("%s\n", str);
memset(str, '\0', sizeof(str));/*re-init buf*/
pdata += col;
n += col;
}
return 0;
}
//resource res;
int hifi4dsp_fw_copy_to_ddr(const struct firmware *fw,
struct hifi4dsp_firmware *dsp_fw)
{
int fw_bytes = 0;
const u8 *fw_src;
void *fw_dst;
fw_src = fw->data;
fw_bytes = fw->size;
fw_dst = dsp_fw->buf;
hifi4dsp_dump_memory(fw_src, 32, 16);
hifi4dsp_dump_memory(fw_src+fw_bytes-32, 32, 16);
pr_debug("%s fw_src:0x%p, pdata_dst=0x%p ,szie=%d bytes\n",
__func__, fw_src, fw_dst, fw_bytes);
//memcpy(fw_dst, fw_src, fw_bytes);
memcpy_toio(fw_dst, fw_src, fw_bytes);
//do memory barrier
//mb();
/*TODO, if need add membarrier code*/
hifi4dsp_dump_memory(fw_dst, 32, 16);
hifi4dsp_dump_memory(fw_dst+fw_bytes-32, 32, 16);
return 0;
}
int hifi4dsp_fw_load(struct hifi4dsp_firmware *dsp_fw)
{
const struct firmware *fw;
struct hifi4dsp_priv *priv;
struct hifi4dsp_dsp *dsp;
int err = 0;
pr_info("%s loading firmware %s\n", __func__, dsp_fw->name);
if ((dsp_fw == NULL) || (dsp_fw->dsp == NULL))
return -1;
priv = dsp_fw->priv;
dsp = dsp_fw->dsp;
err = request_firmware(&fw, dsp_fw->name, priv->dev);
if (err < 0) {
HIFI4DSP_PRNT("can't load the %s,err=%d\n", dsp_fw->name, err);
goto done;
}
if (fw == NULL) {
HIFI4DSP_PRNT("firmware pointer==NULL\n");
goto done;
}
if (dsp_fw == NULL) {
HIFI4DSP_PRNT("hifi4dsp_firmware pointer==NULL\n");
err = ENOMEM;
goto release;
}
dsp_fw->size = fw->size;
hifi4dsp_fw_copy_to_ddr(fw, dsp_fw);
release:
release_firmware(fw);
done:
return err;
}
int hifi4dsp_fw_reload(struct hifi4dsp_dsp *dsp,
struct hifi4dsp_firmware *dsp_fw)
{
int err = 0;
return err;
}
int hifi4dsp_fw_unload(struct hifi4dsp_firmware *dsp_fw)
{
int err = 0;
return err;
}
/* create a hifi4dsp_firmware object and
* add it to the fw_list of hifi4dsp_dsp
*/
struct hifi4dsp_firmware *hifi4dsp_fw_register(struct hifi4dsp_dsp *dsp,
char *name)
{
struct hifi4dsp_firmware *dsp_fw;
int str_len = 0;
dsp_fw = hifi4dsp_fw_search_by_name(dsp, name);
if (dsp_fw != NULL) {
pr_info("%s firmware( %s ) has been registered\n",
__func__, name);
return dsp_fw;
}
dsp_fw = hifi4dsp_fw_new(dsp, NULL, NULL);
if (dsp_fw != NULL) {
str_len = sizeof(dsp_fw->name) - 1;
strncpy(dsp_fw->name, name, str_len);
hifi4dsp_fw_add(dsp_fw);
pr_info("%s %s done\n", __func__, dsp_fw->name);
}
return dsp_fw;
}
/* free single firmware object */
void hifi4dsp_fw_free(struct hifi4dsp_firmware *dsp_fw)
{
struct hifi4dsp_dsp *dsp = dsp_fw->dsp;
mutex_lock(&dsp->mutex);
list_del(&dsp_fw->list);
kfree(dsp_fw);
mutex_unlock(&dsp->mutex);
}
/* free all firmware objects */
void hifi4dsp_fw_free_all(struct hifi4dsp_dsp *dsp)
{
struct hifi4dsp_firmware *dsp_fw, *t;
mutex_lock(&dsp->mutex);
list_for_each_entry_safe(dsp_fw, t, &dsp->fw_list, list) {
list_del(&dsp_fw->list);
kfree(dsp_fw);
}
mutex_unlock(&dsp->mutex);
}

View File

@@ -0,0 +1,58 @@
/*
* drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#ifndef _HIFI4DSP_FIRMWARE_H
#define _HIFI4DSP_FIRMWARE_H
struct firmware;
struct hifi4dsp_priv;
struct hifi4dsp_dsp;
struct hifi4dsp_firmware_block {
int id;
struct list_head list;
phys_addr_t paddr;
};
struct hifi4dsp_firmware {
int id;
char name[32];
int size;
int fmt;
struct list_head list; /*fw list of DSP*/
phys_addr_t paddr; /*physical address of fw data*/
void *buf; /*virtual address of fw data*/
struct hifi4dsp_priv *priv;
struct hifi4dsp_dsp *dsp;
void *private;
};
extern int hifi4dsp_fw_load(struct hifi4dsp_firmware *dsp_fw);
extern int hifi4dsp_fw_unload(struct hifi4dsp_firmware *dsp_fw);
extern void hifi4dsp_fw_free(struct hifi4dsp_firmware *dsp_fw);
extern int hifi4dsp_fw_add(struct hifi4dsp_firmware *dsp_fw);
extern void hifi4dsp_fw_free_all(struct hifi4dsp_dsp *dsp);
extern struct hifi4dsp_firmware *hifi4dsp_fw_new(struct hifi4dsp_dsp *dsp,
const struct firmware *fw, void *private);
extern struct hifi4dsp_firmware *hifi4dsp_fw_register(struct hifi4dsp_dsp *dsp,
char *fw_name);
extern int hifi4dsp_dump_memory(const void *buf, unsigned int bytes, int col);
#endif /*_HIFI4DSP_FIRMWARE_H*/

View File

@@ -0,0 +1,323 @@
/*
* drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/list.h>
#include <linux/wait.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/amlogic/major.h>
#include "hifi4dsp_priv.h"
#include "hifi4dsp_firmware.h"
#include "hifi4dsp_dsp.h"
#include "hifi4dsp_ipc.h"
/* locks held by caller */
static struct hifi4dsp_ipc_message *ipc_get_empty_msg(
struct hifi4dsp_ipc *ipc)
{
struct hifi4dsp_ipc_message *msg = NULL;
if (!list_empty(&ipc->empty_list)) {
msg = list_first_entry(&ipc->empty_list,
struct hifi4dsp_ipc_message, list);
list_del(&msg->list);
}
return msg;
}
static int tx_wait_done(struct hifi4dsp_ipc *ipc,
struct hifi4dsp_ipc_message *msg, void *rx_data)
{
unsigned long flags;
int ret;
/* wait for DSP completion (in all cases atm inc pending) */
ret = wait_event_timeout(msg->waitq, msg->complete,
msecs_to_jiffies(IPC_MSG_TIMEOUT_MSECS));
spin_lock_irqsave(&ipc->dsp->spinlock, flags);
if (ret == 0) {
if (ipc->ops.debug_info != NULL)
ipc->ops.debug_info(ipc, "message timeout");
list_del(&msg->list);
ret = -ETIMEDOUT;
} else {
/* copy the data returned from DSP */
if (msg->rx_size)
memcpy(rx_data, msg->rx_data, msg->rx_size);
ret = msg->errno;
}
list_add_tail(&msg->list, &ipc->empty_list);
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
return ret;
}
static int ipc_tx_message(struct hifi4dsp_ipc *ipc, u64 header,
void *tx_data, size_t tx_bytes, void *rx_data,
size_t rx_bytes, int wait)
{
struct hifi4dsp_ipc_message *msg;
unsigned long flags;
spin_lock_irqsave(&ipc->dsp->spinlock, flags);
msg = ipc_get_empty_msg(ipc);
if (msg == NULL) {
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
return -EBUSY;
}
msg->header = header;
msg->tx_size = tx_bytes;
msg->rx_size = rx_bytes;
msg->wait = wait;
msg->errno = 0;
msg->pending = false;
msg->complete = false;
if ((tx_bytes) && (ipc->ops.tx_data_copy != NULL))
ipc->ops.tx_data_copy(msg, tx_data, tx_bytes);
list_add_tail(&msg->list, &ipc->tx_list);
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
kthread_queue_work(&ipc->kworker, &ipc->kwork);
if (wait)
return tx_wait_done(ipc, msg, rx_data);
else
return 0;
}
static int ipc_msg_empty_list_init(struct hifi4dsp_ipc *ipc)
{
int i;
ipc->msg = kzalloc(sizeof(struct hifi4dsp_ipc_message) *
IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
if (ipc->msg == NULL)
return -ENOMEM;
for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
ipc->msg[i].tx_data = kzalloc
(ipc->tx_data_max_size, GFP_KERNEL);
if (ipc->msg[i].tx_data == NULL)
goto free_mem;
ipc->msg[i].rx_data = kzalloc
(ipc->rx_data_max_size, GFP_KERNEL);
if (ipc->msg[i].rx_data == NULL) {
kfree(ipc->msg[i].tx_data);
goto free_mem;
}
init_waitqueue_head(&ipc->msg[i].waitq);
list_add(&ipc->msg[i].list, &ipc->empty_list);
}
return 0;
free_mem:
while (i > 0) {
kfree(ipc->msg[i-1].tx_data);
kfree(ipc->msg[i-1].rx_data);
--i;
}
kfree(ipc->msg);
return -ENOMEM;
}
static void ipc_tx_msgs(struct kthread_work *work)
{
struct hifi4dsp_ipc *ipc =
container_of(work, struct hifi4dsp_ipc, kwork);
struct hifi4dsp_ipc_message *msg;
unsigned long flags;
spin_lock_irqsave(&ipc->dsp->spinlock, flags);
if (list_empty(&ipc->tx_list) || ipc->pending) {
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
return;
}
/* if the DSP is busy, we will TX messages after IRQ.
* also postpone if we are in the middle of procesing completion irq
*/
if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) {
pr_err("ipc_tx_msgs dsp busy\n");
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
return;
}
msg = list_first_entry(&ipc->tx_list,
struct hifi4dsp_ipc_message, list);
list_move(&msg->list, &ipc->rx_list);
if (ipc->ops.tx_msg != NULL)
ipc->ops.tx_msg(ipc, msg);
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
}
int hifi4dsp_ipc_tx_message_wait(struct hifi4dsp_ipc *ipc, u64 header,
void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes)
{
return ipc_tx_message(ipc, header, tx_data, tx_bytes,
rx_data, rx_bytes, 1);
}
EXPORT_SYMBOL_GPL(hifi4dsp_ipc_tx_message_wait);
int hifi4dsp_ipc_tx_message_nowait(struct hifi4dsp_ipc *ipc, u64 header,
void *tx_data, size_t tx_bytes)
{
return ipc_tx_message(ipc, header, tx_data, tx_bytes,
NULL, 0, 0);
}
EXPORT_SYMBOL_GPL(hifi4dsp_ipc_tx_message_nowait);
struct hifi4dsp_ipc_message *hifi4dsp_ipc_reply_find_msg(
struct hifi4dsp_ipc *ipc, u64 header)
{
struct hifi4dsp_ipc_message *msg;
u64 mask = 0;
if (ipc->ops.reply_msg_match != NULL)
header = ipc->ops.reply_msg_match(header, &mask);
if (list_empty(&ipc->rx_list)) {
pr_err("error: rx list empty but received 0x%llx\n",
header);
return NULL;
}
list_for_each_entry(msg, &ipc->rx_list, list) {
if ((msg->header & mask) == header)
return msg;
}
return NULL;
}
EXPORT_SYMBOL_GPL(hifi4dsp_ipc_reply_find_msg);
/* locks held by caller */
void hifi4dsp_ipc_tx_msg_reply_complete(struct hifi4dsp_ipc *ipc,
struct hifi4dsp_ipc_message *msg)
{
msg->complete = true;
if (!msg->wait)
list_add_tail(&msg->list, &ipc->empty_list);
else
wake_up(&msg->waitq);
}
EXPORT_SYMBOL_GPL(hifi4dsp_ipc_tx_msg_reply_complete);
void hifi4dsp_ipc_drop_all(struct hifi4dsp_ipc *ipc)
{
struct hifi4dsp_ipc_message *msg, *tmp;
unsigned long flags;
int tx_drop_cnt = 0, rx_drop_cnt = 0;
/* drop all TX and Rx messages before we stall + reset DSP */
spin_lock_irqsave(&ipc->dsp->spinlock, flags);
list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) {
list_move(&msg->list, &ipc->empty_list);
tx_drop_cnt++;
}
list_for_each_entry_safe(msg, tmp, &ipc->rx_list, list) {
list_move(&msg->list, &ipc->empty_list);
rx_drop_cnt++;
}
spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
if (tx_drop_cnt || rx_drop_cnt)
pr_err("dropped IPC tx_msg cnt %d, rx_msg=%d\n",
tx_drop_cnt, rx_drop_cnt);
}
EXPORT_SYMBOL_GPL(hifi4dsp_ipc_drop_all);
int hifi4dsp_ipc_init(struct hifi4dsp_ipc *ipc)
{
int ret;
INIT_LIST_HEAD(&ipc->tx_list);
INIT_LIST_HEAD(&ipc->rx_list);
INIT_LIST_HEAD(&ipc->empty_list);
init_waitqueue_head(&ipc->wait_txq);
ret = ipc_msg_empty_list_init(ipc);
if (ret < 0)
return -ENOMEM;
/* start the IPC message thread */
kthread_init_worker(&ipc->kworker);
ipc->tx_thread = kthread_run(kthread_worker_fn,
&ipc->kworker, "%s",
dev_name(ipc->dev));
if (IS_ERR(ipc->tx_thread)) {
pr_err("error: failed to create message TX task\n");
ret = PTR_ERR(ipc->tx_thread);
kfree(ipc->msg);
return ret;
}
kthread_init_work(&ipc->kwork, ipc_tx_msgs);
return 0;
}
EXPORT_SYMBOL_GPL(hifi4dsp_ipc_init);
void hifi4dsp_ipc_finish(struct hifi4dsp_ipc *ipc)
{
int i;
if (ipc->tx_thread)
kthread_stop(ipc->tx_thread);
if (ipc->msg) {
for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
kfree(ipc->msg[i].tx_data);
kfree(ipc->msg[i].rx_data);
}
kfree(ipc->msg);
}
}
EXPORT_SYMBOL_GPL(hifi4dsp_ipc_finish);

View File

@@ -0,0 +1,97 @@
/*
* drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#ifndef _HIFI4DSP_IPC_H
#define _HIFI4DSP_IPC_H
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include "hifi4dsp_dsp.h"
struct hifi4dsp_ipc;
#define IPC_MAILBOX_MAX_BYTES 256
#define IPC_EMPTY_LIST_SIZE 8
#define IPC_MSG_TIMEOUT_MSECS 300 /* IPC message timeout (msecs) */
struct hifi4dsp_ipc_message {
struct list_head list;
u64 header;
char *tx_data;
size_t tx_size;
char *rx_data;
size_t rx_size;
wait_queue_head_t waitq;
bool pending;
bool complete;
bool wait;
int errno;
};
struct hifi4dsp_ipc_plat_ops {
void (*tx_msg)(struct hifi4dsp_ipc *, struct hifi4dsp_ipc_message *);
void (*tx_data_copy)(struct hifi4dsp_ipc_message *, char *, size_t);
bool (*is_dsp_busy)(struct hifi4dsp_dsp *dsp);
void (*debug_info)(struct hifi4dsp_ipc *, const char *);
u64 (*reply_msg_match)(u64 header, u64 *mask);
};
struct hifi4dsp_ipc {
struct device *dev;
struct hifi4dsp_dsp *dsp;
/* IPC messaging */
struct list_head tx_list;
struct list_head rx_list;
struct list_head empty_list;
wait_queue_head_t wait_txq;
struct task_struct *tx_thread;
struct kthread_worker kworker;
struct kthread_work kwork;
bool pending;
struct hifi4dsp_ipc_message *msg;
int tx_data_max_size;
int rx_data_max_size;
struct hifi4dsp_ipc_plat_ops ops;
};
int hifi4dsp_ipc_tx_message_wait(struct hifi4dsp_ipc *ipc, u64 header,
void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes);
int hifi4dsp_ipc_tx_message_nowait(struct hifi4dsp_ipc *ipc, u64 header,
void *tx_data, size_t tx_bytes);
struct hifi4dsp_ipc_message *hifi4dsp_ipc_reply_find_msg(
struct hifi4dsp_ipc *ipc, u64 header);
void hifi4dsp_ipc_tx_msg_reply_complete(struct hifi4dsp_ipc *ipc,
struct hifi4dsp_ipc_message *msg);
void hifi4dsp_ipc_drop_all(struct hifi4dsp_ipc *ipc);
int hifi4dsp_ipc_init(struct hifi4dsp_ipc *ipc);
void hifi4dsp_ipc_fini(struct hifi4dsp_ipc *ipc);
#endif /*_HIFI4DSP_IPC_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
/*
* drivers/amlogic/hifi4dsp/hifi4dsp_priv.h
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#ifndef _HIFI4DSP_PRIV_H
#define _HIFI4DSP_PRIV_H
#include <linux/device.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
/*
* #include <asm/dsp/hifi4dsp_control.h>
* #include <asm/dsp/dsp_register.h>
*/
//#include "hifi4dsp_control.h"
#include <linux/amlogic/media/sound/dsp_register.h>
#include <linux/dma-mapping.h>
#include "hifi4dsp_api.h"
#include "hifi4dsp_dsp.h"
#include "hifi4dsp_firmware.h"
#include "hifi4dsp_ipc.h"
struct class;
struct hifi4dsp_priv {
char name[12];
struct class *class;
struct device *dev;
struct device *dma_dev;
u32 dsp_freq;
bool dsp_is_started;
struct hifi4dsp_dsp *dsp;
struct hifi4dsp_dsp_device *dsp_dev;
struct hifi4dsp_firmware *dsp_fw;
struct hifi4dsp_mailbox *mailbox;
struct hifi4dsp_pdata *pdata;
struct hifi4dsp_ipc ipc;
struct clk *p_clk;
struct clk *p_clk_gate;
};
struct hifi4dsp_miscdev_t {
struct miscdevice dsp_miscdev;
struct hifi4dsp_priv *priv;
};
struct hifi4dsp_resource_t {
struct resource res_iomem;
struct clk *p_clk_gate;
struct clk *p_clk;
int irq;
};
struct hifi4dsp_priv *hifi4dsp_privdata(void);
#ifndef HIFI4DSP_PRNT
#define HIFI4DSP_PRNT(...) pr_info(__VA_ARGS__)
#endif
#endif /*_HIFI4DSP_PRIV_H*/

View File

@@ -0,0 +1,525 @@
/*
* drivers/amlogic/hifi4dsp/tm2_dsp_top.c
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/firmware.h>
#include <linux/amlogic/major.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/amlogic/scpi_protocol.h>
#include "hifi4dsp_priv.h"
#include "hifi4dsp_firmware.h"
#include "hifi4dsp_dsp.h"
/*AO_RTI*/
#define AO_RTI_ADDR_BASE (0xff800000)
#define REG_AO_RTI_GEN_PWR_SLEEP0 (0x03a << 2)
#define REG_AO_RTI_GEN_PWR_ISO0 (0x03b << 2)
/*HIU*/
#define HUI_ADDR_BASE (0xff63c000)
#define REG_HHI_DSP_CLK_CNTL (0x0fc << 2)
#define REG_HHI_DSP_MEM_PD_REG0 (0x044 << 2)
/*reset*/
#define RESET_ADDR_BASE (0xffd01000)
#define REG_RESET1_LEVEL (0x84)
#define REG_RESET4_LEVEL (0x90)
/*DSP TOP*/
#define DSPA_REG_BASE (0xff680000)
#define DSPB_REG_BASE (0xff690000)
#define REG_DSP_CFG0 (0x0)
#define REG_DSP_CFG1 (0x4)
#define REG_DSP_CFG2 (0x8)
#define REG_DSP_RESET_VEC (0x004 << 2)
struct reg_iomem_t {
void __iomem *dsp_addr;
void __iomem *dspa_addr;
void __iomem *dspb_addr;
void __iomem *hiu_addr; /*HIU*/
void __iomem *ao_rti_addr; /*AO_RTI*/
void __iomem *reset_addr;
void __iomem *sleep_addr;
};
static struct reg_iomem_t g_regbases;
static bool regs_iomem_is_inited;
static void __iomem *reg_iomem_init(phys_addr_t paddr, u32 size)
{
void __iomem *vaddr = NULL;
vaddr = ioremap_nocache(paddr, size);
pr_debug("%s phys: %llx, virt: %p, size:%x Bytes\n",
__func__,
(unsigned long long)paddr,
vaddr,
size);
return vaddr;
}
static inline void tm2_dsp_top_reg_dump(char *name,
void __iomem *reg_base, u32 reg_offset)
{
pr_info("%s (%p) = 0x%x\n", name,
(reg_base + reg_offset),
readl(reg_base + reg_offset));
}
void tm2_dsp_regs_iomem_init(void)
{
if (regs_iomem_is_inited == true) {
pr_info("%s has been done\n", __func__);
return;
}
g_regbases.dspa_addr = reg_iomem_init(DSPA_REG_BASE, 0x8B*4);
g_regbases.dspb_addr = reg_iomem_init(DSPB_REG_BASE, 0x8B*4);
g_regbases.hiu_addr = reg_iomem_init(HUI_ADDR_BASE, 0xFF*4);
g_regbases.ao_rti_addr = reg_iomem_init(AO_RTI_ADDR_BASE, 0xFF*4);
g_regbases.reset_addr = reg_iomem_init(RESET_ADDR_BASE, 0x40*4);
regs_iomem_is_inited = true;
pr_debug("%s done\n", __func__);
}
void tm2_dsp_regs_iounmem(void)
{
iounmap(g_regbases.dspa_addr);
iounmap(g_regbases.dspb_addr);
iounmap(g_regbases.hiu_addr);
iounmap(g_regbases.ao_rti_addr);
iounmap(g_regbases.reset_addr);
regs_iomem_is_inited = false;
pr_debug("%s done\n", __func__);
}
static inline void __iomem *get_hiu_addr(void)
{
return g_regbases.hiu_addr;
}
static inline void __iomem *get_ao_rti_addr(void)
{
return g_regbases.ao_rti_addr;
}
static inline void __iomem *get_reset_addr(void)
{
return g_regbases.reset_addr;
}
static inline void __iomem *get_dsp_addr(int dsp_id)
{
if (dsp_id == 1)
return g_regbases.dspb_addr;
else
return g_regbases.dspa_addr;
}
/*
* clk_util_set_dsp_clk
* freq_sel: 0:286MHz fclk_7
* 1:400MHz fclk_5
* 2:500MHz fclk_2/2
* 3:667MHz fclk_3
* 4:1000MHz fclk_2
* others:286MHz fclk/7
*/
//crt_clk_div_mux4_ns #(8)
// u_crt_dspa_clk_mux_div(
// .clk0 (fclk_div2 ),
// .clk1 (fclk_div3 ),
// .clk2 (fclk_div5 ),
// .clk3 (fclk_div7 ),
// .reset_n (crt_reset_n ),
// .force_oscin_clk (1'b0 ),
// .cts_oscin_clk (1'b0 ),
//
// .clk_div (hi_dsp_clk_cntl[7:0] ),
// .clk_en (hi_dsp_clk_cntl[15] ),
// .clk_sel (hi_dsp_clk_cntl[9:8] ),
// .clk_out (cts_dspa_clk ));
static void clk_util_set_dsp_clk(uint32_t id, uint32_t freq_sel)
{
uint32_t clk_sel = 0;
uint32_t clk_div = 0;
uint32_t read;
void __iomem *reg;
reg = get_hiu_addr() + REG_HHI_DSP_CLK_CNTL;
switch (freq_sel) {
case 1:
clk_sel = 2;
clk_div = 0;
pr_debug("CLK_UTIL:dsp:fclk/5:400MHz\n");
break;
case 2:
clk_sel = 0;
clk_div = 1;
pr_debug("CLK_UTIL:dsp:fclk/4:500MHz\n");
break;
case 3:
//clk_sel = 1;
//clk_div = 0;
//pr_debug("CLK_UTIL:dsp:fclk/3:667MHz\n");
break;
case 4:
clk_sel = 1;
clk_div = 1;
pr_debug("CLK_UTIL:dsp:fclk/3/2:333MHz\n");
break;
case 5:
clk_sel = 0;
clk_div = 3;
pr_debug("CLK_UTIL:dsp:fclk/2:250MHz\n");
break;
case 6:
clk_sel = 2;
clk_div = 1;
pr_debug("CLK_UTIL:dsp:fclk/4/2:200MHz\n");
break;
case 7:
clk_sel = 2;
clk_div = 3;
pr_debug("CLK_UTIL:dsp:fclk/4/4:100MHz\n");
break;
case 8:
clk_sel = 4;
clk_div = 0;
pr_debug("CLK_UTIL:dsp:oscin:24MHz\n");
break;
case 10:
//clk_sel = 0;
//clk_div = 0;
//pr_debug("CLK_UTIL:dsp:fclk/2:1000MHz\n");
break;
default:
clk_sel = 3;
clk_div = 0;
pr_debug("CLK_UTIL:dsp:fclk/7:286MHz\n");
break;
}
read = readl(reg);
if (id == 0) {
//read = (read & ~((0x3<<8) | (0xff<<0)));
//read = read | ((1<<15) | (clk_sel<<8) | (clk_div<<0));
if (read & (1 << 15)) { //if sync_mux ==1, sel mux 0
read &= (~((1 << 15) | (0xf << 0) | (0x7 << 4)));
read |= (1 << 7) | (clk_div << 0) | (clk_sel << 4);
} else {
read &= (~((1 << 15) | (0xf << 8) | (0x7 << 12)));
read |= (1 << 7) | (clk_div << 8);
read |= (clk_sel << 12) | (1 << 15);
}
} else {
//read = (read & ~((0x3<<24) | (0xff<<16)));
//read = read | ((1<<31) | (clk_sel<<24) | (clk_div<<16));
if (read & (1 << 31)) { //if sync_mux ==1, sel mux 0
read &= (~((1 << 31) | (0xf << 16) | (0x7 << 20)));
read |= (1 << 23) | (clk_div << 16) | (clk_sel << 20);
} else {
read &= (~((1 << 31) | (0xf << 24) | (0x7 << 28)));
read |= (1 << 23) | (clk_div << 24);
read |= (clk_sel << 28) | (1 << 31);
}
}
writel(read, reg);
pr_debug("%s\n", __func__);
}
static void start_dsp(uint32_t dsp_id, uint32_t reset_addr)
{
uint32_t StatVectorSel;
uint32_t strobe = 1;
uint32_t tmp;
uint32_t read;
void __iomem *reg;
reg = get_dsp_addr(dsp_id);
StatVectorSel = (reset_addr != 0xfffa0000);
// the bit 0 is no use, dsp in tm2 is non-secure
tmp = 0x1 | StatVectorSel<<1 | strobe<<2;
scpi_init_dsp_cfg0(dsp_id, reset_addr, tmp);
read = readl(reg+REG_DSP_CFG0);
pr_debug("REG_DSP_CFG0 read=0x%x\n", read);
if (dsp_id == 0) {
read = read & (~((1 << 31) | (1 << 30) | (0xffff << 0)));
read = read | (1 << 29) | (0 << 0); // 29 irq_clk_en
} else {
read = read & (~((1 << 31) | (1 << 30) | (0xffff << 0)));
read = read | (1 << 29) | (1 << 0);
}
writel(read, reg+REG_DSP_CFG0);
tm2_dsp_top_reg_dump("REG_DSP_CFG0", reg, REG_DSP_CFG0);
pr_debug("%s\n", __func__);
}
static void power_switch_to_dsp_a(int pwr_cntl)
{
uint32_t tmp;
void __iomem *reg;
pr_info("[PWR]: Power %s DSP A\n", pwr_cntl?"On":"Off");
if (pwr_cntl == 1) {
// Powerup dsp a
reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_SLEEP0;
tmp = readl(reg) & (~(0x1<<21));
writel(tmp, reg);// power on
udelay(5);
// Power up memory
reg = get_hiu_addr() + REG_HHI_DSP_MEM_PD_REG0;
tmp = readl(reg) & (~(0xffff<<0));
writel(tmp, reg);
udelay(5);
// reset
reg = get_reset_addr() + REG_RESET4_LEVEL;
tmp = readl(reg) & (~(0x1<<0));
writel(tmp, reg);
reg = get_reset_addr() + REG_RESET1_LEVEL;
tmp = readl(reg) & (~(0x1<<20));
writel(tmp, reg);
// remove isolation
reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_ISO0;
tmp = readl(reg) & (~(0x1<<21));
writel(tmp, reg);
// pull up reset
reg = get_reset_addr() + REG_RESET4_LEVEL;
tmp = readl(reg) | (0x1<<0);
writel(tmp, reg);
reg = get_reset_addr() + REG_RESET1_LEVEL;
tmp = readl(reg) | (0x1<<20);
writel(tmp, reg);
} else {
// reset
reg = get_reset_addr() + REG_RESET4_LEVEL;
tmp = readl(reg) & (~(0x1<<0));
writel(tmp, reg);
reg = get_reset_addr() + REG_RESET1_LEVEL;
tmp = readl(reg) & (~(0x1<<20));
writel(tmp, reg);
// add isolation
reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_ISO0;
tmp = readl(reg) | (0x1<<21);
writel(tmp, reg);
udelay(5);
// power down memory
reg = get_hiu_addr() + REG_HHI_DSP_MEM_PD_REG0;
tmp = readl(reg) | (0xffff<<0);
writel(tmp, reg);
udelay(5);
// power down dsp a
reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_SLEEP0;
tmp = readl(reg) | (0x1<<21);
writel(tmp, reg);
udelay(5);
}
}
static void power_switch_to_dsp_b(int pwr_cntl)
{
uint32_t tmp;
void __iomem *reg;
if (pwr_cntl == 1) {
pr_info("[PWR]: Power on DSP B\n");
// Powerup dsp a
reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_SLEEP0;
tmp = readl(reg) & (~(0x1<<22));
writel(tmp, reg);// power on
udelay(5);
// Power up memory
reg = get_hiu_addr() + REG_HHI_DSP_MEM_PD_REG0;
tmp = readl(reg) & (~(0xffff<<16));
writel(tmp, reg);
udelay(5);
// reset
reg = get_reset_addr() + REG_RESET4_LEVEL;
tmp = readl(reg) & (~(0x1<<1));
writel(tmp, reg);
reg = get_reset_addr() + REG_RESET1_LEVEL;
tmp = readl(reg) & (~(0x1<<21));
writel(tmp, reg);
// remove isolation
reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_ISO0;
tmp = readl(reg) & (~(0x1<<22));
writel(tmp, reg);
// pull up reset
reg = get_reset_addr() + REG_RESET4_LEVEL;
tmp = readl(reg) | (0x1<<1);
writel(tmp, reg);
reg = get_reset_addr() + REG_RESET1_LEVEL;
tmp = readl(reg) | (0x1<<21);
writel(tmp, reg);
} else {
pr_info("[PWR]: Power off DSP B\n");
// reset
reg = get_reset_addr() + REG_RESET4_LEVEL;
tmp = readl(reg) & (~(0x1<<1));
writel(tmp, reg);
reg = get_reset_addr() + REG_RESET1_LEVEL;
tmp = readl(reg) & (~(0x1<<21));
writel(tmp, reg);
// add isolation
reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_ISO0;
tmp = readl(reg) | (0x1<<22);
writel(tmp, reg);
udelay(5);
// power down memory
reg = get_hiu_addr() + REG_HHI_DSP_MEM_PD_REG0;
tmp = readl(reg) | (0xffff<<16);
writel(tmp, reg);
udelay(5);
// power down dsp a
reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_SLEEP0;
tmp = readl(reg) | (0x1<<22);
writel(tmp, reg);
udelay(5);
}
}
static void tm2_dsp_power_switch(int dsp_id, int pwr_cntl)
{
if (dsp_id == 0)
power_switch_to_dsp_a(pwr_cntl);
else if (dsp_id == 1)
power_switch_to_dsp_b(pwr_cntl);
else
pr_err("%s param: dsp_id=%d error\n", __func__, dsp_id);
}
void tm2_dsp_top_regs_dump(int dsp_id)
{
void __iomem *reg;
pr_debug("%s\n", __func__);
reg = get_dsp_addr(dsp_id);
pr_debug("%s base=%p\n", __func__, reg);
tm2_dsp_top_reg_dump("REG_DSP_CFG0", reg, REG_DSP_CFG0);
tm2_dsp_top_reg_dump("REG_DSP_CFG1", reg, REG_DSP_CFG1);
tm2_dsp_top_reg_dump("REG_DSP_CFG2", reg, REG_DSP_CFG2);
tm2_dsp_top_reg_dump("REG_DSP_RESET_VEC", reg, REG_DSP_RESET_VEC);
reg = get_hiu_addr();
tm2_dsp_top_reg_dump("REG_HHI_DSP_CLK_CNTL", reg,
REG_HHI_DSP_CLK_CNTL);
tm2_dsp_top_reg_dump("REG_HHI_DSP_MEM_PD_REG0", reg,
REG_HHI_DSP_MEM_PD_REG0);
reg = get_ao_rti_addr();
tm2_dsp_top_reg_dump("REG_AO_RTI_GEN_PWR_SLEEP0", reg,
REG_AO_RTI_GEN_PWR_SLEEP0);
tm2_dsp_top_reg_dump("REG_AO_RTI_GEN_PWR_ISO0", reg,
REG_AO_RTI_GEN_PWR_ISO0);
reg = get_reset_addr();
tm2_dsp_top_reg_dump("REG_RESET1_LEVEL", reg, REG_RESET1_LEVEL);
tm2_dsp_top_reg_dump("REG_RESET4_LEVEL", reg, REG_RESET4_LEVEL);
}
static void tm2_dsp_set_clk(int dsp_id, int freq_sel)
{
clk_util_set_dsp_clk(dsp_id, freq_sel);
}
void tm2_dsp_hw_init(int dsp_id, int freq_sel)
{
int pwr_cntl = 1;
tm2_dsp_regs_iomem_init();
tm2_dsp_set_clk(dsp_id, freq_sel);
tm2_dsp_power_switch(dsp_id, pwr_cntl);
pr_debug("%s done\n", __func__);
}
void tm2_dsp_start(int dsp_id, int freq_sel)
{
start_dsp(dsp_id, freq_sel);
}
void tm2_dsp_bootup(int dsp_id, uint32_t reset_addr, int freq_sel)
{
int pwr_cntl = 1;
//reset_addr = 0x30000000;
//dsp_id = 0;
freq_sel = 1;
pr_debug("%s dsp_id=%d, address=0x%x\n",
__func__, dsp_id, reset_addr);
tm2_dsp_set_clk(dsp_id, freq_sel);
tm2_dsp_power_switch(dsp_id, pwr_cntl);
tm2_dsp_start(dsp_id, reset_addr);
msleep(5*1000);
tm2_dsp_top_regs_dump(dsp_id);
}

View File

@@ -0,0 +1,36 @@
/*
* drivers/amlogic/hifi4dsp/tm2_dsp_top.h
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#ifndef _TM2_DSP_TOP_H
#define _TM2_DSP_TOP_H
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/firmware.h>
#include <linux/irqreturn.h>
#include "hifi4dsp_priv.h"
#include "hifi4dsp_firmware.h"
#include "hifi4dsp_dsp.h"
extern void tm2_dsp_bootup(int dsp_id, uint32_t reset_addr, int freq_sel);
extern void tm2_dsp_regs_iomem_init(void);
extern void tm2_dsp_hw_init(int dsp_id, int freq_sel);
extern void tm2_dsp_top_regs_dump(int dsp_id);
#endif /*_TM2_DSP_TOP_H*/

View File

@@ -89,6 +89,8 @@ int hdcp22_on;
MODULE_PARM_DESC(hdcp22_on, "\n hdcp22_on\n");
module_param(hdcp22_on, int, 0664);
/* test for HBR CTS, audio module can set it to force 8ch */
int hbr_force_8ch = 1;
/*
* hdcp14_key_mode:hdcp1.4 key handle method select
* NORMAL_MODE:systemcontrol path
@@ -1793,130 +1795,146 @@ else
*/
int hdmirx_audio_init(void)
{
/* 0=I2S 2-channel; 1=I2S 4 x 2-channel. */
int err = 0;
unsigned long data32 = 0;
/* 0=I2S 2-channel; 1=I2S 4 x 2-channel. */
int err = 0;
unsigned long data32 = 0;
data32 |= 7 << 13;
data32 |= 0 << 12;
data32 |= 1 << 11;
data32 |= 0 << 10;
data32 |= 7 << 13;
data32 |= 0 << 12;
data32 |= 1 << 11;
data32 |= 0 << 10;
data32 |= 0 << 9;
data32 |= 1 << 8;
data32 |= 1 << 6;
data32 |= 3 << 4;
data32 |= 0 << 3;
data32 |= acr_mode << 2;
data32 |= acr_mode << 1;
data32 |= acr_mode << 0;
hdmirx_wr_top(TOP_ACR_CNTL_STAT, data32);
data32 |= 0 << 9;
data32 |= 1 << 8;
data32 |= 1 << 6;
data32 |= 3 << 4;
data32 |= 0 << 3;
data32 |= acr_mode << 2;
data32 |= acr_mode << 1;
data32 |= acr_mode << 0;
hdmirx_wr_top(TOP_ACR_CNTL_STAT, data32);
/*
*recover to default value, bit[27:24]
*set aud_pll_lock filter
*data32 = 0;
*data32 |= 0 << 28;
*data32 |= 0 << 24;
*hdmirx_wr_dwc(DWC_AUD_PLL_CTRL, data32);
*/
if (rx.chip_id >= CHIP_ID_TL1) {
data32 = 0;
data32 |= 0 << 2;/*meas_mode*/
data32 |= 1 << 1;/*enable*/
data32 |= 1 << 0;/*reset*/
if (acr_mode)
data32 |= 2 << 16;/*aud pll*/
else
data32 |= 500 << 16;/*acr*/
hdmirx_wr_top(TOP_AUDMEAS_CTRL, data32);
hdmirx_wr_top(TOP_AUDMEAS_CYCLES_M1, 65535);
/*start messure*/
hdmirx_wr_top(TOP_AUDMEAS_CTRL, data32 & (~0x1));
}
/* AFIFO depth 1536word.*/
/*increase start threshold to middle position */
data32 = 0;
data32 |= 160 << 18; /* start */
data32 |= 200 << 9; /* max */
data32 |= 8 << 0; /* min */
hdmirx_wr_dwc(DWC_AUD_FIFO_TH, data32);
/*
*recover to default value, bit[27:24]
*set aud_pll_lock filter
*data32 = 0;
*data32 |= 0 << 28;
*data32 |= 0 << 24;
*hdmirx_wr_dwc(DWC_AUD_PLL_CTRL, data32);
*/
/* recover to default value.*/
/*remain code for some time.*/
/*if no side effect then remove it */
/*
*data32 = 0;
*data32 |= 1 << 16;
*data32 |= 0 << 0;
*hdmirx_wr_dwc(DWC_AUD_FIFO_CTRL, data32);
*/
/* AFIFO depth 1536word.*/
/*increase start threshold to middle position */
data32 = 0;
data32 |= 160 << 18; /* start */
data32 |= 200 << 9; /* max */
data32 |= 8 << 0; /* min */
hdmirx_wr_dwc(DWC_AUD_FIFO_TH, data32);
data32 = 0;
data32 |= 0 << 8;
data32 |= 1 << 7;
data32 |= aud_ch_map << 2;
data32 |= 1 << 0;
hdmirx_wr_dwc(DWC_AUD_CHEXTR_CTRL, data32);
/* recover to default value.*/
/*remain code for some time.*/
/*if no side effect then remove it */
/*
*data32 = 0;
*data32 |= 1 << 16;
*data32 |= 0 << 0;
*hdmirx_wr_dwc(DWC_AUD_FIFO_CTRL, data32);
*/
data32 = 0;
/* [22:21] aport_shdw_ctrl */
data32 |= 3 << 21;
/* [20:19] auto_aclk_mute */
data32 |= auto_aclk_mute << 19;
/* [16:10] aud_mute_speed */
data32 |= 1 << 10;
/* [7] aud_avmute_en */
data32 |= aud_avmute_en << 7;
/* [6:5] aud_mute_sel */
data32 |= aud_mute_sel << 5;
/* [4:3] aud_mute_mode */
data32 |= 1 << 3;
/* [2:1] aud_ttone_fs_sel */
data32 |= 0 << 1;
/* [0] testtone_en */
data32 |= 0 << 0;
hdmirx_wr_dwc(DWC_AUD_MUTE_CTRL, data32);
data32 = 0;
data32 |= 0 << 8;
data32 |= 1 << 7;
data32 |= aud_ch_map << 2;
data32 |= 1 << 0;
hdmirx_wr_dwc(DWC_AUD_CHEXTR_CTRL, data32);
/* recover to default value.*/
/*remain code for some time.*/
/*if no side effect then remove it */
/*
*data32 = 0;
*data32 |= 0 << 16;
*data32 |= 0 << 12;
*data32 |= 0 << 4;
*data32 |= 0 << 1;
*data32 |= 0 << 0;
*hdmirx_wr_dwc(DWC_AUD_PAO_CTRL, data32);
*/
data32 = 0;
/* [22:21] aport_shdw_ctrl */
data32 |= 3 << 21;
/* [20:19] auto_aclk_mute */
data32 |= auto_aclk_mute << 19;
/* [16:10] aud_mute_speed */
data32 |= 1 << 10;
/* [7] aud_avmute_en */
data32 |= aud_avmute_en << 7;
/* [6:5] aud_mute_sel */
data32 |= aud_mute_sel << 5;
/* [4:3] aud_mute_mode */
data32 |= 1 << 3;
/* [2:1] aud_ttone_fs_sel */
data32 |= 0 << 1;
/* [0] testtone_en */
data32 |= 0 << 0;
hdmirx_wr_dwc(DWC_AUD_MUTE_CTRL, data32);
/* recover to default value.*/
/*remain code for some time.*/
/*if no side effect then remove it */
/*
*data32 = 0;
*data32 |= 0 << 8;
*hdmirx_wr_dwc(DWC_PDEC_AIF_CTRL, data32);
*/
/* recover to default value.*/
/*remain code for some time.*/
/*if no side effect then remove it */
/*
*data32 = 0;
*data32 |= 0 << 16;
*data32 |= 0 << 12;
*data32 |= 0 << 4;
*data32 |= 0 << 1;
*data32 |= 0 << 0;
*hdmirx_wr_dwc(DWC_AUD_PAO_CTRL, data32);
*/
data32 = 0;
/* [4:2] deltacts_irqtrig */
data32 |= 0 << 2;
/* [1:0] cts_n_meas_mode */
data32 |= 0 << 0;
/* DEFAULT: {27'd0, 3'd0, 2'd1} */
hdmirx_wr_dwc(DWC_PDEC_ACRM_CTRL, data32);
/* recover to default value.*/
/*remain code for some time.*/
/*if no side effect then remove it */
/*
*data32 = 0;
*data32 |= 0 << 8;
*hdmirx_wr_dwc(DWC_PDEC_AIF_CTRL, data32);
*/
hdmirx_wr_bits_dwc(DWC_AUD_CTRL, DWC_AUD_HBR_ENABLE, 1);
data32 = 0;
/* [4:2] deltacts_irqtrig */
data32 |= 0 << 2;
/* [1:0] cts_n_meas_mode */
data32 |= 0 << 0;
/* DEFAULT: {27'd0, 3'd0, 2'd1} */
hdmirx_wr_dwc(DWC_PDEC_ACRM_CTRL, data32);
/* SAO cfg, disable I2S output, no use */
data32 = 0;
data32 |= 1 << 10;
data32 |= 0 << 9;
data32 |= 0x0f << 5;
data32 |= 0 << 1;
data32 |= 1 << 0;
hdmirx_wr_dwc(DWC_AUD_SAO_CTRL, data32);
/* unsupport HBR serial mode. invalid bit */
/* hdmirx_wr_bits_dwc(DWC_AUD_CTRL, DWC_AUD_HBR_ENABLE, 1); */
data32 = 0;
data32 |= 1 << 6;
data32 |= 0xf << 2;
hdmirx_wr_dwc(DWC_PDEC_ASP_CTRL, data32);
/* SAO cfg, disable I2S output, no use */
data32 = 0;
data32 |= 1 << 10;
data32 |= 0 << 9;
data32 |= 0x0f << 5;
data32 |= 0 << 1;
data32 |= 1 << 0;
hdmirx_wr_dwc(DWC_AUD_SAO_CTRL, data32);
return err;
data32 = 0;
data32 |= 1 << 6;
data32 |= 0xf << 2;
hdmirx_wr_dwc(DWC_PDEC_ASP_CTRL, data32);
return err;
}
/*
* snps phy g3 initial
*/
* snps phy g3 initial
*/
void snps_phyg3_init(void)
{
unsigned int data32;

View File

@@ -151,6 +151,26 @@ struct __extcon_info {
.id = EXTCON_SPDIFIN_AUDIOTYPE,
.name = "SPDIFIN-AUDIOTYPE",
},
[EXTCON_EARCRX_ATNDTYP_ARC] = {
.type = EXTCON_TYPE_MISC,
.id = EXTCON_EARCRX_ATNDTYP_ARC,
.name = "EARCRX-ARC",
},
[EXTCON_EARCRX_ATNDTYP_EARC] = {
.type = EXTCON_TYPE_MISC,
.id = EXTCON_EARCRX_ATNDTYP_EARC,
.name = "EARCRX-EARC",
},
[EXTCON_EARCTX_ATNDTYP_ARC] = {
.type = EXTCON_TYPE_MISC,
.id = EXTCON_EARCTX_ATNDTYP_ARC,
.name = "EARCTX-ARC",
},
[EXTCON_EARCTX_ATNDTYP_EARC] = {
.type = EXTCON_TYPE_MISC,
.id = EXTCON_EARCTX_ATNDTYP_EARC,
.name = "EARCTX-EARC",
},
#endif
/* Display external connector */
[EXTCON_DISP_HDMI] = {

View File

@@ -67,6 +67,10 @@
#ifdef CONFIG_AMLOGIC_SND_SOC_AUGE
#define EXTCON_SPDIFIN_SAMPLERATE 28 /* spdif in sample rate changed */
#define EXTCON_SPDIFIN_AUDIOTYPE 29 /* spdif in PcPd detect */
#define EXTCON_EARCRX_ATNDTYP_ARC 30 /* attended type, RX ARC */
#define EXTCON_EARCRX_ATNDTYP_EARC 31 /* attended type, RX eARC */
#define EXTCON_EARCTX_ATNDTYP_ARC 32 /* attended type, TX ARC */
#define EXTCON_EARCTX_ATNDTYP_EARC 33 /* attended type, TX eARC */
#endif
/* Display external connector */
#define EXTCON_DISP_HDMI 40 /* High-Definition Multimedia Interface */

View File

@@ -6,7 +6,6 @@ obj-$(CONFIG_AMLOGIC_SND_SOC_AUGE) += audio_controller.o \
tl1,clocks.o \
sm1,clocks.o \
tm2,clocks.o \
card.o \
card_utils.o \
tdm.o \
tdm_hw.o \
@@ -37,4 +36,5 @@ obj-$(CONFIG_AMLOGIC_SND_SOC_AUGE) += audio_controller.o \
vad_hw.o \
vad_dev.o \
earc.o \
earc_hw.o
earc_hw.o \
card.o

View File

@@ -99,6 +99,8 @@ static int audio_clocks_probe(struct platform_device *pdev)
return ret;
}
pr_info("%s done\n", __func__);
return 0;
}
@@ -109,7 +111,16 @@ static struct platform_driver audio_clocks_driver = {
},
.probe = audio_clocks_probe,
};
module_platform_driver(audio_clocks_driver);
int __init audio_clocks_init(void)
{
int ret;
ret = platform_driver_register(&audio_clocks_driver);
return ret;
}
core_initcall(audio_clocks_init);
MODULE_AUTHOR("Amlogic, Inc.");
MODULE_DESCRIPTION("Amlogic audio clocks ASoc driver");

View File

@@ -84,6 +84,15 @@ do { \
&_name##_gate.hw, &clk_gate_ops, \
CLK_SET_RATE_NO_REPARENT)
#define REGISTER_CLK_COM_PARENTS(_name, pnames) \
clk_register_composite(NULL, #_name, \
pnames##_parent_names, \
ARRAY_SIZE(pnames##_parent_names), \
&_name##_mux.hw, &clk_mux_ops, \
&_name##_div.hw, &clk_divider_ops, \
&_name##_gate.hw, &clk_gate_ops, \
CLK_SET_RATE_NO_REPARENT)
struct audio_clk_init {
int clk_num;
int (*clk_gates)(struct clk **clks, void __iomem *iobase);

View File

@@ -26,6 +26,8 @@
#include <linux/regmap.h>
#include <linux/clk-provider.h>
#include <linux/amlogic/media/sound/iomapres.h>
#include "audio_io.h"
#include "regs.h"
@@ -35,11 +37,8 @@ static unsigned int aml_audio_mmio_read(struct aml_audio_controller *actrlr,
unsigned int reg)
{
struct regmap *regmap = actrlr->regmap;
unsigned int val;
regmap_read(regmap, (reg << 2), &val);
return val;
return mmio_read(regmap, reg);
}
static int aml_audio_mmio_write(struct aml_audio_controller *actrlr,
@@ -47,7 +46,12 @@ static int aml_audio_mmio_write(struct aml_audio_controller *actrlr,
{
struct regmap *regmap = actrlr->regmap;
return regmap_write(regmap, (reg << 2), value);
pr_debug("audio top reg:[%s] addr: [%#x] val: [%#x]\n",
top_register_table[reg].name,
top_register_table[reg].addr,
value);
return mmio_write(regmap, reg, value);
}
static int aml_audio_mmio_update_bits(struct aml_audio_controller *actrlr,
@@ -55,7 +59,12 @@ static int aml_audio_mmio_update_bits(struct aml_audio_controller *actrlr,
{
struct regmap *regmap = actrlr->regmap;
return regmap_update_bits(regmap, (reg << 2), mask, value);
pr_debug("audio top reg:[%s] addr: [%#x] mask: [%#x] val: [%#x]\n",
top_register_table[reg].name,
top_register_table[reg].addr,
mask, value);
return mmio_update_bits(regmap, reg, mask, value);
}
struct aml_audio_ctrl_ops aml_actrl_mmio_ops = {

View File

@@ -84,7 +84,7 @@ struct aml_card_data {
int micphone_gpio_det;
int mic_detect_flag;
bool mic_det_enable;
bool av_mute_enable;
struct aml_chipset_info *chipinfo;
};
@@ -559,29 +559,48 @@ static int aml_card_dai_link_of(struct device_node *node,
goto dai_link_of_err;
}
dai_link->cpu_of_node = of_parse_phandle(cpu, DAI, 0);
if (!dai_link->cpu_of_node) {
dev_err(dev, "error getting cpu phandle\n");
return -EINVAL;
}
ret = aml_card_parse_daifmt(dev, node, codec,
prefix, &dai_link->dai_fmt);
if (ret < 0)
if (ret < 0) {
dev_err(dev, "%s, dai fmt not found\n",
__func__);
goto dai_link_of_err;
}
of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs);
ret = aml_card_parse_cpu(cpu, dai_link,
DAI, CELL, &single_cpu);
if (ret < 0)
if (ret < 0) {
dev_err(dev, "%s, dai-link idx:%d, error getting cpu dai name:%s\n",
__func__,
idx,
dai_link->cpu_dai_name);
goto dai_link_of_err;
}
#if 0
ret = aml_card_parse_codec(codec, dai_link, DAI, CELL);
#else
ret = snd_soc_of_get_dai_link_codecs(dev, codec, dai_link);
#endif
if (ret < 0)
if (ret < 0) {
dev_err(dev, "%s, dai-link idx:%d, error getting codec dai name:%s\n",
__func__,
idx,
dai_link->codec_dai_name);
goto dai_link_of_err;
}
ret = aml_card_parse_platform(plat, dai_link, DAI, CELL);
if (ret < 0)
if (ret < 0) {
dev_err(dev, "%s, platform not found\n",
__func__);
goto dai_link_of_err;
}
ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask,
&cpu_dai->rx_slot_mask,
@@ -605,12 +624,6 @@ static int aml_card_dai_link_of(struct device_node *node,
if (ret < 0)
goto dai_link_of_err;
#if 0
ret = aml_card_parse_clk_codec(codec, dai_link, codec_dai);
if (ret < 0)
goto dai_link_of_err;
#endif
ret = aml_card_canonicalize_dailink(dai_link);
if (ret < 0)
goto dai_link_of_err;
@@ -749,20 +762,28 @@ static int aml_card_parse_gpios(struct device_node *node,
snd_soc_add_card_controls(soc_card, card_controls,
ARRAY_SIZE(card_controls));
}
pr_info("add line-out mute controls\n");
}
#if !defined(CONFIG_ARCH_MESON64_ODROID_COMMON)
priv->avout_mute_desc = gpiod_get(dev,
"avout_mute", GPIOF_OUT_INIT_LOW);
if (IS_ERR_OR_NULL(priv->avout_mute_desc)) {
priv->avout_mute_desc = gpiod_get(dev,
"avout_mute", GPIOF_OUT_INIT_LOW);
}
if (!IS_ERR(priv->avout_mute_desc)) {
msleep(500);
gpiod_direction_output(priv->avout_mute_desc,
GPIOF_OUT_INIT_HIGH);
pr_info("av out status: %s\n",
gpiod_get_value(priv->avout_mute_desc) ?
"high" : "low");
if (!priv->av_mute_enable) {
msleep(500);
gpiod_direction_output(priv->avout_mute_desc,
GPIOF_OUT_INIT_HIGH);
pr_info("av out status: %s\n",
gpiod_get_value(priv->avout_mute_desc) ?
"high" : "low");
} else {
gpiod_direction_output(priv->avout_mute_desc,
GPIOF_OUT_INIT_LOW);
pr_info("av out status: %s\n",
gpiod_get_value(priv->avout_mute_desc) ?
"high" : "low");
}
}
#endif
return 0;
@@ -778,7 +799,6 @@ static void aml_init_work(struct work_struct *init_work)
struct aml_card_data, init_work);
dev = aml_priv_to_dev(priv);
np = dev->of_node;
aml_card_parse_gpios(np, priv);
}
@@ -823,6 +843,7 @@ static int aml_card_parse_of(struct device_node *node,
ret = aml_card_dai_link_of(np, priv,
i, false);
if (ret < 0) {
dev_err(dev, "parse dai_link-%d fail\n", i);
of_node_put(np);
goto card_parse_end;
}
@@ -876,6 +897,29 @@ static const struct of_device_id auge_of_match[] = {
};
MODULE_DEVICE_TABLE(of, auge_of_match);
static int card_suspend_pre(struct snd_soc_card *card)
{
struct aml_card_data *priv = snd_soc_card_get_drvdata(card);
priv->av_mute_enable = 1;
INIT_WORK(&priv->init_work, aml_init_work);
schedule_work(&priv->init_work);
pr_info("it is card_pre_suspend\n");
return 0;
}
static int card_resume_post(struct snd_soc_card *card)
{
struct aml_card_data *priv = snd_soc_card_get_drvdata(card);
priv->av_mute_enable = 0;
INIT_WORK(&priv->init_work, aml_init_work);
schedule_work(&priv->init_work);
pr_info("it is card_post_resume\n");
return 0;
}
static int aml_card_probe(struct platform_device *pdev)
{
struct aml_card_data *priv;
@@ -916,13 +960,17 @@ static int aml_card_probe(struct platform_device *pdev)
priv->snd_card.dev = dev;
priv->snd_card.dai_link = priv->dai_link;
priv->snd_card.num_links = num;
priv->snd_card.suspend_pre = card_suspend_pre;
priv->snd_card.resume_post = card_resume_post;
if (np && of_device_is_available(np)) {
ret = aml_card_parse_of(np, priv);
if (ret < 0) {
dev_err(dev, "%s, parse error %d\n",
__func__, ret);
dev_err(dev, "%s, aml_card_parse_of error %d %s\n",
__func__,
ret,
(ret == -EPROBE_DEFER) ? "PROBE RETRY" : "");
goto err;
}
@@ -960,6 +1008,7 @@ static int aml_card_probe(struct platform_device *pdev)
sizeof(priv->dai_props->codec_dai));
}
platform_set_drvdata(pdev, priv);
snd_soc_card_set_drvdata(&priv->snd_card, priv);
ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
@@ -986,7 +1035,7 @@ static int aml_card_probe(struct platform_device *pdev)
audio_jack_detect(priv);
audio_extcon_register(priv, dev);
}
priv->av_mute_enable = 0;
INIT_WORK(&priv->init_work, aml_init_work);
schedule_work(&priv->init_work);
@@ -1009,6 +1058,16 @@ static int aml_card_remove(struct platform_device *pdev)
return aml_card_clean_reference(card);
}
static void aml_card_platform_shutdown(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct aml_card_data *priv = snd_soc_card_get_drvdata(card);
priv->av_mute_enable = 1;
INIT_WORK(&priv->init_work, aml_init_work);
schedule_work(&priv->init_work);
}
static struct platform_driver aml_card = {
.driver = {
.name = "asoc-aml-card",
@@ -1017,6 +1076,7 @@ static struct platform_driver aml_card = {
},
.probe = aml_card_probe,
.remove = aml_card_remove,
.shutdown = aml_card_platform_shutdown,
};
module_platform_driver(aml_card);

View File

@@ -106,7 +106,6 @@ static struct toddr *register_toddr_l(struct device *dev,
/* (1 << 31)|(1 << mask_bit));*/
to->dev = dev;
to->actrl = actrl;
to->in_use = true;
pr_debug("toddrs[%d] registered by device %s\n", i, dev_name(dev));
return to;
@@ -147,7 +146,6 @@ static int unregister_toddr_l(struct device *dev, void *data)
free_irq(to->irq, data);
to->dev = NULL;
to->actrl = NULL;
to->in_use = false;
pr_debug("toddrs[%d] released by device %s\n", i, dev_name(dev));
@@ -901,7 +899,6 @@ static struct frddr *register_frddr_l(struct device *dev,
return NULL;
}
from->dev = dev;
from->actrl = actrl;
from->in_use = true;
pr_debug("frddrs[%d] registered by device %s\n", i, dev_name(dev));
return from;
@@ -942,7 +939,6 @@ static int unregister_frddr_l(struct device *dev, void *data)
free_irq(from->irq, data);
from->dev = NULL;
from->actrl = NULL;
from->in_use = false;
pr_debug("frddrs[%d] released by device %s\n", i, dev_name(dev));
return 0;
@@ -1019,10 +1015,6 @@ int aml_check_sharebuffer_valid(struct frddr *fr, int ss_sel)
if (frddrs[i].in_use
&& (frddrs[i].fifo_id != current_fifo_id)
&& (frddrs[i].dest == ss_sel)) {
pr_debug("%s, ss_sel:%d used, not for share buffer at same time\n",
__func__,
ss_sel);
ret = 0;
break;
}
@@ -1295,20 +1287,42 @@ void aml_frddr_set_format(struct frddr *fr,
static void aml_aed_enable(struct frddr_attach *p_attach_aed, bool enable)
{
struct frddr *fr = fetch_frddr_by_src(p_attach_aed->attach_module);
int aed_version = check_aed_version();
if (check_aed_v2()) {
if (aed_version == VERSION2 || aed_version == VERSION3) {
struct aml_audio_controller *actrl = fr->actrl;
unsigned int reg_base = fr->reg_base;
unsigned int reg;
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL2, reg_base);
aml_audiobus_update_bits(actrl,
reg, 0x1 << 3, enable << 3);
aed_set_ctrl(enable, 0, p_attach_aed->attach_module);
aed_set_format(fr->msb, fr->type, fr->fifo_id);
aed_enable(enable);
} else {
if (enable) {
aml_audiobus_update_bits(actrl,
reg, 0x1 << 3, enable << 3);
if (aed_version == VERSION3) {
aed_set_ctrl(enable, 0,
p_attach_aed->attach_module, 1);
aed_set_format(fr->msb,
fr->type, fr->fifo_id, 1);
} else {
aed_set_ctrl(enable, 0,
p_attach_aed->attach_module, 0);
aed_set_format(fr->msb,
fr->type, fr->fifo_id, 0);
}
aed_enable(enable);
} else {
aed_enable(enable);
if (aed_version == VERSION3) {
aed_set_ctrl(enable, 0,
p_attach_aed->attach_module, 1);
} else {
aed_set_ctrl(enable, 0,
p_attach_aed->attach_module, 0);
}
aml_audiobus_update_bits(actrl,
reg, 0x1 << 3, enable << 3);
}
} else if (aed_version == VERSION1) {
if (enable) {
/* frddr type and bit depth for AED */
aml_aed_format_set(fr->dest);
@@ -1479,7 +1493,8 @@ static int toddr_src_enum_set(struct snd_kcontrol *kcontrol,
static int frddr_src_idx = -1;
static const char *const frddr_src_sel_texts[] = {
"TDMOUT_A", "TDMOUT_B", "TDMOUT_C", "SPDIFOUT_A", "SPDIFOUT_B"
"TDMOUT_A", "TDMOUT_B", "TDMOUT_C",
"SPDIFOUT_A", "SPDIFOUT_B", "EARCTX_DMAC"
};
static const struct soc_enum frddr_output_source_enum =
@@ -1493,7 +1508,7 @@ int frddr_src_get(void)
const char *frddr_src_get_str(int idx)
{
if (idx < 0 || idx > 4)
if (idx < 0 || idx >= FRDDR_MAX)
return NULL;
return frddr_src_sel_texts[idx];
@@ -1627,77 +1642,93 @@ static struct notifier_block ddr_pm_notifier_block = {
.notifier_call = ddr_pm_event,
};
/* table Must in order */
static struct ddr_info ddr_info[] = {
{EE_AUDIO_TODDR_A_CTRL0, EE_AUDIO_FRDDR_A_CTRL0, "toddr_a", "frddr_a"},
{EE_AUDIO_TODDR_B_CTRL0, EE_AUDIO_FRDDR_B_CTRL0, "toddr_b", "frddr_b"},
{EE_AUDIO_TODDR_C_CTRL0, EE_AUDIO_FRDDR_C_CTRL0, "toddr_c", "frddr_c"},
{EE_AUDIO_TODDR_D_CTRL0, EE_AUDIO_FRDDR_D_CTRL0, "toddr_d", "frddr_d"},
};
static int ddr_get_toddr_base_addr_by_idx(int idx)
{
return ddr_info[idx].toddr_addr;
}
static int ddr_get_frddr_base_addr_by_idx(int idx)
{
return ddr_info[idx].frddr_addr;
}
static char *ddr_get_toddr_name_by_idx(int idx)
{
return ddr_info[idx].toddr_name;
}
static char *ddr_get_frddr_name_by_idx(int idx)
{
return ddr_info[idx].frddr_name;
}
static int aml_ddr_mngr_platform_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct device_node *node_prt = NULL;
struct platform_device *pdev_parent;
struct aml_audio_controller *actrl = NULL;
struct ddr_chipinfo *p_ddr_chipinfo;
int ddr_num = 3; /* early chipset support max 3 ddr num */
int i, ret;
/* get audio controller */
node_prt = of_get_parent(node);
if (!node_prt)
return -ENXIO;
pdev_parent = of_find_device_by_node(node_prt);
of_node_put(node_prt);
actrl = (struct aml_audio_controller *)
platform_get_drvdata(pdev_parent);
p_ddr_chipinfo = (struct ddr_chipinfo *)
of_device_get_match_data(&pdev->dev);
if (!p_ddr_chipinfo)
dev_warn_once(&pdev->dev,
"check whether to update ddr_mngr chipinfo\n");
/* irqs */
toddrs[DDR_A].irq = platform_get_irq_byname(pdev, "toddr_a");
toddrs[DDR_B].irq = platform_get_irq_byname(pdev, "toddr_b");
toddrs[DDR_C].irq = platform_get_irq_byname(pdev, "toddr_c");
frddrs[DDR_A].irq = platform_get_irq_byname(pdev, "frddr_a");
frddrs[DDR_B].irq = platform_get_irq_byname(pdev, "frddr_b");
frddrs[DDR_C].irq = platform_get_irq_byname(pdev, "frddr_c");
if (p_ddr_chipinfo
&& (p_ddr_chipinfo->fifo_num == 4)) {
toddrs[DDR_D].irq = platform_get_irq_byname(pdev, "toddr_d");
frddrs[DDR_D].irq = platform_get_irq_byname(pdev, "frddr_d");
if (toddrs[DDR_D].irq < 0 || frddrs[DDR_D].irq < 0)
dev_err(&pdev->dev, "check irq for DDR_D\n");
ddr_num = p_ddr_chipinfo->fifo_num;
if (!p_ddr_chipinfo) {
dev_err(&pdev->dev,
"check to update ddr_mngr chipinfo\n");
return -EINVAL;
}
if (p_ddr_chipinfo->fifo_num == 2)
ddr_num = p_ddr_chipinfo->fifo_num;
else if (p_ddr_chipinfo->fifo_num == 4)
ddr_num = p_ddr_chipinfo->fifo_num;
for (i = 0; i < ddr_num; i++) {
pr_info("%d, irqs toddr %d, frddr %d\n",
i, toddrs[i].irq, frddrs[i].irq);
toddrs[i].irq =
platform_get_irq_byname(pdev,
ddr_get_toddr_name_by_idx(i));
toddrs[i].reg_base = ddr_get_toddr_base_addr_by_idx(i);
toddrs[i].fifo_id = i;
toddrs[i].chipinfo = p_ddr_chipinfo;
toddrs[i].actrl = actrl;
frddrs[i].irq =
platform_get_irq_byname(pdev,
ddr_get_frddr_name_by_idx(i));
frddrs[i].reg_base = ddr_get_frddr_base_addr_by_idx(i);
frddrs[i].fifo_id = i;
frddrs[i].chipinfo = p_ddr_chipinfo;
frddrs[i].actrl = actrl;
dev_info(&pdev->dev, "%d, irqs toddr %d, frddr %d\n",
i, toddrs[i].irq, frddrs[i].irq);
if (toddrs[i].irq <= 0 || frddrs[i].irq <= 0) {
dev_err(&pdev->dev, "platform_get_irq_byname failed\n");
dev_err(&pdev->dev, "%s, get irq failed\n", __func__);
return -ENXIO;
}
}
/* inits */
toddrs[DDR_A].reg_base = EE_AUDIO_TODDR_A_CTRL0;
toddrs[DDR_B].reg_base = EE_AUDIO_TODDR_B_CTRL0;
toddrs[DDR_C].reg_base = EE_AUDIO_TODDR_C_CTRL0;
toddrs[DDR_A].fifo_id = DDR_A;
toddrs[DDR_B].fifo_id = DDR_B;
toddrs[DDR_C].fifo_id = DDR_C;
frddrs[DDR_A].reg_base = EE_AUDIO_FRDDR_A_CTRL0;
frddrs[DDR_B].reg_base = EE_AUDIO_FRDDR_B_CTRL0;
frddrs[DDR_C].reg_base = EE_AUDIO_FRDDR_C_CTRL0;
frddrs[DDR_A].fifo_id = DDR_A;
frddrs[DDR_B].fifo_id = DDR_B;
frddrs[DDR_C].fifo_id = DDR_C;
if (p_ddr_chipinfo) {
toddrs[DDR_A].chipinfo = p_ddr_chipinfo;
toddrs[DDR_B].chipinfo = p_ddr_chipinfo;
toddrs[DDR_C].chipinfo = p_ddr_chipinfo;
frddrs[DDR_A].chipinfo = p_ddr_chipinfo;
frddrs[DDR_B].chipinfo = p_ddr_chipinfo;
frddrs[DDR_C].chipinfo = p_ddr_chipinfo;
if (p_ddr_chipinfo->fifo_num == 4) {
toddrs[DDR_D].reg_base = EE_AUDIO_TODDR_D_CTRL0;
toddrs[DDR_D].fifo_id = DDR_D;
frddrs[DDR_D].reg_base = EE_AUDIO_FRDDR_D_CTRL0;
frddrs[DDR_D].fifo_id = DDR_D;
}
}
ret = register_pm_notifier(&ddr_pm_notifier_block);
if (ret)
pr_warn("[%s] failed to register PM notifier %d\n",

View File

@@ -90,6 +90,8 @@ enum frddr_dest {
TDMOUT_C,
SPDIFOUT_A,
SPDIFOUT_B,
EARCTX_DMAC,
FRDDR_MAX
};
enum status_sel {
@@ -228,6 +230,13 @@ struct frddr {
struct ddr_chipinfo *chipinfo;
};
struct ddr_info {
unsigned int toddr_addr;
unsigned int frddr_addr;
char *toddr_name;
char *frddr_name;
};
/* to ddrs */
struct toddr *fetch_toddr_by_src(int toddr_src);
struct toddr *aml_audio_register_toddr(struct device *dev,

File diff suppressed because it is too large Load Diff

View File

@@ -15,112 +15,583 @@
*
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/amlogic/media/sound/spdif_info.h>
#include "earc_hw.h"
void earcrx_cmdc_init(void)
void earcrx_cmdc_init(struct regmap *top_map)
{
/* set irq mask */
earcrx_top_write(EARCRX_CMDC_INT_MASK,
(0 << 15) | /* idle2_int */
(0 << 14) | /* idle1_int */
(0 << 13) | /* disc2_int */
(0 << 12) | /* disc1_int */
(0 << 11) | /* earc_int */
(1 << 10) | /* hb_status_int */
(0 << 9) | /* losthb_int */
(0 << 8) | /* timeout_int */
(0 << 7) | /* status_ch_int */
(0 << 6) | /* int_rec_invalid_id */
(0 << 5) | /* int_rec_invalid_offset */
(0 << 4) | /* int_rec_unexp */
(0 << 3) | /* int_rec_ecc_err */
(0 << 2) | /* int_rec_parity_err */
(0 << 1) | /* int_recv_packet */
(0 << 0) /* int_rec_time_out */
);
mmio_write(top_map, EARCRX_CMDC_INT_MASK,
(1 << 15) | /* idle2_int */
(1 << 14) | /* idle1_int */
(1 << 13) | /* disc2_int */
(1 << 12) | /* disc1_int */
(1 << 11) | /* earc_int */
(1 << 10) | /* hb_status_int */
(1 << 9) | /* losthb_int */
(1 << 8) | /* timeout_int */
(0 << 7) | /* status_ch_int */
(0 << 6) | /* int_rec_invalid_id */
(0 << 5) | /* int_rec_invalid_offset */
(0 << 4) | /* int_rec_unexp */
(0 << 3) | /* int_rec_ecc_err */
(0 << 2) | /* int_rec_parity_err */
(0 << 1) | /* int_recv_packet */
(0 << 0) /* int_rec_time_out */
);
earcrx_top_write(EARCRX_ANA_CTRL0, 0x90884814);
earcrx_top_write(EARCRX_PLL_CTRL3, 0x242000);
earcrx_top_write(EARCRX_PLL_CTRL0, 0x10800400);
mmio_write(top_map, EARCRX_ANA_CTRL0,
0x1 << 31 | /* earcrx_en_d2a */
0x10 << 24 | /* earcrx_cmdcrx_reftrim */
0x8 << 20 | /* earcrx_idr_trim */
0x10 << 15 | /* earcrx_rterm_trim */
0x4 << 12 | /* earcrx_cmdctx_ack_hystrim */
0x10 << 7 | /* earcrx_cmdctx_ack_reftrim */
0x1 << 4 | /* earcrx_cmdcrx_rcfilter_sel */
0x4 << 0 /* earcrx_cmdcrx_hystrim */
);
mmio_write(top_map, EARCRX_PLL_CTRL3,
0x2 << 20 | /* earcrx_pll_bias_adj */
0x4 << 16 | /* earcrx_pll_rou */
0x1 << 13 /* earcrx_pll_dco_sdm_e */
);
mmio_write(top_map, EARCRX_PLL_CTRL0,
0x1 << 28 | /* earcrx_pll_en */
0x1 << 23 | /* earcrx_pll_dmacrx_sqout_rstn_sel */
0x1 << 10 /* earcrx_pll_n */
);
}
void earcrx_dmac_init(void)
void earcrx_cmdc_arc_connect(struct regmap *cmdc_map, bool init)
{
earcrx_dmac_write(EARCRX_DMAC_SYNC_CTRL0,
(1 << 16) | /* reg_ana_buf_data_sel_en */
(3 << 12) | /* reg_ana_buf_data_sel */
(7 << 8) | /* reg_ana_clr_cnt */
(7 << 4) /* reg_ana_set_cnt */
);
earcrx_dmac_write(EARCRX_DMAC_UBIT_CTRL0,
(47 << 16) | /* reg_fifo_thd */
(1 << 12) | /* reg_user_lr */
(29 << 0) /* reg_data_bit */
);
earcrx_dmac_write(EARCRX_ANA_RST_CTRL0, 1 << 31);
if (init)
mmio_update_bits(cmdc_map,
EARC_RX_CMDC_VSM_CTRL0,
0x7 << 25,
0x1 << 27 | /* arc_initiated */
0x0 << 26 | /* arc_terminated */
0x1 << 25 /* arc_enable */
);
else
mmio_update_bits(cmdc_map,
EARC_RX_CMDC_VSM_CTRL0,
0x7 << 25,
0x0 << 27 | /* arc_initiated */
0x1 << 26 | /* arc_terminated */
0x0 << 25 /* arc_enable */
);
}
void earc_arc_init(void)
void earcrx_cmdc_hpd_detect(struct regmap *cmdc_map, bool st)
{
earcrx_dmac_write(EARCRX_SPDIFIN_CTRL0,
(1 << 31) | /* reg_work_en */
(1 << 30) | /* reg_chnum_sel */
(1 << 25) | /* reg_findpapb_en */
(0xFFF<<12) /* reg_nonpcm2pcm_th */
);
earcrx_dmac_write(EARCRX_SPDIFIN_CTRL2,
(1 << 14) | /* reg_earc_auto */
(1 << 13) /* reg_earcin_papb_lr */
);
earcrx_dmac_write(EARCRX_SPDIFIN_CTRL3,
(0xEC37<<16) | /* reg_earc_pa_value */
(0x5A5A<<0) /* reg_earc_pb_value */
);
}
if (st) {
mmio_update_bits(cmdc_map,
EARC_RX_CMDC_VSM_CTRL0,
0x1 << 19,
0x1 << 19 /* comma_cnt_rst */
);
void earc_rx_enable(bool enable)
{
if (enable) {
earcrx_dmac_update_bits(EARCRX_DMAC_SYNC_CTRL0,
1 << 30, /* reg_rst_afifo_out_n */
1 << 30);
earcrx_dmac_update_bits(EARCRX_DMAC_SYNC_CTRL0,
1 << 29, /* reg_rst_afifo_in_n */
0x1 << 29);
earcrx_dmac_update_bits(EARCRX_ERR_CORRECT_CTRL0,
1 << 29, /* reg_rst_afifo_out_n */
1 << 29
);
earcrx_dmac_update_bits(EARCRX_ERR_CORRECT_CTRL0,
1 << 28, /* reg_rst_afifo_in_n */
1 << 28 /* reg_rst_afifo_in_n */
);
mmio_update_bits(cmdc_map,
EARC_RX_CMDC_VSM_CTRL0,
0x1 << 19 | 0xff << 0,
0x1 << 19 | /* comma_cnt_rst */
0xff << 0
);
} else {
earcrx_dmac_update_bits(EARCRX_DMAC_SYNC_CTRL0,
0x3 << 29,
0x0 << 29);
/* soft reset */
mmio_update_bits(cmdc_map,
EARC_RX_CMDC_TOP_CTRL1,
0xf << 0,
0xf << 0);
mmio_update_bits(cmdc_map,
EARC_RX_CMDC_TOP_CTRL1,
0xf << 0,
0x0 << 0);
}
}
earcrx_dmac_update_bits(EARCRX_ERR_CORRECT_CTRL0,
0x3 << 28, 0x0 << 28);
void earcrx_dmac_init(struct regmap *top_map, struct regmap *dmac_map)
{
mmio_write(top_map, EARCRX_DMAC_INT_MASK,
(0x0 << 17) | /* earcrx_ana_rst c_new_format_set */
(0x0 << 16) | /* earcrx_ana_rst c_earcrx_div2_hold_set */
(0x0 << 15) | /* earcrx_err_correct c_bcherr_int_set */
(0x0 << 14) | /* earcrx_err_correct r_afifo_overflow_set */
(0x0 << 13) | /* earcrx_err_correct r_fifo_overflow_set */
(0x0 << 12) | /* earcrx_user_bit_check r_fifo_overflow */
(0x0 << 11) | /* earcrx_user_bit_check c_fifo_thd_pass */
(0x0 << 10) | /* earcrx_user_bit_check c_u_pk_lost_int_set */
(0x0 << 9) | /* arcrx_user_bit_check c_iu_pk_end */
(0x0 << 8) | /* arcrx_biphase_decode c_chst_mute_clr */
(0x1 << 7) | /* arcrx_biphase_decode c_find_papb */
(0x1 << 6) | /* arcrx_biphase_decode c_valid_change */
(0x1 << 5) | /* arcrx_biphase_decode c_find_nonpcm2pcm */
(0x1 << 4) | /* arcrx_biphase_decode c_pcpd_change */
(0x1 << 3) | /* arcrx_biphase_decode c_ch_status_change */
(0x1 << 2) | /* arcrx_biphase_decode sample_mod_change */
(0x1 << 1) | /* arcrx_biphase_decode r_parity_err */
(0x0 << 0) /* arcrx_dmac_sync afifo_overflow */
);
mmio_write(dmac_map, EARCRX_DMAC_SYNC_CTRL0,
(1 << 16) | /* reg_ana_buf_data_sel_en */
(3 << 12) | /* reg_ana_buf_data_sel */
(7 << 8) | /* reg_ana_clr_cnt */
(7 << 4) /* reg_ana_set_cnt */
);
mmio_write(dmac_map, EARCRX_DMAC_UBIT_CTRL0,
(47 << 16) | /* reg_fifo_thd */
(1 << 12) | /* reg_user_lr */
(29 << 0) /* reg_data_bit */
);
mmio_write(dmac_map, EARCRX_ANA_RST_CTRL0, 1 << 31);
}
void earcrx_arc_init(struct regmap *dmac_map)
{
unsigned int spdifin_clk = 500000000;
/* sysclk/rate/32(bit)/2(ch)/2(bmc) */
unsigned int counter_32k = (spdifin_clk / (32000 * 64));
unsigned int counter_44k = (spdifin_clk / (44100 * 64));
unsigned int counter_48k = (spdifin_clk / (48000 * 64));
unsigned int counter_88k = (spdifin_clk / (88200 * 64));
unsigned int counter_96k = (spdifin_clk / (96000 * 64));
unsigned int counter_176k = (spdifin_clk / (176400 * 64));
unsigned int counter_192k = (spdifin_clk / (192000 * 64));
unsigned int mode0_th = 3 * (counter_32k + counter_44k) >> 1;
unsigned int mode1_th = 3 * (counter_44k + counter_48k) >> 1;
unsigned int mode2_th = 3 * (counter_48k + counter_88k) >> 1;
unsigned int mode3_th = 3 * (counter_88k + counter_96k) >> 1;
unsigned int mode4_th = 3 * (counter_96k + counter_176k) >> 1;
unsigned int mode5_th = 3 * (counter_176k + counter_192k) >> 1;
unsigned int mode0_timer = counter_32k >> 1;
unsigned int mode1_timer = counter_44k >> 1;
unsigned int mode2_timer = counter_48k >> 1;
unsigned int mode3_timer = counter_88k >> 1;
unsigned int mode4_timer = counter_96k >> 1;
unsigned int mode5_timer = (counter_176k >> 1);
unsigned int mode6_timer = (counter_192k >> 1);
mmio_write(dmac_map,
EARCRX_SPDIFIN_SAMPLE_CTRL0,
0x0 << 28 | /* detect by max_width */
(spdifin_clk / 10000) << 0 /* base timer */
);
mmio_write(dmac_map,
EARCRX_SPDIFIN_SAMPLE_CTRL1,
mode0_th << 20 |
mode1_th << 10 |
mode2_th << 0);
mmio_write(dmac_map,
EARCRX_SPDIFIN_SAMPLE_CTRL2,
mode3_th << 20 |
mode4_th << 10 |
mode5_th << 0);
mmio_write(dmac_map,
EARCRX_SPDIFIN_SAMPLE_CTRL3,
(mode0_timer << 24) |
(mode1_timer << 16) |
(mode2_timer << 8) |
(mode3_timer << 0)
);
mmio_write(dmac_map,
EARCRX_SPDIFIN_SAMPLE_CTRL4,
(mode4_timer << 24) |
(mode5_timer << 16) |
(mode6_timer << 8)
);
mmio_write(dmac_map,
EARCRX_SPDIFIN_CTRL0,
0x1 << 31 | /* reg_work_en */
0x1 << 30 | /* reg_chnum_sel */
0x1 << 25 | /* reg_findpapb_en */
0x1 << 24 | /* nonpcm2pcm_th enable */
0xFFF << 12 /* reg_nonpcm2pcm_th */
);
mmio_write(dmac_map,
EARCRX_SPDIFIN_CTRL2,
(1 << 14) | /* reg_earc_auto */
(1 << 13) /* reg_earcin_papb_lr */
);
mmio_write(dmac_map,
EARCRX_SPDIFIN_CTRL3,
(0xEC37 << 16) | /* reg_earc_pa_value */
(0x5A5A << 0) /* reg_earc_pb_value */
);
}
enum cmdc_st earcrx_cmdc_get_state(struct regmap *cmdc_map)
{
int val = mmio_read(cmdc_map, EARC_RX_CMDC_STATUS0);
enum cmdc_st state = (enum cmdc_st)(val & 0x7);
return state;
}
enum attend_type earcrx_cmdc_get_attended_type(struct regmap *cmdc_map)
{
int val = mmio_read(cmdc_map, EARC_RX_CMDC_STATUS0);
enum cmdc_st state = (enum cmdc_st)(val & 0x7);
enum attend_type type = ATNDTYP_DISCNCT;
if ((val & (1 << 0x3)) && (state == CMDC_ST_ARC))
type = ATNDTYP_ARC;
else if ((val & (1 << 0x4)) && (state == CMDC_ST_EARC))
type = ATNDTYP_EARC;
return type;
}
void earcrx_cdmc_clr_irqs(struct regmap *top_map, int clr)
{
mmio_write(top_map, EARCRX_CMDC_INT_PENDING, clr);
}
int earcrx_cdmc_get_irqs(struct regmap *top_map)
{
return mmio_read(top_map, EARCRX_CMDC_INT_PENDING);
}
void earcrx_dmac_clr_irqs(struct regmap *top_map, int clr)
{
mmio_write(top_map, EARCRX_DMAC_INT_PENDING, clr);
}
int earcrx_dmac_get_irqs(struct regmap *top_map)
{
return mmio_read(top_map, EARCRX_DMAC_INT_PENDING);
}
void earcrx_enable(struct regmap *cmdc_map,
struct regmap *dmac_map, bool enable)
{
enum attend_type type = earcrx_cmdc_get_attended_type(cmdc_map);
if (enable) {
mmio_update_bits(dmac_map, EARCRX_DMAC_SYNC_CTRL0,
1 << 30, /* reg_rst_afifo_out_n */
1 << 30);
mmio_update_bits(dmac_map, EARCRX_DMAC_SYNC_CTRL0,
1 << 29, /* reg_rst_afifo_in_n */
0x1 << 29);
mmio_update_bits(dmac_map, EARCRX_ERR_CORRECT_CTRL0,
1 << 29, /* reg_rst_afifo_out_n */
1 << 29
);
mmio_update_bits(dmac_map, EARCRX_ERR_CORRECT_CTRL0,
1 << 28, /* reg_rst_afifo_in_n */
1 << 28 /* reg_rst_afifo_in_n */
);
} else {
mmio_update_bits(dmac_map, EARCRX_DMAC_SYNC_CTRL0,
0x3 << 29,
0x0 << 29);
mmio_update_bits(dmac_map, EARCRX_ERR_CORRECT_CTRL0,
0x3 << 28, 0x0 << 28);
}
earcrx_dmac_update_bits(EARCRX_DMAC_SYNC_CTRL0,
1 << 31, /* reg_work_en */
enable << 31);
if (type == ATNDTYP_EARC)
mmio_update_bits(dmac_map, EARCRX_DMAC_SYNC_CTRL0,
1 << 31, /* reg_work_en */
enable << 31);
else if (type == ATNDTYP_ARC) {
mmio_update_bits(dmac_map,
EARCRX_SPDIFIN_SAMPLE_CTRL0,
0x1 << 31, /* reg_work_enable */
enable << 31);
earcrx_dmac_update_bits(EARCRX_DMAC_UBIT_CTRL0,
1 << 31, /* reg_work_enable */
enable << 31);
mmio_write(dmac_map, EARCRX_DMAC_SYNC_CTRL0, 0x0);
}
earcrx_dmac_update_bits(EARCRX_ERR_CORRECT_CTRL0,
1 << 31,
enable << 31); /* reg_work_en */
mmio_update_bits(dmac_map, EARCRX_DMAC_UBIT_CTRL0,
1 << 31, /* reg_work_enable */
enable << 31);
earcrx_dmac_update_bits(EARCRX_DMAC_TOP_CTRL0,
1 << 31,
enable << 31); /* reg_top_work_en */
mmio_update_bits(dmac_map, EARCRX_ERR_CORRECT_CTRL0,
1 << 31,
enable << 31); /* reg_work_en */
mmio_update_bits(dmac_map, EARCRX_DMAC_TOP_CTRL0,
1 << 31,
enable << 31); /* reg_top_work_en */
}
void earctx_cmdc_int_mask(struct regmap *top_map)
{
mmio_write(top_map, EARCTX_CMDC_INT_MASK,
(0x0 << 17) | /* hpd_high_int */
(0x0 << 16) | /* hpd_low_int */
(0x1 << 15) | /* idle2_int */
(0x1 << 14) | /* idle1_int */
(0x1 << 13) | /* disc2_int */
(0x1 << 12) | /* disc1_int */
(0x1 << 11) | /* earc_int */
(0x0 << 10) | /* hb_status_int */
(0x1 << 9) | /* losthb_int */
(0x1 << 8) | /* timeout_int */
(0x0 << 7) | /* status_ch_int */
(0x0 << 6) | /* int_recv_finished */
(0x0 << 5) | /* int_recv_nack */
(0x0 << 4) | /* int_recv_norsp */
(0x0 << 3) | /* int_recv_unexp */
(0x0 << 2) | /* int_recv_data */
(0x0 << 1) | /* int_recv_ack */
(0x0 << 0) /* int_recv_ecc_err */
);
}
void earctx_cmdc_init(struct regmap *top_map)
{
/* ana */
mmio_write(top_map, EARCTX_ANA_CTRL0,
0x1 << 31 | /* earctx_en_d2a */
0x1 << 28 | /* earctx_cmdcrx_rcfilter_sel */
0x4 << 26 | /* earctx_cmdcrx_hystrim */
0x8 << 20 | /* earctx_idr_trim */
0x10 << 12 | /* earctx_rterm_trim */
0x4 << 8 | /* earctx_dmac_slew_con */
0x4 << 5 | /* earctx_cmdctx_ack_hystrim */
0x10 << 0 /* earctx_cmdctx_ack_reftrim */
);
}
void earctx_cmdc_arc_connect(struct regmap *cmdc_map, bool init)
{
if (init)
mmio_update_bits(cmdc_map,
EARC_TX_CMDC_VSM_CTRL0,
0x7 << 25,
0x1 << 27 | /* arc_initiated */
0x0 << 26 | /* arc_terminated */
0x1 << 25 /* arc_enable */
);
else
mmio_update_bits(cmdc_map,
EARC_TX_CMDC_VSM_CTRL0,
0x7 << 25,
0x0 << 27 | /* arc_initiated */
0x1 << 26 | /* arc_terminated */
0x0 << 25 /* arc_enable */
);
}
void earctx_cmdc_hpd_detect(struct regmap *top_map,
struct regmap *cmdc_map,
int earc_port, bool st)
{
/* select hdmirx_hpd */
mmio_update_bits(cmdc_map,
EARC_TX_CMDC_VSM_CTRL1,
0x1 << 8, /* cntl_hpd_sel */
0x1 << 8);
/* select hdmi_hpd mux */
mmio_update_bits(top_map, EARCTX_TOP_CTRL0,
0xf << 8,
0x1 << 11 | /* hdmi_hpd invent */
earc_port << 8 /* hdmi_hpd mux, port 0/1/2 */
);
if (st) {
mmio_update_bits(cmdc_map,
EARC_TX_CMDC_VSM_CTRL0,
0x1 << 19,
0x1 << 19 /* comma_cnt_rst */
);
mmio_update_bits(cmdc_map,
EARC_TX_CMDC_VSM_CTRL0,
0x1 << 19 | 0xf << 20,
0x0 << 19 | /* comma_cnt_rst */
0x3 << 20 | 0x3 << 22
);
/* no timeout */
mmio_update_bits(cmdc_map,
EARC_TX_CMDC_VSM_CTRL5,
0x3 << 0,
0x1 << 1
);
mmio_update_bits(cmdc_map,
EARC_TX_CMDC_VSM_CTRL1,
0xff << 0,
0xa << 0 /* comma_cnt_th */
);
} else {
/* soft reset */
mmio_update_bits(cmdc_map,
EARC_TX_CMDC_TOP_CTRL1,
0xf << 0,
0xf << 0);
mmio_update_bits(cmdc_map,
EARC_TX_CMDC_TOP_CTRL1,
0xf << 0,
0x0 << 0);
}
}
void earctx_dmac_init(struct regmap *top_map, struct regmap *dmac_map)
{
mmio_update_bits(dmac_map, EARCTX_SPDIFOUT_CTRL0,
0x3 << 28,
0x0 << 28);
mmio_update_bits(dmac_map, EARCTX_SPDIFOUT_CTRL0,
0x1 << 29, /* afifo out reset */
0x1 << 29);
mmio_update_bits(dmac_map, EARCTX_SPDIFOUT_CTRL0,
0x1 << 28 | /* afifo in reset */
0x1 << 26 | /* user Bit select */
0x1 << 24 | /* chdata select*/
0x1 << 20 | /* reg_data_sel, 1: data from 27bit */
0x1 << 19 | /* 0: lsb first */
0x1 << 18 | /* biphase encode valid Bit value sel */
0xff << 4, /* lane mask */
0x1 << 28 |
0x1 << 26 |
0x1 << 24 |
0x1 << 20 |
0x0 << 19 |
0x1 << 18 |
0x3 << 4 // ODO: lane 0 now
);
mmio_update_bits(dmac_map, EARCTX_ERR_CORRT_CTRL0,
0x1 << 16 | /* reg_ubit_fifo_init_n */
0x7 << 8 | /* r channel select */
0x7 << 4, /* l channel select */
0x1 << 16 |
0x1 << 8 |
0x0 << 4);
mmio_update_bits(dmac_map, EARCTX_ERR_CORRT_CTRL4,
0xf << 17,
0x1 << 19 | /* userBit sel, 1: reg_value */
0x1 << 18 | /* validBit sel, 1: reg_value */
0x1 << 17 /* reg_chst_sel, 1: reg_value */
);
}
void earctx_dmac_set_format(struct regmap *dmac_map,
int frddr_idx, int msb, int frddr_type)
{
mmio_update_bits(dmac_map, EARCTX_SPDIFOUT_CTRL1,
0x7 << 24 | /* frddr src */
0xff << 16 | /* waiting count after enabled */
0x1f << 8 | /* msb position */
0x7 << 4, /* frddr type */
frddr_idx << 24 |
0x40 << 16 |
msb << 8 |
frddr_type << 4);
mmio_update_bits(dmac_map, EARCTX_ERR_CORRT_CTRL4,
0x1f << 25,
0x1f << 25 /* msb */
);
/* for raw data */
mmio_update_bits(dmac_map, EARCTX_ERR_CORRT_CTRL3,
0x1f << 24,
0x17 << 24
);
}
void earctx_set_channel_status_info(struct regmap *dmac_map,
struct iec958_chsts *chsts)
{
/* ch status = reg_chsts0~B */
/* L Channel */
mmio_write(dmac_map, EARCTX_SPDIFOUT_CHSTS0,
((chsts->chstat1_l >> 8) & 0xf) << 24 | chsts->chstat0_l);
/* R Channel */
mmio_write(dmac_map, EARCTX_SPDIFOUT_CHSTS6,
((chsts->chstat1_r >> 8) & 0xf) << 24 | chsts->chstat0_r);
}
enum attend_type earctx_cmdc_get_attended_type(struct regmap *cmdc_map)
{
int val = mmio_read(cmdc_map, EARC_TX_CMDC_STATUS0);
enum cmdc_st state = (enum cmdc_st)(val & 0x7);
enum attend_type tx_type = ATNDTYP_DISCNCT;
if ((val & (1 << 0x3)) && (state == CMDC_ST_ARC))
tx_type = ATNDTYP_ARC;
else if ((val & (1 << 0x4)) && (state == CMDC_ST_EARC))
tx_type = ATNDTYP_EARC;
return tx_type;
}
void earctx_cdmc_clr_irqs(struct regmap *top_map, int clr)
{
mmio_write(top_map, EARCTX_CMDC_INT_PENDING, clr);
}
int earctx_cdmc_get_irqs(struct regmap *top_map)
{
return mmio_read(top_map, EARCTX_CMDC_INT_PENDING);
}
void earctx_dmac_clr_irqs(struct regmap *top_map, int clr)
{
mmio_write(top_map, EARCTX_DMAC_INT_PENDING, clr);
}
int earctx_dmac_get_irqs(struct regmap *top_map)
{
return mmio_read(top_map, EARCTX_DMAC_INT_PENDING);
}
void earctx_enable(struct regmap *top_map,
struct regmap *cmdc_map,
struct regmap *dmac_map,
bool enable)
{
mmio_update_bits(dmac_map, EARCTX_SPDIFOUT_CTRL0,
0x1 << 31,
enable << 31);
mmio_update_bits(dmac_map, EARC_RX_CMDC_BIPHASE_CTRL1,
0x1 << 30,
enable << 30);
if (enable)
mmio_write(top_map, EARCTX_DMAC_INT_MASK,
(0x1 << 4) | /* fe_modulation c_hold_clr */
(0x1 << 3) | /* fe_modulation c_hold_start */
(0x1 << 2) | /* err_correct c_fifo_thd_less_pass */
(0x1 << 1) | /* err_correct r_fifo_overflow_set */
(0x1 << 0) /* err_correct c_fifo_empty_set */
);
else
mmio_write(top_map, EARCTX_DMAC_INT_MASK,
0x0
);
if (earctx_cmdc_get_attended_type(cmdc_map) == ATNDTYP_EARC) {
if (spdifout_is_raw()) {
mmio_update_bits(dmac_map, EARCTX_ERR_CORRT_CTRL3,
0x1 << 29,
enable << 29);
}
mmio_update_bits(dmac_map, EARCTX_FE_CTRL0,
0x1 << 30,
enable << 30);
}
}

View File

@@ -19,9 +19,126 @@
#include "regs.h"
#include "iomap.h"
#include <linux/amlogic/media/sound/iomapres.h>
#include <linux/amlogic/media/sound/spdif_info.h>
#define INT_EARCRX_CMDC_IDLE2 (0x1 << 15)
#define INT_EARCRX_CMDC_IDLE1 (0x1 << 14)
#define INT_EARCRX_CMDC_DISC2 (0x1 << 13)
#define INT_EARCRX_CMDC_DISC1 (0x1 << 12)
#define INT_EARCRX_CMDC_EARC (0x1 << 11)
#define INT_EARCRX_CMDC_HB_STATUS (0x1 << 10)
#define INT_EARCRX_CMDC_LOSTHB (0x1 << 9)
#define INT_EARCRX_CMDC_TIMEOUT (0x1 << 8)
#define INT_EARCRX_CMDC_STATUS_CH (0x1 << 7)
#define INT_EARCRX_CMDC_REC_INVALID_ID (0x1 << 6)
#define INT_EARCRX_CMDC_REC_INVALID_OFFSET (0x1 << 5)
#define INT_EARCRX_CMDC_REC_UNEXP (0x1 << 4)
#define INT_EARCRX_CMDC_REC_ECC_ERR (0x1 << 3)
#define INT_EARCRX_CMDC_REC_PARITY_ERR (0x1 << 2)
#define INT_EARCRX_CMDC_RECV_PACKET (0x1 << 1)
#define INT_EARCRX_CMDC_REC_TIME_OUT (0x1 << 0)
#define INT_EARCRX_ANA_RST_C_NEW_FORMAT_SET (0x1 << 17)
#define INT_EARCRX_ANA_RST_C_EARCRX_DIV2_HOLD_SET (0x1 << 16)
#define INT_EARCRX_ERR_CORRECT_C_BCHERR_INT_SET (0x1 << 15)
#define INT_EARCRX_ERR_CORRECT_R_AFIFO_OVERFLOW_SET (0x1 << 14)
#define INT_EARCRX_ERR_CORRECT_R_FIFO_OVERFLOW_SET (0x1 << 13)
#define INT_EARCRX_USER_BIT_CHECK_R_FIFO_OVERFLOW (0x1 << 12)
#define INT_EARCRX_USER_BIT_CHECK_C_FIFO_THD_PASS (0x1 << 11)
#define INT_EARCRX_USER_BIT_CHECK_C_U_PK_LOST_INT_SET (0x1 << 10)
#define INT_ARCRX_USER_BIT_CHECK_C_IU_PK_END (0x1 << 9)
#define INT_ARCRX_BIPHASE_DECODE_C_CHST_MUTE_CLR (0x1 << 8)
#define INT_ARCRX_BIPHASE_DECODE_C_FIND_PAPB (0x1 << 7)
#define INT_ARCRX_BIPHASE_DECODE_C_VALID_CHANGE (0x1 << 6)
#define INT_ARCRX_BIPHASE_DECODE_C_FIND_NONPCM2PCM (0x1 << 5)
#define INT_ARCRX_BIPHASE_DECODE_C_PCPD_CHANGE (0x1 << 4)
#define INT_ARCRX_BIPHASE_DECODE_C_CH_STATUS_CHANGE (0x1 << 3)
#define INT_ARCRX_BIPHASE_DECODE_I_SAMPLE_MODE_CHANGE (0x1 << 2)
#define INT_ARCRX_BIPHASE_DECODE_R_PARITY_ERR (0x1 << 1)
#define INT_ARCRX_DMAC_SYNC_AFIFO_OVERFLOW (0x1 << 0)
#define INT_EARCTX_CMDC_HPD_H (0x1 << 17)
#define INT_EARCTX_CMDC_HPD_L (0x1 << 16)
#define INT_EARCTX_CMDC_IDLE2 (0x1 << 15)
#define INT_EARCTX_CMDC_IDLE1 (0x1 << 14)
#define INT_EARCTX_CMDC_DISC2 (0x1 << 13)
#define INT_EARCTX_CMDC_DISC1 (0x1 << 12)
#define INT_EARCTX_CMDC_EARC (0x1 << 11)
#define INT_EARCTX_CMDC_HB_STATUS (0x1 << 10)
#define INT_EARCTX_CMDC_LOSTHB (0x1 << 9)
#define INT_EARCTX_CMDC_TIMEOUT (0x1 << 8)
#define INT_EARCTX_CMDC_STATUS_CH (0x1 << 7)
#define INT_EARCTX_CMDC_RECV_FINISHED (0x1 << 6)
#define INT_EARCTX_CMDC_RECV_NACK (0x1 << 5)
#define INT_EARCTX_CMDC_RECV_NORSP (0x1 << 4)
#define INT_EARCTX_CMDC_RECV_UNEXP (0x1 << 3)
#define INT_EARCTX_CMDC_RECV_DATA (0x1 << 2)
#define INT_EARCTX_CMDC_RECV_ACK (0x1 << 1)
#define INT_EARCTX_CMDC_RECV_ECC_ERR (0x1 << 0)
#define INT_EARCTX_FEM_C_HOLD_CLR (0x1 << 4)
#define INT_EARCTX_FEM_C_HOLD_START (0x1 << 3)
#define INT_EARCTX_ERRCORR_C_FIFO_THD_LESS_PASS (0x1 << 2)
#define INT_EARCTX_ERRCORR_C_FIFO_OVERFLOW (0x1 << 1)
#define INT_EARCTX_ERRCORR_C_FIFO_EMPTY (0x1 << 0)
/* cmdc discovery and disconnect state */
enum cmdc_st {
CMDC_ST_OFF,
CMDC_ST_IDLE1,
CMDC_ST_IDLE2,
CMDC_ST_DISC1,
CMDC_ST_DISC2,
CMDC_ST_EARC,
CMDC_ST_ARC
};
/* attended type: disconect, ARC, eARC */
enum attend_type {
ATNDTYP_DISCNCT,
ATNDTYP_ARC,
ATNDTYP_EARC
};
enum tx_hd_hdp_mux {
GPIOW_1,
GPIOW_9,
GPIOW_5
};
void earcrx_cmdc_init(struct regmap *top_map);
void earcrx_cmdc_arc_connect(struct regmap *cmdc_map, bool init);
void earcrx_cmdc_hpd_detect(struct regmap *cmdc_map, bool st);
void earcrx_dmac_init(struct regmap *top_map, struct regmap *dmac_map);
void earcrx_arc_init(struct regmap *dmac_map);
enum cmdc_st earcrx_cmdc_get_state(struct regmap *cmdc_map);
enum attend_type earcrx_cmdc_get_attended_type(struct regmap *cmdc_map);
void earcrx_cdmc_clr_irqs(struct regmap *top_map, int clr);
int earcrx_cdmc_get_irqs(struct regmap *top_map);
void earcrx_dmac_clr_irqs(struct regmap *top_map, int clr);
int earcrx_dmac_get_irqs(struct regmap *top_map);
void earcrx_enable(struct regmap *cmdc_map,
struct regmap *dmac_map, bool enable);
void earctx_cmdc_int_mask(struct regmap *top_map);
void earctx_cmdc_init(struct regmap *top_map);
void earctx_cmdc_arc_connect(struct regmap *cmdc_map, bool init);
void earctx_cmdc_hpd_detect(struct regmap *top_map,
struct regmap *cmdc_map,
int earc_port, bool st);
void earctx_dmac_init(struct regmap *top_map, struct regmap *dmac_map);
void earctx_dmac_set_format(struct regmap *dmac_map,
int frddr_idx, int msb, int frddr_type);
void earctx_set_channel_status_info(struct regmap *dmac_map,
struct iec958_chsts *chsts);
void earctx_cdmc_clr_irqs(struct regmap *top_map, int clr);
int earctx_cdmc_get_irqs(struct regmap *top_map);
void earctx_dmac_clr_irqs(struct regmap *top_map, int clr);
int earctx_dmac_get_irqs(struct regmap *top_map);
void earctx_enable(struct regmap *top_map,
struct regmap *cmdc_map,
struct regmap *dmac_map,
bool enable);
extern void earcrx_cmdc_init(void);
extern void earcrx_dmac_init(void);
extern void earc_arc_init(void);
extern void earc_rx_enable(bool enable);
#endif

View File

@@ -235,7 +235,35 @@ void aed_set_lane_and_channels(int lane_mask, int ch_mask)
0xff << 12, (ch_num - 1) << 12);
}
void aed_set_ctrl(bool enable, int sel, enum frddr_dest module)
/* max 16ch, 8 lane*/
void aed_set_lane_and_channels_v3(int lane_mask, int ch_mask)
{
int ch_num = 0, lane_num = 0, i = 0;
int val_ch = ch_mask & 0xffff;
int val_lane = lane_mask & 0xff;
for (i = 0; i < 16; i++) {
if ((val_ch & 0x1) == 0x1)
ch_num++;
if ((val_lane & 0x1) == 0x1)
lane_num = i;
val_ch >>= 1;
val_lane >>= 1;
}
eqdrc_update_bits(AED_TOP_CTL1,
0xf << 20 | 0xf << 16 | 0x1 << 5 | 0x1 << 4 | 0xf,
(lane_num * 2 + 1) << 20 | (lane_num * 2) << 16 |
0x1 << 5 | 0x1 << 4 | (ch_num - 1));
eqdrc_update_bits(AED_TOP_CTL2,
0xff << 12, (ch_num - 1) << 12);
/*pr_info("ch_num = %d, lane_num = %d", ch_num, lane_num);*/
}
void aed_set_ctrl(bool enable, int sel, enum frddr_dest module, int offset)
{
int mask = 0, val = 0;
@@ -258,7 +286,8 @@ void aed_set_ctrl(bool enable, int sel, enum frddr_dest module)
return;
}
eqdrc_update_bits(AED_TOP_REQ_CTL, mask, val);
/*AED_TOP_REQ_CTL or AED_TOP_CTL2*/
eqdrc_update_bits((AED_TOP_REQ_CTL + offset), mask, val);
/* Effect Module */
if (module <= TDMOUT_C && module >= TDMOUT_A) {
@@ -271,11 +300,12 @@ void aed_set_ctrl(bool enable, int sel, enum frddr_dest module)
}
void aed_set_format(int msb, enum ddr_types frddr_type, enum ddr_num source)
void aed_set_format(int msb, enum ddr_types frddr_type,
enum ddr_num source, int offset)
{
eqdrc_update_bits(AED_TOP_CTL,
0x7 << 11 | 0x1f << 6 | 0x3 << 4,
frddr_type << 11 | msb << 6 | source << 4);
frddr_type << 11 | msb << 6 | source << (4 - offset));
}
void aed_enable(bool enable)

View File

@@ -33,8 +33,11 @@ void aed_set_multiband_drc_enable(bool enable);
void aed_set_volume(unsigned int master_volume,
unsigned int Lch_vol, unsigned int Rch_vol);
void aed_set_lane_and_channels(int lane_mask, int ch_mask);
void aed_set_ctrl(bool enable, int sel, enum frddr_dest module);
void aed_set_format(int msb, enum ddr_types frddr_type, enum ddr_num source);
void aed_set_lane_and_channels_v3(int lane_mask, int ch_mask);
void aed_set_ctrl(bool enable, int sel,
enum frddr_dest module, int offset);
void aed_set_format(int msb, enum ddr_types frddr_type,
enum ddr_num source, int offset);
void aed_enable(bool enable);
void aed_set_mixer_params(void);
void aed_eq_taps(unsigned int eq1_taps);

View File

@@ -49,8 +49,10 @@ enum {
struct effect_chipinfo {
/* v1 is for G12X(g12a, g12b)
* v2 is for tl1
* v3 is for sm1/tm2
*/
bool v2;
int version;
bool reserved_frddr;
};
struct audioeffect {
@@ -90,17 +92,24 @@ static struct audioeffect *get_audioeffects(void)
return s_effect;
}
bool check_aed_v2(void)
int get_aed_dst(void)
{
struct audioeffect *p_effect = get_audioeffects();
if (!p_effect)
return false;
return -1;
else
return p_effect->effect_module;
}
if (p_effect->chipinfo && p_effect->chipinfo->v2)
return true;
int check_aed_version(void)
{
struct audioeffect *p_effect = get_audioeffects();
return false;
if ((!p_effect) || (!p_effect->chipinfo))
return -1;
return p_effect->chipinfo->version;
}
static int eqdrc_clk_set(struct audioeffect *p_effect)
@@ -577,7 +586,13 @@ int card_add_effect_v2_kcontrols(struct snd_soc_card *card)
}
static struct effect_chipinfo tl1_effect_chipinfo = {
.v2 = true,
.version = VERSION2,
.reserved_frddr = true,
};
static struct effect_chipinfo sm1_effect_chipinfo = {
.version = VERSION3,
.reserved_frddr = true,
};
static const struct of_device_id effect_device_id[] = {
@@ -589,8 +604,8 @@ static const struct of_device_id effect_device_id[] = {
.data = &tl1_effect_chipinfo,
},
{
.compatible = "amlogic, tl1-effect",
.data = &tl1_effect_chipinfo,
.compatible = "amlogic, snd-effect-v3",
.data = &sm1_effect_chipinfo,
},
{}
};
@@ -701,8 +716,16 @@ static int effect_platform_probe(struct platform_device *pdev)
p_effect->ch_mask = channel_mask;
p_effect->effect_module = eqdrc_module;
p_effect->dev = dev;
s_effect = p_effect;
dev_set_drvdata(&pdev->dev, p_effect);
/*set eq/drc module lane & channels*/
aed_set_lane_and_channels(lane_mask, channel_mask);
if (check_aed_version() == VERSION3)
aed_set_lane_and_channels_v3(lane_mask, channel_mask);
else
aed_set_lane_and_channels(lane_mask, channel_mask);
/*set master & channel volume gain to 0dB*/
aed_set_volume(0xc0, 0x30, 0x30);
/*set default mixer gain*/
@@ -720,9 +743,10 @@ static int effect_platform_probe(struct platform_device *pdev)
/*set EQ/DRC module enable*/
aml_set_aed(1, p_effect->effect_module);
p_effect->dev = dev;
s_effect = p_effect;
dev_set_drvdata(&pdev->dev, p_effect);
if (p_effect->chipinfo &&
p_effect->chipinfo->reserved_frddr) {
aml_aed_set_frddr_reserved();
}
return 0;
}

View File

@@ -17,7 +17,13 @@
#ifndef __EFFECTS_V2_H__
#define __EFFECTS_V2_H__
extern bool check_aed_v2(void);
enum {
VERSION1 = 0,
VERSION2,
VERSION3
};
extern int check_aed_version(void);
extern int card_add_effect_v2_kcontrols(struct snd_soc_card *card);
#endif

View File

@@ -33,6 +33,7 @@
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include "ddr_mngr.h"
#include "audio_utils.h"
@@ -45,11 +46,19 @@
#define MAX_INT 0x7ffffff
#define DYNC_KCNTL_CNT 2
struct extn_chipinfo {
/* try to check papb before fetch pcpd
* no nonpcm2pcm irq for tl1
*/
bool no_nonpcm2pcm_clr;
/* eARC-ARC or CEC-ARC
* CEC-ARC: tl1
* eARC-ARC: sm1/tm2, etc
*/
bool cec_arc;
};
struct extn {
@@ -89,6 +98,8 @@ struct extn {
bool nonpcm_flag;
struct extn_chipinfo *chipinfo;
struct snd_kcontrol *controls[DYNC_KCNTL_CNT];
};
#define PREALLOC_BUFFER (256 * 1024)
@@ -134,6 +145,17 @@ static irqreturn_t extn_ddr_isr(int irq, void *devid)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct device *dev = rtd->platform->dev;
struct extn *p_extn = (struct extn *)dev_get_drvdata(dev);
int timeout_thres = 5;
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_HDMI
int sample_rate_index = get_hdmi_sample_rate_index();
/*192K audio*/
if (sample_rate_index == 7)
timeout_thres = 10;
else
timeout_thres = 5;
#endif
if (!snd_pcm_running(substream))
return IRQ_HANDLED;
@@ -356,10 +378,19 @@ struct snd_soc_platform_driver extn_platform = {
.pcm_new = extn_new,
};
static int extn_create_controls(struct snd_card *card,
struct extn *p_extn);
static int extn_dai_probe(struct snd_soc_dai *cpu_dai)
{
struct snd_card *card = cpu_dai->component->card->snd_card;
struct extn *p_extn = snd_soc_dai_get_drvdata(cpu_dai);
pr_info("asoc debug: %s-%d\n", __func__, __LINE__);
if (p_extn->chipinfo && p_extn->chipinfo->cec_arc)
extn_create_controls(card, p_extn);
return 0;
}
@@ -607,8 +638,10 @@ static int arc_get_src(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct extn *p_extn = dev_get_drvdata(component->dev);
struct extn *p_extn = snd_kcontrol_chip(kcontrol);
if (!p_extn)
return 0;
ucontrol->value.integer.value[0] = p_extn->arc_src;
@@ -619,8 +652,10 @@ static int arc_set_src(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct extn *p_extn = dev_get_drvdata(component->dev);
struct extn *p_extn = snd_kcontrol_chip(kcontrol);
if (!p_extn)
return 0;
p_extn->arc_src = ucontrol->value.integer.value[0];
@@ -633,8 +668,10 @@ static int arc_get_enable(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct extn *p_extn = dev_get_drvdata(component->dev);
struct extn *p_extn = snd_kcontrol_chip(kcontrol);
if (!p_extn)
return 0;
ucontrol->value.integer.value[0] = p_extn->arc_en;
@@ -645,8 +682,10 @@ static int arc_set_enable(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct extn *p_extn = dev_get_drvdata(component->dev);
struct extn *p_extn = snd_kcontrol_chip(kcontrol);
if (!p_extn)
return 0;
p_extn->arc_en = ucontrol->value.integer.value[0];
@@ -662,6 +701,9 @@ static int frhdmirx_get_mode(
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct extn *p_extn = dev_get_drvdata(component->dev);
if (!p_extn)
return 0;
ucontrol->value.integer.value[0] = p_extn->hdmirx_mode;
return 0;
@@ -674,6 +716,9 @@ static int frhdmirx_set_mode(
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct extn *p_extn = dev_get_drvdata(component->dev);
if (!p_extn)
return 0;
p_extn->hdmirx_mode = ucontrol->value.integer.value[0];
return 0;
@@ -745,6 +790,9 @@ static int hdmirx_audio_type_get_enum(
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct extn *p_extn = dev_get_drvdata(component->dev);
if (!p_extn)
return 0;
ucontrol->value.enumerated.item[0] =
hdmiin_check_audio_type(p_extn);
@@ -752,8 +800,7 @@ static int hdmirx_audio_type_get_enum(
}
#endif
static const struct snd_kcontrol_new extn_controls[] = {
/* Out */
static const struct snd_kcontrol_new extn_arc_controls[DYNC_KCNTL_CNT] = {
SOC_ENUM_EXT("HDMI ARC Source",
arc_src_enum,
arc_get_src,
@@ -763,6 +810,34 @@ static const struct snd_kcontrol_new extn_controls[] = {
0,
arc_get_enable,
arc_set_enable),
};
static int extn_create_controls(struct snd_card *card,
struct extn *p_extn)
{
int i, err = 0;
memset(p_extn->controls, 0, sizeof(p_extn->controls));
for (i = 0; i < DYNC_KCNTL_CNT; i++) {
p_extn->controls[i] =
snd_ctl_new1(&extn_arc_controls[i], p_extn);
err = snd_ctl_add(card, p_extn->controls[i]);
if (err < 0)
goto __error;
}
return 0;
__error:
for (i = 0; i < DYNC_KCNTL_CNT; i++)
if (p_extn->controls[i])
snd_ctl_remove(card, p_extn->controls[i]);
return err;
}
static const struct snd_kcontrol_new extn_controls[] = {
/* In */
SOC_SINGLE_BOOL_EXT("SPDIFIN PAO",
@@ -831,12 +906,12 @@ static const struct snd_soc_component_driver extn_component = {
struct extn_chipinfo tl1_extn_chipinfo = {
.no_nonpcm2pcm_clr = true,
.cec_arc = true,
};
static const struct of_device_id extn_device_id[] = {
{
.compatible = "amlogic, snd-extn",
.data = &tl1_extn_chipinfo,
},
{
.compatible = "amlogic, tl1-snd-extn",
@@ -898,10 +973,10 @@ static int extn_platform_probe(struct platform_device *pdev)
/* Default: PAO mode */
p_extn->hdmirx_mode = 1;
ret = snd_soc_register_component(&pdev->dev,
&extn_component,
extn_dai,
ARRAY_SIZE(extn_dai));
ret = devm_snd_soc_register_component(&pdev->dev,
&extn_component,
extn_dai,
ARRAY_SIZE(extn_dai));
if (ret) {
dev_err(&pdev->dev,
"snd_soc_register_component failed\n");

View File

@@ -231,87 +231,6 @@ void vad_update_bits(unsigned int reg,
}
EXPORT_SYMBOL(vad_update_bits);
int earcrx_cmdc_read(unsigned int reg)
{
int ret, val = 0;
ret = aml_snd_read(IO_EARCRX_CMDC, reg, &val);
if (ret) {
pr_err("read audio reg %x error %d\n", reg, ret);
return -1;
}
return val;
}
EXPORT_SYMBOL(earcrx_cmdc_read);
void earcrx_cmdc_write(unsigned int reg, unsigned int val)
{
aml_snd_write(IO_EARCRX_CMDC, reg, val);
}
EXPORT_SYMBOL(earcrx_cmdc_write);
void earcrx_cmdc_update_bits(unsigned int reg,
unsigned int mask, unsigned int val)
{
aml_snd_update_bits(IO_EARCRX_CMDC, reg, mask, val);
}
EXPORT_SYMBOL(earcrx_cmdc_update_bits);
int earcrx_dmac_read(unsigned int reg)
{
int ret, val = 0;
ret = aml_snd_read(IO_EARCRX_DMAC, reg, &val);
if (ret) {
pr_err("read audio reg %x error %d\n", reg, ret);
return -1;
}
return val;
}
EXPORT_SYMBOL(earcrx_dmac_read);
void earcrx_dmac_write(unsigned int reg, unsigned int val)
{
aml_snd_write(IO_EARCRX_DMAC, reg, val);
}
EXPORT_SYMBOL(earcrx_dmac_write);
void earcrx_dmac_update_bits(unsigned int reg,
unsigned int mask, unsigned int val)
{
aml_snd_update_bits(IO_EARCRX_DMAC, reg, mask, val);
}
EXPORT_SYMBOL(earcrx_dmac_update_bits);
int earcrx_top_read(unsigned int reg)
{
int ret, val = 0;
ret = aml_snd_read(IO_EARCRX_TOP, reg, &val);
if (ret) {
pr_err("read audio reg %x error %d\n", reg, ret);
return -1;
}
return val;
}
EXPORT_SYMBOL(earcrx_top_read);
void earcrx_top_write(unsigned int reg, unsigned int val)
{
aml_snd_write(IO_EARCRX_TOP, reg, val);
}
EXPORT_SYMBOL(earcrx_top_write);
void earcrx_top_update_bits(unsigned int reg,
unsigned int mask, unsigned int val)
{
aml_snd_update_bits(IO_EARCRX_TOP, reg, mask, val);
}
EXPORT_SYMBOL(earcrx_top_update_bits);
static int snd_iomap_probe(struct platform_device *pdev)
{
struct resource res;

View File

@@ -25,9 +25,6 @@ enum{
IO_EQDRC_BUS,
IO_RESET,
IO_VAD,
IO_EARCRX_CMDC,
IO_EARCRX_DMAC,
IO_EARCRX_TOP,
IO_MAX,
};
@@ -61,17 +58,4 @@ extern int vad_read(unsigned int reg);
extern void vad_write(unsigned int reg, unsigned int val);
extern void vad_update_bits(unsigned int reg,
unsigned int mask, unsigned int val);
extern int earcrx_cmdc_read(unsigned int reg);
extern void earcrx_cmdc_write(unsigned int reg, unsigned int val);
extern void earcrx_cmdc_update_bits(unsigned int reg,
unsigned int mask, unsigned int val);
extern int earcrx_dmac_read(unsigned int reg);
extern void earcrx_dmac_write(unsigned int reg, unsigned int val);
extern void earcrx_dmac_update_bits(unsigned int reg,
unsigned int mask, unsigned int val);
extern int earcrx_top_read(unsigned int reg);
extern void earcrx_top_write(unsigned int reg, unsigned int val);
extern void earcrx_top_update_bits(unsigned int reg,
unsigned int mask, unsigned int val);
#endif

View File

@@ -75,7 +75,8 @@ struct loopback {
unsigned int datalb_chnum;
unsigned int datalb_chmask;
unsigned int datalb_lane_mask; /* related with data lane */
unsigned int lb_format;
unsigned int lb_lane_chmask;
unsigned int sysclk_freq;
struct toddr *tddr;
@@ -430,8 +431,8 @@ static int loopback_dai_startup(
/* datalb */
switch (p_loopback->datalb_src) {
case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC:
tdminlb_startup(p_loopback);
case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC_D:
/*tdminlb_startup(p_loopback);*/
break;
case SPDIFINLB_SPDIFOUTA ... SPDIFINLB_SPDIFOUTB:
break;
@@ -472,8 +473,8 @@ static void loopback_dai_shutdown(
/* datalb */
switch (p_loopback->datalb_src) {
case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC:
tdminlb_shutdown(p_loopback);
case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC_D:
/*tdminlb_shutdown(p_loopback);*/
break;
case SPDIFINLB_SPDIFOUTA ... SPDIFINLB_SPDIFOUTB:
break;
@@ -554,12 +555,7 @@ static int loopback_set_ctrl(struct loopback *p_loopback, int bitwidth)
}
switch (p_loopback->datalb_src) {
case TDMINLB_TDMOUTA:
case TDMINLB_TDMOUTB:
case TDMINLB_TDMOUTC:
case TDMINLB_PAD_TDMINA:
case TDMINLB_PAD_TDMINB:
case TDMINLB_PAD_TDMINC:
case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC_D:
if (bitwidth == 24) {
datalb_toddr_type = 4;
datalb_msb = 32 - 1;
@@ -603,9 +599,10 @@ static int loopback_set_ctrl(struct loopback *p_loopback, int bitwidth)
lb_set_datain_cfg(p_loopback->id, &datain_cfg);
lb_set_datalb_cfg(p_loopback->id, &datalb_cfg);
tdminlb_set_format(1); /* tdmin_lb i2s mode */
tdminlb_set_format(p_loopback->lb_format == SND_SOC_DAIFMT_I2S);
tdminlb_set_lanemask_and_chswap(0x76543210,
p_loopback->datalb_lane_mask);
p_loopback->datalb_lane_mask,
p_loopback->lb_lane_chmask);
tdminlb_set_ctrl(p_loopback->datalb_src);
return 0;
@@ -714,6 +711,10 @@ static int loopback_dai_prepare(
case TDMINLB_PAD_TDMINB:
case TDMINLB_PAD_TDMINC:
break;
case TDMINLB_PAD_TDMINA_D:
case TDMINLB_PAD_TDMINB_D:
case TDMINLB_PAD_TDMINC_D:
break;
case SPDIFINLB_SPDIFOUTA:
case SPDIFINLB_SPDIFOUTB:
break;
@@ -861,8 +862,8 @@ static int loopback_dai_hw_params(
/* datalb */
switch (p_loopback->datalb_src) {
case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC:
datalb_tdminlb_set_clk(p_loopback);
case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC_D:
/*datalb_tdminlb_set_clk(p_loopback);*/
break;
case SPDIFINLB_SPDIFOUTA ... SPDIFINLB_SPDIFOUTB:
break;
@@ -1008,6 +1009,9 @@ static const char *const datalb_tdminlb_texts[] = {
"TDMIN_A",
"TDMIN_B",
"TDMIN_C",
"TDMIN_A_D",
"TDMIN_B_D",
"TDMIN_C_D",
};
static const struct soc_enum datalb_tdminlb_enum =
@@ -1247,6 +1251,39 @@ err:
return -EINVAL;
}
static unsigned int loopback_parse_format(struct device_node *node)
{
unsigned int format = 0;
int ret = 0;
const char *str;
struct {
char *name;
unsigned int val;
} fmt_table[] = {
{"i2s", SND_SOC_DAIFMT_I2S},
{"dsp_a", SND_SOC_DAIFMT_DSP_A},
{"dsp_b", SND_SOC_DAIFMT_DSP_B}
};
ret = of_property_read_string(node, "datalb-format", &str);
if (ret == 0) {
int i;
for (i = 0; i < ARRAY_SIZE(fmt_table); i++) {
if (strcmp(str, fmt_table[i].name) == 0) {
format |= fmt_table[i].val;
break;
}
}
}
/* default format is I2S */
if (format == 0)
format = SND_SOC_DAIFMT_I2S;
return format;
}
static int loopback_parse_of(
struct device_node *node,
struct loopback *p_loopback)
@@ -1320,6 +1357,15 @@ static int loopback_parse_of(
goto fail;
}
p_loopback->lb_format = loopback_parse_format(node);
snd_soc_of_get_slot_mask
(node,
"datalb-channels-mask",
&p_loopback->lb_lane_chmask);
if (p_loopback->lb_lane_chmask == 0) {
/* default format is I2S and mask two channels */
p_loopback->lb_lane_chmask = 0x3;
}
pr_info("\tdatain_src:%d, datain_chnum:%d, datain_chumask:%x\n",
p_loopback->datain_src,
p_loopback->datain_chnum,
@@ -1331,6 +1377,8 @@ static int loopback_parse_of(
pr_info("\tdatain_lane_mask:0x%x, datalb_lane_mask:0x%x\n",
p_loopback->datain_lane_mask,
p_loopback->datalb_lane_mask);
pr_info("datalb_format: %d, chmask for lanes: %#x\n",
p_loopback->lb_format, p_loopback->lb_lane_chmask);
ret = datain_parse_of(node, p_loopback);
if (ret) {

View File

@@ -55,6 +55,10 @@ enum datalb_src {
TDMINLB_PAD_TDMINB,
TDMINLB_PAD_TDMINC,
TDMINLB_PAD_TDMINA_D,
TDMINLB_PAD_TDMINB_D,
TDMINLB_PAD_TDMINC_D,
SPDIFINLB_SPDIFOUTA,
SPDIFINLB_SPDIFOUTB,
};

View File

@@ -22,35 +22,40 @@
#include "regs.h"
#include "iomap.h"
static unsigned int
get_tdmin_id_from_lb_src(enum datalb_src lb_src)
{
return lb_src % TDMINLB_PAD_TDMINA;
}
void tdminlb_set_clk(int datatlb_src, int sclk_div, int ratio, bool enable)
void tdminlb_set_clk(enum datalb_src lb_src,
int sclk_div, int ratio, bool enable)
{
unsigned int bclk_sel, fsclk_sel;
unsigned int tdmin_src;
/* config for external codec */
if (datatlb_src >= 3) {
unsigned int clk_id = datatlb_src - 3;
if (lb_src >= TDMINLB_PAD_TDMINA) {
unsigned int id = get_tdmin_id_from_lb_src(lb_src);
unsigned int offset, reg;
unsigned int fsclk_hi;
fsclk_hi = ratio / 2;
bclk_sel = clk_id;
fsclk_sel = clk_id;
bclk_sel = id;
fsclk_sel = id;
/*sclk, lrclk*/
offset = EE_AUDIO_MST_B_SCLK_CTRL0 - EE_AUDIO_MST_A_SCLK_CTRL0;
reg = EE_AUDIO_MST_A_SCLK_CTRL0 + offset * clk_id;
reg = EE_AUDIO_MST_A_SCLK_CTRL0 + offset * id;
audiobus_update_bits(reg,
0x3 << 30 | 0x3ff << 20 | 0x3ff<<10 | 0x3ff,
(enable ? 0x3 : 0x0) << 30
| sclk_div << 20 | fsclk_hi << 10
| ratio);
tdmin_src = datatlb_src - 3;
tdmin_src = id;
} else
tdmin_src = datatlb_src;
tdmin_src = lb_src;
audiobus_update_bits(
EE_AUDIO_CLK_TDMIN_LB_CTRL,
@@ -72,11 +77,11 @@ void tdminlb_set_format(int i2s_fmt)
{
audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL,
0x1 << 30,
i2s_fmt << 30 /* 0:tdm mode; 1: i2s mode; */
!!i2s_fmt << 30 /* 0:tdm mode; 1: i2s mode; */
);
}
void tdminlb_set_ctrl(int src)
void tdminlb_set_ctrl(enum datalb_src src)
{
audiobus_update_bits(
EE_AUDIO_TDMIN_LB_CTRL,
@@ -101,14 +106,13 @@ static void tdminlb_set_lane_mask(int lane, int mask)
audiobus_write(EE_AUDIO_TDMIN_LB_MASK0 + lane, mask);
}
void tdminlb_set_lanemask_and_chswap(int swap, int lane_mask)
void tdminlb_set_lanemask_and_chswap(int swap, int lane_mask, unsigned int mask)
{
unsigned int mask;
unsigned int i;
pr_debug("tdmin_lb lane swap:0x%x mask:0x%x\n", swap, lane_mask);
mask = 0x3; /* i2s format */
pr_debug
("%s() lane swap:0x%x lane mask:0x%x, chmask:%#x\n",
__func__, swap, lane_mask, mask);
/* channel swap */
audiobus_write(EE_AUDIO_TDMIN_LB_SWAP0, swap);

View File

@@ -19,6 +19,7 @@
#define __AML_LOOPBACK_HW_H__
#include <linux/types.h>
#include "loopback.h"
struct data_cfg {
/*
@@ -46,20 +47,20 @@ struct data_cfg {
bool ch_ctrl_switch;
};
extern void tdminlb_set_clk(int datatlb_src,
int sclk_div, int ratio, bool enable);
void tdminlb_set_clk(enum datalb_src lb_src,
int sclk_div, int ratio, bool enable);
extern void tdminlb_set_format(int i2s_fmt);
extern void tdminlb_set_ctrl(int src);
void tdminlb_set_ctrl(enum datalb_src src);
extern void tdminlb_enable(int tdm_index, int in_enable);
extern void tdminlb_fifo_enable(int is_enable);
extern void tdminlb_set_format(int i2s_fmt);
extern void tdminlb_set_lanemask_and_chswap(int swap, int lane_mask);
void tdminlb_set_lanemask_and_chswap
(int swap, int lane_mask, unsigned int mask);
extern void tdminlb_set_src(int src);
extern void lb_set_datain_src(int id, int src);

View File

@@ -880,6 +880,9 @@ enum clk_sel {
#define AED_TOP_CTL 0x83
#define AED_TOP_REQ_CTL 0x84
#define AED_TOP_CTL0 0x83
#define AED_TOP_CTL1 0x84
#define AED_TOP_CTL2 0x85
/*
* VAD, Voice activity detection

View File

@@ -34,6 +34,11 @@ static const char *const audioclk_parent_names[] = {
"i_slv_sclk_d", "i_slv_sclk_e", "i_slv_sclk_f", "i_slv_sclk_g",
"i_slv_sclk_h", "i_slv_sclk_i", "i_slv_sclk_j"};
static const char *const mclk_pad_parent_names[] = {
"mclk_a", "mclk_b", "mclk_c",
"mclk_d", "mclk_e", "mclk_f"
};
CLOCK_GATE(audio_ddr_arb, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN0), 0);
CLOCK_GATE(audio_pdm, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN0), 1);
CLOCK_GATE(audio_tdmina, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN0), 2);
@@ -194,6 +199,15 @@ CLOCK_COM_GATE(mclk_d, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_D_CTRL(1)), 31);
CLOCK_COM_MUX(mclk_e, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_E_CTRL(1)), 0x7, 24);
CLOCK_COM_DIV(mclk_e, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_E_CTRL(1)), 0, 16);
CLOCK_COM_GATE(mclk_e, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_E_CTRL(1)), 31);
/* mclk_pad0 */
CLOCK_COM_MUX(mclk_pad0, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 0x7, 8);
CLOCK_COM_DIV(mclk_pad0, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 0, 8);
CLOCK_COM_GATE(mclk_pad0, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 15);
/* mclk_pad1 */
CLOCK_COM_MUX(mclk_pad1, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 0x7, 24);
CLOCK_COM_DIV(mclk_pad1, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 16, 8);
CLOCK_COM_GATE(mclk_pad1, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 31);
/* mclk_f */
CLOCK_COM_MUX(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL(1)), 0x7, 24);
CLOCK_COM_DIV(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL(1)), 0, 16);
@@ -369,6 +383,16 @@ static int sm1_clks_init(struct clk **clks, void __iomem *iobase)
clks[CLKID_EARCRX_DMAC] = REGISTER_CLK_COM(earcrx_dmac);
WARN_ON(IS_ERR_OR_NULL(clks[CLKID_EARCRX_DMAC]));
IOMAP_COM_CLK(mclk_pad0, iobase);
clks[CLKID_AUDIO_MCLK_PAD0] =
REGISTER_CLK_COM_PARENTS(mclk_pad0, mclk_pad);
WARN_ON(IS_ERR_OR_NULL(clks[CLKID_AUDIO_MCLK_PAD0]));
IOMAP_COM_CLK(mclk_pad1, iobase);
clks[CLKID_AUDIO_MCLK_PAD1] =
REGISTER_CLK_COM_PARENTS(mclk_pad1, mclk_pad);
WARN_ON(IS_ERR_OR_NULL(clks[CLKID_AUDIO_MCLK_PAD1]));
return 0;
}

View File

@@ -273,6 +273,62 @@ static int aml_audio_set_spdif_mute(struct snd_kcontrol *kcontrol,
return 0;
}
static int aml_spdif_platform_suspend(
struct platform_device *pdev, pm_message_t state)
{
struct aml_spdif *p_spdif = dev_get_drvdata(&pdev->dev);
struct pinctrl_state *pstate = NULL;
int stream = SNDRV_PCM_STREAM_PLAYBACK;
if (!IS_ERR_OR_NULL(p_spdif->pin_ctl)) {
pstate = pinctrl_lookup_state
(p_spdif->pin_ctl, "spdif_pins_mute");
if (!IS_ERR_OR_NULL(pstate))
pinctrl_select_state(p_spdif->pin_ctl, pstate);
}
aml_spdif_enable(p_spdif->actrl,
stream, p_spdif->id, false);
pr_info("%s is mute\n", __func__);
return 0;
}
static int aml_spdif_platform_resume(struct platform_device *pdev)
{
struct aml_spdif *p_spdif = dev_get_drvdata(&pdev->dev);
struct pinctrl_state *state = NULL;
int stream = SNDRV_PCM_STREAM_PLAYBACK;
if (!IS_ERR_OR_NULL(p_spdif->pin_ctl)) {
state = pinctrl_lookup_state
(p_spdif->pin_ctl, "spdif_pins");
if (!IS_ERR_OR_NULL(state))
pinctrl_select_state(p_spdif->pin_ctl, state);
}
aml_spdif_enable(p_spdif->actrl,
stream, p_spdif->id, true);
pr_info("%s is unmute\n", __func__);
return 0;
}
static void aml_spdif_platform_shutdown(struct platform_device *pdev)
{
struct aml_spdif *p_spdif = dev_get_drvdata(&pdev->dev);
struct pinctrl_state *pstate = NULL;
int stream = SNDRV_PCM_STREAM_PLAYBACK;
if (!IS_ERR_OR_NULL(p_spdif->pin_ctl)) {
pstate = pinctrl_lookup_state
(p_spdif->pin_ctl, "spdif_pins_mute");
if (!IS_ERR_OR_NULL(pstate))
pinctrl_select_state(p_spdif->pin_ctl, pstate);
}
aml_spdif_enable(p_spdif->actrl,
stream, p_spdif->id, false);
pr_info("%s is mute\n", __func__);
}
static int aml_audio_get_spdif_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -658,7 +714,7 @@ static void spdifin_status_event(struct aml_spdif *p_spdif)
if (p_spdif->chipinfo
&& p_spdif->chipinfo->pcpd_separated) {
if (intrpt_status & 0x8) {
pr_info("Pc changed, try to read spdifin audio type\n");
pr_debug("Pc changed, try to read spdifin audio type\n");
extcon_set_state(p_spdif->edev,
EXTCON_SPDIFIN_AUDIOTYPE, 1);
@@ -672,7 +728,7 @@ static void spdifin_status_event(struct aml_spdif *p_spdif)
#endif
}
if (intrpt_status & 0x10)
pr_info("Pd changed\n");
pr_debug("Pd changed\n");
} else {
if (intrpt_status & 0x8)
pr_debug("CH status changed\n");
@@ -1586,6 +1642,9 @@ struct platform_driver aml_spdif_driver = {
.of_match_table = aml_spdif_device_id,
},
.probe = aml_spdif_platform_probe,
.suspend = aml_spdif_platform_suspend,
.resume = aml_spdif_platform_resume,
.shutdown = aml_spdif_platform_shutdown,
};
module_platform_driver(aml_spdif_driver);

View File

@@ -83,6 +83,8 @@ struct aml_tdm {
struct clk *clk;
struct clk *clk_gate;
struct clk *mclk;
/* mclk mux out to pad */
struct clk *mclk2pad;
struct clk *samesrc_srcpll;
struct clk *samesrc_clk;
bool contns_clk;
@@ -723,12 +725,11 @@ static int aml_tdm_set_lanes(struct aml_tdm *p_tdm,
#if 1
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* set lanes mask acordingly */
if (p_tdm->chipinfo
&& p_tdm->chipinfo->oe_fn
&& p_tdm->setting.lane_oe_mask_out)
lane_mask = setting->lane_mask_out;
/* compatible using oe masks */
if (!lane_mask && setting->lane_oe_mask_out)
lane_mask = setting->lane_oe_mask_out;
else
lane_mask = setting->lane_mask_out;
for (i = 0; i < p_tdm->lane_cnt; i++) {
if (((1 << i) & lane_mask) && lanes) {
aml_tdm_set_channel_mask(p_tdm->actrl,
@@ -844,12 +845,17 @@ static int aml_tdm_set_clk_pad(struct aml_tdm *p_tdm)
if (p_tdm->chipinfo && (!p_tdm->chipinfo->mclkpad_no_offset))
mpad_offset = 1;
aml_tdm_clk_pad_select(p_tdm->actrl,
p_tdm->mclk_pad,
mpad_offset,
p_tdm->id,
p_tdm->id,
p_tdm->clk_sel);
if (p_tdm->mclk_pad >= 0) {
aml_tdm_mclk_pad_select(p_tdm->actrl,
p_tdm->mclk_pad,
mpad_offset,
p_tdm->clk_sel);
}
aml_tdm_sclk_pad_select(p_tdm->actrl,
mpad_offset,
p_tdm->id,
p_tdm->id);
return 0;
}
@@ -1131,21 +1137,17 @@ static int aml_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai,
unsigned int lanes_oe_out_cnt = 0, lanes_oe_in_cnt = 0;
unsigned int force_oe = 0, oe_val = 0;
unsigned int lanes_lb_cnt = 0;
int out_lanes, in_lanes;
int out_lanes = 0, in_lanes = 0;
int in_src = -1;
lanes_out_cnt = pop_count(p_tdm->setting.lane_mask_out);
lanes_in_cnt = pop_count(p_tdm->setting.lane_mask_in);
lanes_oe_out_cnt = pop_count(p_tdm->setting.lane_oe_mask_out);
lanes_oe_in_cnt = pop_count(p_tdm->setting.lane_oe_mask_in);
lanes_lb_cnt = pop_count(p_tdm->setting.lane_lb_mask_in);
pr_debug("%s(), txmask(%#x), rxmask(%#x)\n",
__func__, tx_mask, rx_mask);
pr_debug("\tlanes_out_cnt(%d), lanes_in_cnt(%d)\n",
lanes_out_cnt, lanes_in_cnt);
pr_debug("\tlanes_oe_out_cnt(%d), lanes_oe_in_cnt(%d)\n",
lanes_oe_out_cnt, lanes_oe_in_cnt);
pr_debug("\tlanes_lb_cnt(%d)\n",
lanes_lb_cnt);
pr_debug("\tslots(%d), slot_width(%d)\n",
@@ -1179,9 +1181,30 @@ static int aml_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai,
p_tdm->setting.lane_lb_mask_in
& p_tdm->setting.lane_oe_mask_in);
lanes_oe_out_cnt = pop_count(p_tdm->setting.lane_oe_mask_out);
lanes_oe_in_cnt = pop_count(p_tdm->setting.lane_oe_mask_in);
pr_debug
("\tlanes_oe_out_cnt(%d), lanes_oe_in_cnt(%d)\n",
lanes_oe_out_cnt, lanes_oe_in_cnt);
if (lanes_oe_out_cnt) {
force_oe = p_tdm->setting.lane_oe_mask_out;
unsigned int oe_fn_version = p_tdm->chipinfo->oe_fn;
force_oe = (1 << p_tdm->chipinfo->lane_cnt) - 1;
oe_val = p_tdm->setting.lane_oe_mask_out;
if (oe_fn_version == OE_FUNCTION_V1) {
aml_tdm_set_oe_v1
(p_tdm->actrl, p_tdm->id,
force_oe, oe_val);
} else if (oe_fn_version == OE_FUNCTION_V2) {
aml_tdm_set_oe_v2
(p_tdm->actrl, p_tdm->id,
force_oe, oe_val);
} else {
pr_err
("%s(), oe version(%d) not support\n",
__func__, oe_fn_version);
}
}
if (lanes_lb_cnt)
@@ -1201,7 +1224,7 @@ static int aml_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai,
}
}
out_lanes = lanes_out_cnt + lanes_oe_out_cnt;
out_lanes = lanes_out_cnt;
in_lanes = lanes_in_cnt + lanes_oe_in_cnt + lanes_lb_cnt;
if (p_tdm->chipinfo
@@ -1216,8 +1239,7 @@ static int aml_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai,
if (out_lanes > 0 && out_lanes <= LANE_MAX3)
aml_tdm_set_slot_out(p_tdm->actrl,
p_tdm->id, slots, slot_width,
force_oe, oe_val);
p_tdm->id, slots, slot_width);
/* constrains hw channels_max by DTS configs */
drv->playback.channels_max = slots * out_lanes;
@@ -1564,7 +1586,7 @@ static int aml_tdm_platform_probe(struct platform_device *pdev)
ret = of_parse_tdm_lane_slot_out(node,
&p_tdm->setting.lane_mask_out);
if (ret < 0)
p_tdm->setting.lane_mask_out = 0x0;
p_tdm->setting.lane_mask_out = 0x1;
/* get tdm lanes oe info. if not, set to default 0 */
ret = of_parse_tdm_lane_oe_slot_in(node,
@@ -1606,6 +1628,28 @@ static int aml_tdm_platform_probe(struct platform_device *pdev)
return ret;
}
/* clk tree style after SM1, instead of legacy prop */
p_tdm->mclk2pad = devm_clk_get(&pdev->dev, "mclk_pad");
if (!IS_ERR(p_tdm->mclk2pad)) {
ret = clk_set_parent(p_tdm->mclk2pad, p_tdm->mclk);
if (ret) {
dev_err(&pdev->dev, "Can't set tdm mclk_pad parent\n");
return -EINVAL;
}
clk_prepare_enable(p_tdm->mclk2pad);
p_tdm->mclk_pad = -1;
} else {
/* mclk pad ctrl */
ret = of_property_read_u32(node, "mclk_pad",
&p_tdm->mclk_pad);
if (ret < 0) {
/* No mclk in defalut if chip needs mclk pad mux. */
p_tdm->mclk_pad = -1;
dev_warn_once(&pdev->dev,
"neither mclk_pad nor mclk2pad set\n");
}
}
/* complete mclk for tdm */
if (get_meson_cpu_version(MESON_CPU_VERSION_LVL_MINOR) == 0xa)
meson_clk_measure((1<<16) | 0x67);
@@ -1625,12 +1669,6 @@ static int aml_tdm_platform_probe(struct platform_device *pdev)
/*set default clk for output*/
aml_set_default_tdm_clk(p_tdm);
/* mclk pad ctrl */
ret = of_property_read_u32(node, "mclk_pad",
&p_tdm->mclk_pad);
if (ret < 0)
p_tdm->mclk_pad = -1; /* not use mclk in defalut. */
p_tdm->dev = dev;
/* For debug to disable share buffer */
p_tdm->en_share = 1;

View File

@@ -454,34 +454,64 @@ void aml_update_tdmin_rev_ws(struct aml_audio_controller *actrl,
off_set = EE_AUDIO_TDMIN_B_CTRL - EE_AUDIO_TDMIN_A_CTRL;
reg_in = EE_AUDIO_TDMIN_A_CTRL + off_set * idx;
aml_audiobus_update_bits(actrl, reg_in,
aml_audiobus_update_bits
(actrl, reg_in,
0x1 << 25, is_rev << 25);
}
void aml_tdm_set_slot_out(
void aml_tdm_set_oe_v1(
struct aml_audio_controller *actrl,
int index, int slots, int slot_width,
int force_oe, int oe_val)
int index, int force_oe, int oe_val)
{
unsigned int reg, offset;
offset = EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0;
reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * index;
aml_audiobus_update_bits(actrl, reg,
0x3ff, ((slots - 1) << 5) | (slot_width - 1));
if (force_oe) {
offset = EE_AUDIO_TDMOUT_B_CTRL2 - EE_AUDIO_TDMOUT_A_CTRL2;
reg = EE_AUDIO_TDMOUT_A_CTRL2 + offset * index;
aml_audiobus_update_bits(actrl, reg, 0xf << 24, force_oe << 24);
/* force oe val, in or out */
reg = EE_AUDIO_TDMOUT_A_CTRL1 + offset * index;
aml_audiobus_update_bits(actrl, reg, 0xf, oe_val);
}
}
void aml_tdm_set_oe_v2(
struct aml_audio_controller *actrl,
int index, int force_oe, int oe_val)
{
unsigned int reg, offset;
if (force_oe) {
offset = EE_AUDIO_TDMOUT_B_CTRL2 - EE_AUDIO_TDMOUT_A_CTRL2;
reg = EE_AUDIO_TDMOUT_A_CTRL2 + offset * index;
aml_audiobus_update_bits(actrl, reg, 0xff << 8, force_oe << 8);
/* force oe val, in or out */
if (oe_val) {
reg = EE_AUDIO_TDMOUT_A_CTRL1 + offset * index;
aml_audiobus_update_bits(actrl, reg,
0xf << 0, oe_val << 0);
aml_audiobus_update_bits
(actrl, reg, 0xff << 16, oe_val << 16);
}
}
}
void aml_tdm_set_slot_out(
struct aml_audio_controller *actrl,
int index, int slots, int slot_width)
{
unsigned int reg, offset;
offset = EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0;
reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * index;
aml_audiobus_update_bits
(actrl, reg, 0x3ff, ((slots - 1) << 5) | (slot_width - 1));
}
void aml_tdm_set_slot_in(
struct aml_audio_controller *actrl,
int index, int in_src, int slot_width)
@@ -675,39 +705,40 @@ void aml_tdmout_enable_gain(int tdmout_id, int en)
audiobus_update_bits(reg, 0x1 << 26, !!en << 26);
}
void aml_tdm_clk_pad_select(
struct aml_audio_controller *actrl,
int mpad, int mpad_offset, int mclk_sel,
int tdm_index, int clk_sel)
void aml_tdm_mclk_pad_select(struct aml_audio_controller *actrl,
int mpad, int mpad_offset, int mclk_sel)
{
unsigned int reg, mask_offset, val_offset;
if (mpad >= 0) {
switch (mpad) {
case 0:
mask_offset = 0x7 << 0;
val_offset = mclk_sel << 0;
break;
case 1:
mask_offset = 0x7 << 4;
val_offset = mclk_sel << 4;
break;
default:
mask_offset = 0;
val_offset = 0;
pr_info("unknown tdm mpad:%d\n", mpad);
break;
}
switch (mpad) {
case 0:
mask_offset = 0x7 << 0;
val_offset = mclk_sel << 0;
break;
case 1:
mask_offset = 0x7 << 4;
val_offset = mclk_sel << 4;
break;
default:
mask_offset = 0x7 << 4;
val_offset = 0;
pr_info("unknown tdm mpad:%d\n", mpad);
break;
}
reg = EE_AUDIO_MST_PAD_CTRL0(mpad_offset);
if (actrl)
aml_audiobus_update_bits(actrl, reg,
mask_offset, val_offset);
else
audiobus_update_bits(reg,
mask_offset, val_offset);
} else
pr_warn("mclk is not configured\n");
reg = EE_AUDIO_MST_PAD_CTRL0(mpad_offset);
if (actrl)
aml_audiobus_update_bits(actrl, reg,
mask_offset, val_offset);
else
audiobus_update_bits(reg,
mask_offset, val_offset);
}
void aml_tdm_sclk_pad_select(struct aml_audio_controller *actrl,
int mpad_offset, int tdm_index, int clk_sel)
{
unsigned int reg, mask_offset, val_offset;
reg = EE_AUDIO_MST_PAD_CTRL1(mpad_offset);
switch (tdm_index) {

View File

@@ -107,8 +107,7 @@ extern void aml_update_tdmin_rev_ws(struct aml_audio_controller *actrl,
extern void aml_tdm_set_slot_out(
struct aml_audio_controller *actrl,
int index, int slots, int slot_width,
int force_oe, int oe_val);
int index, int slots, int slot_width);
extern void aml_tdm_set_slot_in(
struct aml_audio_controller *actrl,
@@ -151,6 +150,10 @@ extern void aml_tdm_clk_pad_select(
struct aml_audio_controller *actrl,
int mpad, int mpad_offset, int mclk_sel,
int tdm_index, int clk_sel);
void aml_tdm_mclk_pad_select(struct aml_audio_controller *actrl,
int mpad, int mpad_offset, int mclk_sel);
void aml_tdm_sclk_pad_select(struct aml_audio_controller *actrl,
int mpad_offset, int tdm_index, int clk_sel);
extern void i2s_to_hdmitx_ctrl(int tdm_index);
extern void i2s_to_hdmitx_disable(void);
@@ -164,4 +167,12 @@ void aml_tdm_mute_capture(
int tdm_index,
bool mute,
int lane_cnt);
void aml_tdm_out_reset(unsigned int tdm_id, int offset);
void aml_tdm_set_oe_v1(
struct aml_audio_controller *actrl,
int index, int force_oe, int oe_val);
void aml_tdm_set_oe_v2(
struct aml_audio_controller *actrl,
int index, int force_oe, int oe_val);
#endif

View File

@@ -15,6 +15,15 @@
*
*/
/* For OE function V1:
* OE is set by EE_AUDIO_TDMOUT_A_CTRL0 & EE_AUDIO_TDMOUT_A_CTRL1
*/
#define OE_FUNCTION_V1 1
/* For OE function V2:
* OE is set by EE_AUDIO_TDMOUT_A_CTRL2
*/
#define OE_FUNCTION_V2 2
struct tdm_chipinfo {
/* device id */
unsigned int id;
@@ -26,7 +35,7 @@ struct tdm_chipinfo {
bool sclk_ws_inv;
/* output en (oe) for pinmux */
bool oe_fn;
unsigned int oe_fn;
/* clk pad */
bool no_mclkpad_ctrl;
@@ -76,7 +85,7 @@ struct tdm_chipinfo g12a_tdma_chipinfo = {
struct tdm_chipinfo g12a_tdmb_chipinfo = {
.id = TDM_B,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V1,
.same_src_fn = true,
.mclkpad_no_offset = true,
};
@@ -84,7 +93,7 @@ struct tdm_chipinfo g12a_tdmb_chipinfo = {
struct tdm_chipinfo g12a_tdmc_chipinfo = {
.id = TDM_C,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V1,
.same_src_fn = true,
.mclkpad_no_offset = true,
};
@@ -92,7 +101,7 @@ struct tdm_chipinfo g12a_tdmc_chipinfo = {
struct tdm_chipinfo g12a_tdminlb_chipinfo = {
.id = TDM_LB,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V1,
.same_src_fn = true,
.mclkpad_no_offset = true,
};
@@ -100,7 +109,7 @@ struct tdm_chipinfo g12a_tdminlb_chipinfo = {
struct tdm_chipinfo tl1_tdma_chipinfo = {
.id = TDM_A,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V1,
.same_src_fn = true,
.adc_fn = true,
};
@@ -108,7 +117,7 @@ struct tdm_chipinfo tl1_tdma_chipinfo = {
struct tdm_chipinfo tl1_tdmb_chipinfo = {
.id = TDM_B,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V1,
.same_src_fn = true,
.adc_fn = true,
};
@@ -116,7 +125,7 @@ struct tdm_chipinfo tl1_tdmb_chipinfo = {
struct tdm_chipinfo tl1_tdmc_chipinfo = {
.id = TDM_C,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V1,
.same_src_fn = true,
.adc_fn = true,
};
@@ -124,7 +133,7 @@ struct tdm_chipinfo tl1_tdmc_chipinfo = {
struct tdm_chipinfo tl1_tdminlb_chipinfo = {
.id = TDM_LB,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V1,
.same_src_fn = true,
.adc_fn = true,
};
@@ -132,7 +141,7 @@ struct tdm_chipinfo tl1_tdminlb_chipinfo = {
struct tdm_chipinfo sm1_tdma_chipinfo = {
.id = TDM_A,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V2,
.same_src_fn = true,
.lane_cnt = LANE_MAX0,
};
@@ -140,7 +149,7 @@ struct tdm_chipinfo sm1_tdma_chipinfo = {
struct tdm_chipinfo sm1_tdmb_chipinfo = {
.id = TDM_B,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V2,
.same_src_fn = true,
.lane_cnt = LANE_MAX3,
};
@@ -148,7 +157,7 @@ struct tdm_chipinfo sm1_tdmb_chipinfo = {
struct tdm_chipinfo sm1_tdmc_chipinfo = {
.id = TDM_C,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V2,
.same_src_fn = true,
.lane_cnt = LANE_MAX1,
};
@@ -156,7 +165,7 @@ struct tdm_chipinfo sm1_tdmc_chipinfo = {
struct tdm_chipinfo sm1_tdminlb_chipinfo = {
.id = TDM_LB,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V2,
.same_src_fn = true,
.lane_cnt = LANE_MAX3,
};
@@ -164,7 +173,7 @@ struct tdm_chipinfo sm1_tdminlb_chipinfo = {
struct tdm_chipinfo tm2_tdma_chipinfo = {
.id = TDM_A,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V2,
.same_src_fn = true,
.adc_fn = true,
.lane_cnt = LANE_MAX3,
@@ -173,7 +182,7 @@ struct tdm_chipinfo tm2_tdma_chipinfo = {
struct tdm_chipinfo tm2_tdmb_chipinfo = {
.id = TDM_B,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V2,
.same_src_fn = true,
.adc_fn = true,
.lane_cnt = LANE_MAX1,
@@ -182,7 +191,7 @@ struct tdm_chipinfo tm2_tdmb_chipinfo = {
struct tdm_chipinfo tm2_tdmc_chipinfo = {
.id = TDM_C,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V2,
.same_src_fn = true,
.adc_fn = true,
.lane_cnt = LANE_MAX1,
@@ -191,7 +200,7 @@ struct tdm_chipinfo tm2_tdmc_chipinfo = {
struct tdm_chipinfo tm2_tdminlb_chipinfo = {
.id = TDM_LB,
.sclk_ws_inv = true,
.oe_fn = true,
.oe_fn = OE_FUNCTION_V2,
.same_src_fn = true,
.lane_cnt = LANE_MAX3,
};

View File

@@ -34,6 +34,11 @@ static const char *const audioclk_parent_names[] = {
"i_slv_sclk_d", "i_slv_sclk_e", "i_slv_sclk_f", "i_slv_sclk_g",
"i_slv_sclk_h", "i_slv_sclk_i", "i_slv_sclk_j"};
static const char *const mclk_pad_parent_names[] = {
"mclk_a", "mclk_b", "mclk_c",
"mclk_d", "mclk_e", "mclk_f"
};
CLOCK_GATE(audio_ddr_arb, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN0), 0);
CLOCK_GATE(audio_pdm, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN0), 1);
CLOCK_GATE(audio_tdmina, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN0), 2);
@@ -214,6 +219,15 @@ CLOCK_COM_GATE(mclk_e, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_E_CTRL(1)), 31);
CLOCK_COM_MUX(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL(1)), 0x7, 24);
CLOCK_COM_DIV(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL(1)), 0, 16);
CLOCK_COM_GATE(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL(1)), 31);
/* mclk_pad0 */
CLOCK_COM_MUX(mclk_pad0, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 0x7, 8);
CLOCK_COM_DIV(mclk_pad0, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 0, 8);
CLOCK_COM_GATE(mclk_pad0, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 15);
/* mclk_pad1 */
CLOCK_COM_MUX(mclk_pad1, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 0x7, 24);
CLOCK_COM_DIV(mclk_pad1, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 16, 8);
CLOCK_COM_GATE(mclk_pad1, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 31);
/* spdifin */
CLOCK_COM_MUX(spdifin, AUD_ADDR_OFFSET(EE_AUDIO_CLK_SPDIFIN_CTRL), 0x7, 24);
CLOCK_COM_DIV(spdifin, AUD_ADDR_OFFSET(EE_AUDIO_CLK_SPDIFIN_CTRL), 0, 8);
@@ -385,6 +399,16 @@ static int tm2_clks_init(struct clk **clks, void __iomem *iobase)
clks[CLKID_EARCRX_DMAC] = REGISTER_CLK_COM(earcrx_dmac);
WARN_ON(IS_ERR_OR_NULL(clks[CLKID_EARCRX_DMAC]));
IOMAP_COM_CLK(mclk_pad0, iobase);
clks[CLKID_AUDIO_MCLK_PAD0] =
REGISTER_CLK_COM_PARENTS(mclk_pad0, mclk_pad);
WARN_ON(IS_ERR_OR_NULL(clks[CLKID_AUDIO_MCLK_PAD0]));
IOMAP_COM_CLK(mclk_pad1, iobase);
clks[CLKID_AUDIO_MCLK_PAD1] =
REGISTER_CLK_COM_PARENTS(mclk_pad1, mclk_pad);
WARN_ON(IS_ERR_OR_NULL(clks[CLKID_AUDIO_MCLK_PAD1]));
return 0;
}

View File

@@ -1,3 +1,4 @@
obj-y += notify.o \
spdif_info.o \
misc.o
misc.o \
iomapres.o

View File

@@ -0,0 +1,75 @@
/*
* sound/soc/amlogic/common/iomapres.c
*
* Copyright (C) 2019 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/device.h>
#include <linux/amlogic/media/sound/iomapres.h>
struct regmap *regmap_resource(struct device *dev, char *name)
{
struct resource res;
void __iomem *base;
struct device_node *node = dev->of_node;
static struct regmap_config aud_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
int i;
i = of_property_match_string(node, "reg-names", name);
if (of_address_to_resource(node, i, &res))
return ERR_PTR(-ENOENT);
base = devm_ioremap_resource(dev, &res);
if (IS_ERR(base))
return ERR_CAST(base);
pr_info("%s, %s, start:%#x, size:%#x\n",
__func__, name, (u32)res.start, (u32)resource_size(&res));
aud_regmap_config.max_register = resource_size(&res) - 4;
aud_regmap_config.name =
devm_kasprintf(dev, GFP_KERNEL, "%s-%s", node->name, name);
if (!aud_regmap_config.name)
return ERR_PTR(-ENOMEM);
return devm_regmap_init_mmio(dev, base, &aud_regmap_config);
}
unsigned int mmio_read(struct regmap *map, unsigned int reg_ofs)
{
unsigned int val;
regmap_read(map, (reg_ofs << 2), &val);
return val;
}
int mmio_write(struct regmap *map, unsigned int reg_ofs, unsigned int value)
{
return regmap_write(map, (reg_ofs << 2), value);
}
int mmio_update_bits(struct regmap *map,
unsigned int reg_ofs,
unsigned int mask,
unsigned int value)
{
return regmap_update_bits(map, (reg_ofs << 2), mask, value);
}

View File

@@ -34,6 +34,11 @@
unsigned int IEC958_mode_codec;
EXPORT_SYMBOL(IEC958_mode_codec);
bool spdifout_is_raw(void)
{
return (IEC958_mode_codec && IEC958_mode_codec != 9);
}
bool spdif_is_4x_clk(void)
{
bool is_4x = false;

View File

@@ -69,7 +69,7 @@ static int ad82584f_set_eq_drc(struct snd_soc_codec *codec);
/* Power-up register defaults */
static const
struct reg_default ad82584f_reg_defaults[AD82584F_REGISTER_COUNT] = {
{0x00, 0x00},//##State_Control_1
{0x00, 0x04},//##State_Control_1
{0x01, 0x04},//##State_Control_2
{0x02, 0x30},//##State_Control_3
{0x03, 0x4e},//##Master_volume_control
@@ -207,7 +207,7 @@ struct reg_default ad82584f_reg_defaults[AD82584F_REGISTER_COUNT] = {
};
static const int m_reg_tab[AD82584F_REGISTER_COUNT][2] = {
{0x00, 0x00},//##State_Control_1
{0x00, 0x04},//##State_Control_1
{0x01, 0x04},//##State_Control_2
{0x02, 0x30},//##State_Control_3
{0x03, 0x4e},//##Master_volume_control
@@ -856,7 +856,7 @@ static int ad82584f_probe(struct snd_soc_codec *codec)
{
struct ad82584f_priv *ad82584f = snd_soc_codec_get_drvdata(codec);
struct ad82584f_platform_data *pdata = ad82584f->pdata;
int ret;
int ret = 0;
#ifdef CONFIG_HAS_EARLYSUSPEND
ad82584f->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
@@ -866,12 +866,15 @@ static int ad82584f_probe(struct snd_soc_codec *codec)
register_early_suspend(&(ad82584f->early_suspend));
#endif
ret = devm_gpio_request_one(codec->dev, pdata->reset_pin,
GPIOF_OUT_INIT_LOW,
"ad82584f-reset-pin");
if (ret < 0) {
dev_err(codec->dev, "ad82584f get gpio error!\n");
return -1;
if (pdata->reset_pin > 0) {
ret = devm_gpio_request_one(codec->dev, pdata->reset_pin,
GPIOF_OUT_INIT_LOW,
"ad82584f-reset-pin");
if (ret < 0) {
dev_err(codec->dev, "ad82584f get gpio error!\n");
return -1;
}
}
ad82584f_init(codec);

View File

@@ -133,7 +133,7 @@ static int tas5782m_ch2_vol_info(struct snd_kcontrol *kcontrol,
static int tas5782m_ch1_mute_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
| SNDRV_CTL_ELEM_ACCESS_READWRITE;
uinfo->count = 1;
@@ -148,7 +148,7 @@ static int tas5782m_ch1_mute_info(struct snd_kcontrol *kcontrol,
static int tas5782m_ch2_mute_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
| SNDRV_CTL_ELEM_ACCESS_READWRITE;
uinfo->count = 1;
@@ -361,14 +361,14 @@ static const struct snd_kcontrol_new tas5782m_snd_controls[] = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Ch1 Mute",
.name = "Ch1 Switch",
.info = tas5782m_ch1_mute_info,
.get = tas5782m_ch1_mute_get,
.put = tas5782m_ch1_mute_set,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Ch2 Mute",
.name = "Ch2 Switch",
.info = tas5782m_ch2_mute_info,
.get = tas5782m_ch2_mute_get,
.put = tas5782m_ch2_mute_set,
@@ -671,6 +671,11 @@ static void tas5782m_init_func(struct work_struct *p_work)
}
tas5782m_init_i2s_tdm_mode(codec, g_sample_bitsize);
tas5782m_set_mute(codec, tas5782m->Ch1_mute, tas5782m->Ch2_mute);
tas5782m_set_volume(codec, 255-tas5782m->Ch1_vol, 0);
tas5782m_set_volume(codec, 255-tas5782m->Ch2_vol, 1);
}
static int tas5782m_probe(struct snd_soc_codec *codec)
@@ -688,6 +693,11 @@ static int tas5782m_probe(struct snd_soc_codec *codec)
tas5782m = snd_soc_codec_get_drvdata(codec);
tas5782m->codec = codec;
tas5782m->Ch1_mute = 0;
tas5782m->Ch2_mute = 0;
tas5782m->Ch1_vol = 0;
tas5782m->Ch2_vol = 0;
INIT_WORK(&tas5782m->work, tas5782m_init_func);
schedule_work(&tas5782m->work);

View File

@@ -649,10 +649,10 @@ const uint32_t tas5805m_volume[] = {
#define TAS5805_EQPARAM_LENGTH 610
#define TAS5805_EQ_LENGTH 245
#define FILTER_PARAM_BYTE 244
static int m_eq_tab[TAS5805_EQPARAM_LENGTH][2] = {0};
static int m_eq_tab[TAS5805_EQPARAM_LENGTH][2];
#define TAS5805_DRC_PARAM_LENGTH 29
#define TAS5805_DRC_PARAM_COUNT 58
static int m_drc_tab[TAS5805_DRC_PARAM_LENGTH][2] = {0};
static int m_drc_tab[TAS5805_DRC_PARAM_LENGTH][2];
struct tas5805m_priv {
struct regmap *regmap;