seeed-voicecard/ac10x.h
Hin-Tak Leung 19067f3333 Second/Alternative change to correct for 5.5+ change in trigger order
The idea is the same as the previous attempt: calls ac101_trigger() just
before set_clock(1).

https://github.com/respeaker/seeed-voicecard/issues/290
    6mics / linear 4 mics: fails to record against v5.10 kernel
https://github.com/raspberrypi/linux/issues/4279
    [regression] alsa system call blocks on record between 5.4.83 and 5.5.19

In v5.5,

commit 4378f1fbe924054a09ff0d4e39e1a581b9245252
Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
Date:   Fri Sep 27 10:16:46 2019 +0300

    ASoC: soc-pcm: Use different sequence for start/stop trigger

    On stream stop currently we stop the DMA first followed by the CPU DAI.
    This can cause underflow (playback) or overflow (capture) on the DAI side
    as the DMA is no longer feeding data while the DAI is still active.
    It can be observed easily if the DAI side does not have FIFO (or it is
    disabled) to survive the time while the DMA is stopped, but still can
    happen on relatively slow CPUs when relatively high sampling rate is used:
    the FIFO is drained between the time the DMA is stopped and the DAI is
    stopped.

    It can only fixed by using different sequence within trigger for 'stop' and
    'start':
    case SNDRV_PCM_TRIGGER_START:
    case SNDRV_PCM_TRIGGER_RESUME:
    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
            Trigger order: dai_link, DMA, CPU DAI then the codec

    case SNDRV_PCM_TRIGGER_STOP:
    case SNDRV_PCM_TRIGGER_SUSPEND:
    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
            Trigger order: codec, CPU DAI, DMA then dai_link

    Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
    Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
    Link: https://lore.kernel.org/r/20190927071646.22319-1-peter.ujfalusi@ti.com
    Signed-off-by: Mark Brown <broonie@kernel.org>
2021-04-27 02:49:25 +01:00

126 lines
3.4 KiB
C

/*
* ac10x.h
*
* (C) Copyright 2017-2018
* Seeed Technology Co., Ltd. <www.seeedstudio.com>
*
* PeterYang <linsheng.yang@seeed.cc>
*
* (C) Copyright 2010-2017
* Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
* huangxin <huangxin@reuuimllatech.com>
*
* some simple description for this code
*
* 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.
*
*/
#ifndef __AC10X_H__
#define __AC10X_H__
#define AC101_I2C_ID 4
#define _MASTER_AC108 0
#define _MASTER_AC101 1
#define _MASTER_MULTI_CODEC _MASTER_AC101
/* enable headset detecting & headset button pressing */
#define CONFIG_AC101_SWITCH_DETECT
/* obsolete */
#define CONFIG_AC10X_TRIG_LOCK 0
#ifdef AC101_DEBG
#define AC101_DBG(format,args...) printk("[AC101] %s() L%d " format, __func__, __LINE__, ##args)
#else
#define AC101_DBG(...)
#endif
#include "sound-compatible-4.18.h"
#ifdef CONFIG_AC101_SWITCH_DETECT
enum headphone_mode_u {
HEADPHONE_IDLE,
FOUR_HEADPHONE_PLUGIN,
THREE_HEADPHONE_PLUGIN,
};
#endif
struct ac10x_priv {
struct i2c_client *i2c[4];
struct regmap* i2cmap[4];
int codec_cnt;
unsigned sysclk;
#define _FREQ_24_576K 24576000
#define _FREQ_22_579K 22579200
unsigned mclk; /* master clock or aif_clock/aclk */
int clk_id;
unsigned char i2s_mode;
unsigned char data_protocol;
struct delayed_work dlywork;
int tdm_chips_cnt;
int sysclk_en;
/* member for ac101 .begin */
struct snd_soc_codec *codec;
struct i2c_client *i2c101;
struct regmap* regmap101;
struct mutex dac_mutex;
u8 dac_enable;
spinlock_t lock;
u8 aif1_clken;
struct work_struct codec_resume;
struct gpio_desc* gpiod_spk_amp_gate;
#ifdef CONFIG_AC101_SWITCH_DETECT
struct gpio_desc* gpiod_irq;
long irq;
volatile int irq_cntr;
volatile int pullout_cntr;
volatile int state;
enum headphone_mode_u mode;
struct work_struct work_switch;
struct work_struct work_clear_irq;
struct input_dev* inpdev;
#endif
/* member for ac101 .end */
};
/* AC101 DAI operations */
int ac101_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai);
void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai);
int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt);
int ac101_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *codec_dai);
int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai);
int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute);
/* codec driver specific */
int ac101_codec_probe(struct snd_soc_codec *codec);
int ac101_codec_remove(struct snd_soc_codec *codec);
int ac101_codec_suspend(struct snd_soc_codec *codec);
int ac101_codec_resume(struct snd_soc_codec *codec);
int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level);
/* i2c device specific */
int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id);
void ac101_shutdown(struct i2c_client *i2c);
int ac101_remove(struct i2c_client *i2c);
/* seeed voice card export */
int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int, struct snd_pcm_substream *, int, struct snd_soc_dai *));
int ac10x_fill_regcache(struct device* dev, struct regmap* map);
#endif//__AC10X_H__