mirror of
https://github.com/apache/nuttx.git
synced 2026-06-05 15:58:59 +08:00
7731367027
CHAMPION-140 update aw88266a lib of stable version Signed-off-by: genghuihui <genghuihui@xiaomi.com> Change-Id: Ia426c3d895d3bb1b1bdf8a187bdc5a017be0ffe5
1509 lines
42 KiB
C
1509 lines
42 KiB
C
/****************************************************************************
|
|
* drivers/audio/aw88266a.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
#include <nuttx/arch.h>
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/signal.h>
|
|
#include <nuttx/random.h>
|
|
#include <nuttx/i2c/i2c_master.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/fs/ioctl.h>
|
|
#include <nuttx/audio/i2s.h>
|
|
#include <nuttx/audio/audio.h>
|
|
#include <nuttx/audio/aw88266a.h>
|
|
#include "aw88266a.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define AW88266A_CHIPID (0x2013)
|
|
#define AW88266A_SOFT_RESET (0x55aa)
|
|
#define MAX_RETRIES 3
|
|
#define AW88266A_SYSST_CHECK_MAX 10
|
|
#define ARRAY_SIZE(array) sizeof(array) / sizeof(array[0])
|
|
|
|
/****************************************************************************
|
|
* Private Type Declarations
|
|
****************************************************************************/
|
|
|
|
typedef enum
|
|
{
|
|
DEFAULT_OUTPUT_SPEAKER = 0,
|
|
SCO_OUTPUT,
|
|
VOIP,
|
|
}aw88266a_dev_t;
|
|
|
|
typedef struct
|
|
{
|
|
uint8_t addr;
|
|
uint16_t data;
|
|
}aw88266a_reg_cfg_t;
|
|
|
|
typedef enum
|
|
{
|
|
FREQUENCY_08K = 0,
|
|
FREQUENCY_11K = 1,
|
|
FREQUENCY_16K = 2,
|
|
FREQUENCY_22K = 3,
|
|
FREQUENCY_24K = 4,
|
|
FREQUENCY_32K = 5,
|
|
FREQUENCY_44K = 6,
|
|
FREQUENCY_48K = 7,
|
|
FREQUENCY_96K = 8,
|
|
FREQUENCY_192K = 9,
|
|
AW_FREQUENCY_NUM,
|
|
}aw_i2s_sample_rate_t;
|
|
|
|
typedef enum
|
|
{
|
|
WIDTH_16BITS = 16,
|
|
WIDTH_24BITS = 24,
|
|
WIDTH_32BITS = 32,
|
|
AW_WIDTH_NUM,
|
|
}aw_i2s_width_t;
|
|
|
|
struct aw88266a_dev_s
|
|
{
|
|
/* We are an audio lower half driver (We are also the upper "half" of
|
|
* the AW88266A driver with respect to the board lower half driver).
|
|
*
|
|
* Terminology: Our "lower" half audio instances will be called dev for the
|
|
* publicly visible version and "priv" for the version that only this
|
|
* driver knows. From the point of view of this driver, it is the board
|
|
* lower "half" that is referred to as "lower".
|
|
*/
|
|
|
|
struct audio_lowerhalf_s dev; /* AW88266A audio lower half (this device) */
|
|
|
|
/* Our specific driver data goes here */
|
|
|
|
const FAR struct aw88266a_lower_s *lower; /* Pointer to the board lower functions */
|
|
FAR struct i2c_master_s *i2c; /* I2C driver to use */
|
|
|
|
uint16_t samprate; /* Configured samprate (samples/sec) */
|
|
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
|
|
#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
|
|
uint16_t balance; /* Current balance level (b16) */
|
|
#endif /* CONFIG_AUDIO_EXCLUDE_BALANCE */
|
|
uint8_t volume; /* Current volume level {0..63} */
|
|
#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
|
|
uint8_t nchannels; /* Number of channels (1 or 2) */
|
|
uint8_t bpsamp; /* Bits per sample (8 or 16) */
|
|
uint32_t bclk; /* IIS BCLK */
|
|
|
|
bool paused; /* True: Playing is paused */
|
|
bool mute; /* True: Output is muted */
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
aw88266a_dev_t dev; /* aw88266a device type */
|
|
FAR char *dev_str; /* aw88266a device string */
|
|
}aw88266a_dev_info_t;
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_write_reg(FAR struct aw88266a_dev_s *priv,
|
|
uint8_t regaddr, uint16_t regval);
|
|
|
|
static void aw88266a_write_reg_bit(FAR struct aw88266a_dev_s *priv,
|
|
uint8_t regaddr, uint16_t mask,
|
|
uint16_t regval);
|
|
|
|
static int16_t aw88266a_read_reg(FAR struct aw88266a_dev_s *priv,
|
|
uint8_t regaddr);
|
|
|
|
static int aw88266a_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
|
|
FAR struct audio_caps_s *caps);
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int
|
|
aw88266a_configure(FAR struct audio_lowerhalf_s *dev,
|
|
FAR void *session, FAR const struct audio_caps_s *caps);
|
|
#else
|
|
static int aw88266a_configure(FAR struct audio_lowerhalf_s *dev,
|
|
FAR const struct audio_caps_s *caps);
|
|
#endif
|
|
|
|
static int aw88266a_shutdown(FAR struct audio_lowerhalf_s *dev);
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int
|
|
aw88266a_start(FAR struct audio_lowerhalf_s *dev,
|
|
FAR void *session);
|
|
#else
|
|
static int aw88266a_start(FAR struct audio_lowerhalf_s *dev);
|
|
#endif
|
|
|
|
#ifndef CONFIG_AUDIO_EXCLUDE_STOP
|
|
# ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int
|
|
aw88266a_stop(FAR struct audio_lowerhalf_s *dev, FAR void *session);
|
|
# else
|
|
static int aw88266a_stop(FAR struct audio_lowerhalf_s *dev);
|
|
# endif
|
|
#endif
|
|
|
|
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
|
|
# ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int aw88266a_pause(FAR struct audio_lowerhalf_s *dev,
|
|
FAR void *session);
|
|
# else
|
|
static int aw88266a_pause(FAR struct audio_lowerhalf_s *dev);
|
|
# endif
|
|
#endif
|
|
|
|
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
|
|
# ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int aw88266a_resume(FAR struct audio_lowerhalf_s *dev,
|
|
FAR void *session);
|
|
# else
|
|
static int aw88266a_resume(FAR struct audio_lowerhalf_s *dev);
|
|
# endif
|
|
#endif
|
|
|
|
static int aw88266a_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd,
|
|
unsigned long arg);
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int
|
|
aw88266a_reserve(FAR struct audio_lowerhalf_s *dev, FAR void **session);
|
|
#else
|
|
static int aw88266a_reserve(FAR struct audio_lowerhalf_s *dev);
|
|
#endif
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int aw88266a_release(FAR struct audio_lowerhalf_s *dev,
|
|
FAR void *session);
|
|
#else
|
|
static int aw88266a_release(FAR struct audio_lowerhalf_s *dev);
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const unsigned char g_aw88266a_reg_access[AW88266A_REG_MAX] =
|
|
{
|
|
[AW88266A_ID_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_SYSST_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_SYSINT_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_SYSINTM_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_SYSCTRL_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_SYSCTRL2_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_I2SCTRL1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_I2SCTRL2_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DACCFG1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DACCFG2_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DACCFG3_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DACCFG4_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DACCFG5_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DACCFG6_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DACCFG7_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_PWMCTRL_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_I2SCFG1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DBGCTRL_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DACST_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_VBAT_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_TEMP_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_PVDD_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_ISNDAT_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_VSNDAT_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_I2SINT_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_I2SCAPCNT_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_ANASTA1_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_ANASTA2_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_ANASTA3_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_TESTDET_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_TESTIN_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_TESTOUT_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_VSNTM1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_VSNTM2_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_ISNCTRL1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_PLLCTRL1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_PLLCTRL2_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_PLLCTRL3_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_CDACTRL1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_CDACTRL2_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_SADCCTRL1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_BSTCTRL1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_BSTCTRL2_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_BSTCTRL3_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_BSTCTRL4_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_BSTCTRL5_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_BSTCTRL6_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DSMCFG1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DSMCFG2_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DSMCFG3_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DSMCFG4_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DSMCFG5_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DSMCFG6_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DSMCFG7_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_DSMCFG8_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_TESTCTRL1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_TESTCTRL2_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_EFCTRL1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_EFCTRL2_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_EFWH_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_EFWM2_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_EFWM1_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_EFWL_REG] = (AW88266A_REG_RD_ACCESS |
|
|
AW88266A_REG_WR_ACCESS),
|
|
[AW88266A_EFRH_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_EFRM2_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_EFRM1_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_EFRL_REG] = (AW88266A_REG_RD_ACCESS),
|
|
[AW88266A_TM_REG] = (AW88266A_REG_NONE_ACCESS),
|
|
};
|
|
|
|
static const aw88266a_reg_cfg_t g_aw88266a_spk_cfg[] =
|
|
{
|
|
{0x03, 0xffff},
|
|
{0x04, 0xb240},
|
|
{0x05, 0x6007},
|
|
{0x06, 0x84e8},
|
|
{0x07, 0x0010},
|
|
{0x08, 0x3940},
|
|
{0x09, 0x0030},
|
|
{0x0a, 0x01e0},
|
|
{0x0b, 0x1c64},
|
|
{0x0c, 0x001b},
|
|
{0x0d, 0x4abb},
|
|
{0x0e, 0x4500},
|
|
{0x10, 0x5700},
|
|
{0x11, 0x2101},
|
|
{0x12, 0x6003},
|
|
{0x38, 0x002c},
|
|
{0x51, 0x00d8},
|
|
{0x52, 0x00e8},
|
|
{0x53, 0x3f08},
|
|
{0x54, 0x0202},
|
|
{0x55, 0x3020},
|
|
{0x56, 0x9411},
|
|
{0x57, 0x1105},
|
|
{0x58, 0x9884},
|
|
{0x60, 0x9b19},
|
|
{0x61, 0x6b3b},
|
|
{0x62, 0x7d30},
|
|
{0x63, 0x3047},
|
|
{0x64, 0xa008},
|
|
{0x65, 0x2abf},
|
|
{0x66, 0x460c},
|
|
{0x67, 0xc989},
|
|
{0x68, 0x3541},
|
|
{0x69, 0x4db8},
|
|
{0x6a, 0xd6f9},
|
|
{0x6b, 0x7ace},
|
|
{0x6c, 0xec7c},
|
|
{0x6d, 0x000c},
|
|
};
|
|
|
|
static const struct audio_ops_s g_audioops =
|
|
{
|
|
.getcaps = aw88266a_getcaps,
|
|
.configure = aw88266a_configure,
|
|
.shutdown = aw88266a_shutdown,
|
|
.start = aw88266a_start,
|
|
#ifndef CONFIG_AUDIO_EXCLUDE_STOP
|
|
.stop = aw88266a_stop,
|
|
#endif
|
|
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
|
|
.pause = aw88266a_pause,
|
|
.resume = aw88266a_resume,
|
|
#endif
|
|
.allocbuffer = NULL,
|
|
.freebuffer = NULL,
|
|
.enqueuebuffer = NULL,
|
|
.cancelbuffer = NULL,
|
|
.ioctl = aw88266a_ioctl,
|
|
.read = NULL,
|
|
.write = NULL,
|
|
.reserve = aw88266a_reserve,
|
|
.release = aw88266a_release,
|
|
};
|
|
|
|
static const aw88266a_dev_info_t g_aw88266a_device_mode[] =
|
|
{
|
|
{DEFAULT_OUTPUT_SPEAKER, "speaker"},
|
|
{SCO_OUTPUT, "sco"},
|
|
{VOIP, "voip"}
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_set_device
|
|
*
|
|
* Description:
|
|
* set aw88266a device type
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_set_device(FAR struct aw88266a_dev_s *priv,
|
|
aw88266a_dev_t device)
|
|
{
|
|
uint8_t i;
|
|
|
|
if (device == DEFAULT_OUTPUT_SPEAKER)
|
|
{
|
|
for (i = 0; i < ARRAY_SIZE(g_aw88266a_spk_cfg); i++)
|
|
{
|
|
aw88266a_write_reg(priv, g_aw88266a_spk_cfg[i].addr,
|
|
g_aw88266a_spk_cfg[i].data);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_run_pwd
|
|
*
|
|
* Description:
|
|
* set aw88266a power down or work normally
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_run_pwd(FAR struct aw88266a_dev_s *priv,
|
|
bool status)
|
|
{
|
|
if (status)
|
|
{
|
|
aw88266a_write_reg_bit(priv, AW88266A_SYSCTRL_REG,
|
|
AW88266A_PWDN_MASK,
|
|
AW88266A_PWDN_POWER_DOWN_VALUE);
|
|
}
|
|
else
|
|
{
|
|
aw88266a_write_reg_bit(priv, AW88266A_SYSCTRL_REG,
|
|
AW88266A_PWDN_MASK,
|
|
AW88266A_PWDN_WORKING_VALUE);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_set_channel
|
|
*
|
|
* Description:
|
|
* set aw88266a i2s channel
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_set_channel(FAR struct aw88266a_dev_s *priv,
|
|
uint8_t channel)
|
|
{
|
|
uint16_t reg_value;
|
|
|
|
if (channel == CHSEL_LEFT || channel == CHSEL_RIGHT)
|
|
{
|
|
if (priv->lower->channelfmt == CHSEL_LEFT)
|
|
{
|
|
reg_value = AW88266A_CHSEL_LEFT_VALUE;
|
|
}
|
|
else if (priv->lower->channelfmt == CHSEL_RIGHT)
|
|
{
|
|
reg_value = AW88266A_CHSEL_RIGHT_VALUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
reg_value = AW88266A_CHSEL_MONO_VALUE;
|
|
}
|
|
|
|
aw88266a_write_reg_bit(priv, AW88266A_I2SCTRL1_REG,
|
|
AW88266A_CHSEL_MASK, reg_value);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_set_rate
|
|
*
|
|
* Description:
|
|
* set aw88266a i2s sample rate
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_set_rate(FAR struct aw88266a_dev_s *priv,
|
|
uint16_t rate)
|
|
{
|
|
uint16_t reg_value;
|
|
|
|
switch (rate)
|
|
{
|
|
case 8000:
|
|
{
|
|
reg_value = AW88266A_I2SSR_8_KHZ_VALUE;
|
|
}
|
|
break;
|
|
|
|
case 11000:
|
|
{
|
|
reg_value = AW88266A_I2SSR_11_KHZ_VALUE;
|
|
}
|
|
break;
|
|
|
|
case 16000:
|
|
{
|
|
reg_value = AW88266A_I2SSR_16_KHZ_VALUE;
|
|
}
|
|
break;
|
|
|
|
case 22000:
|
|
{
|
|
reg_value = AW88266A_I2SSR_22_KHZ_VALUE;
|
|
}
|
|
break;
|
|
|
|
case 24000:
|
|
{
|
|
reg_value = AW88266A_I2SSR_24_KHZ_VALUE;
|
|
}
|
|
break;
|
|
|
|
case 32000:
|
|
{
|
|
reg_value = AW88266A_I2SSR_32_KHZ_VALUE;
|
|
}
|
|
break;
|
|
|
|
case 44000:
|
|
{
|
|
reg_value = AW88266A_I2SSR_44_KHZ_VALUE;
|
|
}
|
|
break;
|
|
|
|
case 48000:
|
|
{
|
|
reg_value = AW88266A_I2SSR_48_KHZ_VALUE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
reg_value = AW88266A_I2SSR_48_KHZ_VALUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* set rate */
|
|
|
|
aw88266a_write_reg_bit(priv, AW88266A_I2SCTRL1_REG,
|
|
AW88266A_I2SSR_MASK, reg_value);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_set_width
|
|
*
|
|
* Description:
|
|
* set aw88266a i2s width
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_set_width(FAR struct aw88266a_dev_s *priv,
|
|
uint8_t width)
|
|
{
|
|
uint16_t reg_value;
|
|
|
|
switch (width)
|
|
{
|
|
case 16:
|
|
{
|
|
reg_value = AW88266A_I2SFS_16_BITS_VALUE;
|
|
}
|
|
break;
|
|
|
|
case 24:
|
|
{
|
|
reg_value = AW88266A_I2SFS_24_BITS_VALUE;
|
|
}
|
|
break;
|
|
|
|
case 32:
|
|
{
|
|
reg_value = AW88266A_I2SFS_32_BITS_VALUE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
reg_value = AW88266A_I2SFS_16_BITS_VALUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* set width */
|
|
|
|
aw88266a_write_reg_bit(priv, AW88266A_I2SCTRL1_REG,
|
|
AW88266A_I2SFS_MASK, reg_value);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_set_blck
|
|
*
|
|
* Description:
|
|
* set aw88266a i2s bclk
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_set_blck(FAR struct aw88266a_dev_s *priv,
|
|
uint8_t bclk_factor)
|
|
{
|
|
uint16_t reg_value;
|
|
|
|
switch (bclk_factor)
|
|
{
|
|
case 32:
|
|
{
|
|
reg_value = AW88266A_I2SBCK_32FS_VALUE;
|
|
}
|
|
break;
|
|
|
|
case 48:
|
|
{
|
|
reg_value = AW88266A_I2SBCK_48FS_VALUE;
|
|
}
|
|
break;
|
|
|
|
case 64:
|
|
{
|
|
reg_value = AW88266A_I2SBCK_64FS_VALUE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
reg_value = AW88266A_I2SBCK_64FS_VALUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* set fs */
|
|
|
|
aw88266a_write_reg_bit(priv, AW88266A_I2SCTRL1_REG,
|
|
AW88266A_I2SBCK_MASK, reg_value);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_run_mute
|
|
*
|
|
* Description:
|
|
* set aw88266a mute or unmute
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_run_mute(FAR struct aw88266a_dev_s *priv, bool mute)
|
|
{
|
|
if (mute)
|
|
{
|
|
aw88266a_write_reg_bit(priv, AW88266A_SYSCTRL2_REG,
|
|
AW88266A_HMUTE_MASK,
|
|
AW88266A_HMUTE_ENABLE_VALUE);
|
|
}
|
|
else
|
|
{
|
|
aw88266a_write_reg_bit(priv, AW88266A_SYSCTRL2_REG,
|
|
AW88266A_HMUTE_MASK,
|
|
AW88266A_HMUTE_DISABLE_VALUE);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_soft_reset
|
|
*
|
|
* Description:
|
|
* aw88266a software reset
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_soft_reset(FAR struct aw88266a_dev_s *priv)
|
|
{
|
|
aw88266a_write_reg(priv, AW88266A_ID_REG, AW88266A_SOFT_RESET);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_dump_register
|
|
*
|
|
* Description:
|
|
* dump aw88266a register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_dump_register(FAR struct aw88266a_dev_s *priv)
|
|
{
|
|
uint8_t i;
|
|
uint16_t reg_val;
|
|
|
|
for (i = 0; i < AW88266A_REG_MAX; i++)
|
|
{
|
|
if (g_aw88266a_reg_access[i] & AW88266A_REG_RD_ACCESS)
|
|
{
|
|
reg_val = aw88266a_read_reg(priv, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_pll_check
|
|
*
|
|
* Description:
|
|
* check aw88266a pll clock
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int aw88266a_pll_check(FAR struct aw88266a_dev_s *priv)
|
|
{
|
|
int ret;
|
|
uint8_t i;
|
|
uint16_t reg_val;
|
|
|
|
for (i = 0; i < AW88266A_SYSST_CHECK_MAX; i++)
|
|
{
|
|
reg_val = aw88266a_read_reg(priv, AW88266A_SYSST_REG);
|
|
if (((reg_val & (~AW88266A_SYSST_CHECK_MASK)) &
|
|
AW88266A_SYSST_CHECK) == AW88266A_SYSST_CHECK)
|
|
{
|
|
ret = OK;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
auderr("ERROR: check pll fail:cnt=%d reg_val=0x%04x\n\n",
|
|
i, reg_val);
|
|
ret = ERROR;
|
|
up_mdelay(2);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_run_amp
|
|
*
|
|
* Description:
|
|
* enable/disable aw88266a amp running status
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_run_amp(FAR struct aw88266a_dev_s *priv, bool status)
|
|
{
|
|
if (status)
|
|
{
|
|
aw88266a_write_reg_bit(priv, AW88266A_SYSCTRL_REG,
|
|
AW88266A_AMPPD_MASK,
|
|
AW88266A_AMPPD_WORKING_VALUE);
|
|
}
|
|
else
|
|
{
|
|
aw88266a_write_reg_bit(priv, AW88266A_SYSCTRL_REG,
|
|
AW88266A_AMPPD_MASK,
|
|
AW88266A_AMPPD_POWER_DOWN_VALUE);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_set_gain
|
|
*
|
|
* Description:
|
|
* set aw88266a gain
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int aw88266a_set_gain(FAR struct aw88266a_dev_s *priv, int gain)
|
|
{
|
|
uint16_t reg_value;
|
|
|
|
if (gain > AW88266A_GAIN_MAX || gain < 0)
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
gain = AW88266A_GAIN_MAX - gain;
|
|
|
|
/* cal real gain */
|
|
|
|
gain = (((gain / AW88266A_VOL_STEP_DB) << 6)
|
|
+ (gain % AW88266A_VOL_STEP_DB));
|
|
|
|
/* get reg_val form real gain */
|
|
|
|
gain = (gain & (~0xfc00));
|
|
reg_value = aw88266a_read_reg(priv, AW88266A_SYSCTRL2_REG);
|
|
gain = gain | (reg_value & 0xfc00);
|
|
aw88266a_write_reg(priv, AW88266A_SYSCTRL2_REG, gain);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_get_gain
|
|
*
|
|
* Description:
|
|
* get aw88266a gain
|
|
*
|
|
****************************************************************************/
|
|
|
|
int aw88266a_get_gain(FAR struct aw88266a_dev_s *priv, FAR int *gain)
|
|
{
|
|
uint16_t reg_val;
|
|
|
|
reg_val = aw88266a_read_reg(priv, AW88266A_SYSCTRL2_REG);
|
|
|
|
reg_val = (reg_val & (~0xfc00));
|
|
|
|
*gain = AW88266A_GAIN_MAX -
|
|
((reg_val >> 6) * AW88266A_VOL_STEP_DB + (reg_val & 0x3f));
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_set_volume
|
|
*
|
|
* Description:
|
|
* set aw88266a volume
|
|
*
|
|
****************************************************************************/
|
|
|
|
int aw88266a_set_volume(FAR struct aw88266a_dev_s *priv, int volume)
|
|
{
|
|
int ret;
|
|
uint16_t reg_val;
|
|
|
|
if (volume > 100 || volume < 0)
|
|
{
|
|
auderr("param scope (0-100), keep last volume.\r\n");
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
reg_val = (volume / 100) * AW88266A_GAIN_MAX +
|
|
(1 - (volume / 100)) * (volume / 10) * 80;
|
|
|
|
ret = aw88266a_set_gain(priv, reg_val);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_get_volume
|
|
*
|
|
* Description:
|
|
* get aw88266a volume
|
|
*
|
|
****************************************************************************/
|
|
|
|
int aw88266a_get_volume(FAR struct aw88266a_dev_s *priv, int *volume)
|
|
{
|
|
int ret;
|
|
int gain;
|
|
|
|
ret = aw88266a_get_gain(priv, &gain);
|
|
|
|
*volume = (gain / AW88266A_GAIN_MAX) * 100 +
|
|
(1 - (gain / AW88266A_GAIN_MAX)) * (gain / 80) * 10;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_getcaps
|
|
*
|
|
* Description:
|
|
* audio driver of get aw88266a caps
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int aw88266a_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
|
|
FAR struct audio_caps_s *caps)
|
|
{
|
|
/* Validate the structure */
|
|
|
|
DEBUGASSERT(caps && caps->ac_len >= sizeof(struct audio_caps_s));
|
|
audinfo("type=%d ac_type=%d\n", type, caps->ac_type);
|
|
|
|
/* Fill in the caller's structure based on requested info */
|
|
|
|
caps->ac_format.hw = 0;
|
|
caps->ac_controls.w = 0;
|
|
|
|
switch (caps->ac_type)
|
|
{
|
|
/* Caller is querying for the types of units we support */
|
|
|
|
case AUDIO_TYPE_QUERY:
|
|
|
|
/* Provide our overall capabilities. The interfacing software
|
|
* must then call us back for specific info for each capability.
|
|
*/
|
|
|
|
caps->ac_channels = 2; /* Stereo output */
|
|
|
|
switch (caps->ac_subtype)
|
|
{
|
|
case AUDIO_TYPE_QUERY:
|
|
|
|
/* We don't decode any formats! Only something above us in
|
|
* the audio stream can perform decoding on our behalf.
|
|
*/
|
|
|
|
/* The types of audio units we implement */
|
|
|
|
caps->ac_controls.b[0] =
|
|
AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE |
|
|
AUDIO_TYPE_PROCESSING;
|
|
break;
|
|
|
|
case AUDIO_FMT_MIDI:
|
|
|
|
/* We only support Format 0 */
|
|
|
|
caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
|
|
break;
|
|
|
|
default:
|
|
caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
/* Provide capabilities of our OUTPUT unit */
|
|
|
|
case AUDIO_TYPE_OUTPUT:
|
|
|
|
caps->ac_channels = 2;
|
|
|
|
switch (caps->ac_subtype)
|
|
{
|
|
case AUDIO_TYPE_QUERY:
|
|
|
|
/* Report the Sample rates we support */
|
|
|
|
caps->ac_controls.b[0] =
|
|
AUDIO_SAMP_RATE_8K | AUDIO_SAMP_RATE_11K |
|
|
AUDIO_SAMP_RATE_16K | AUDIO_SAMP_RATE_22K |
|
|
AUDIO_SAMP_RATE_32K | AUDIO_SAMP_RATE_44K |
|
|
AUDIO_SAMP_RATE_48K;
|
|
break;
|
|
|
|
case AUDIO_FMT_MP3:
|
|
case AUDIO_FMT_WMA:
|
|
case AUDIO_FMT_PCM:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
/* Provide capabilities of our FEATURE units */
|
|
|
|
case AUDIO_TYPE_FEATURE:
|
|
|
|
/* If the sub-type is UNDEF, then report the Feature Units we
|
|
* support.
|
|
*/
|
|
|
|
if (caps->ac_subtype == AUDIO_FU_UNDEF)
|
|
{
|
|
/* Fill in the ac_controls section with the Feature Units we
|
|
* have.
|
|
*/
|
|
|
|
caps->ac_controls.b[0] = AUDIO_FU_VOLUME | AUDIO_FU_BASS |
|
|
AUDIO_FU_TREBLE;
|
|
caps->ac_controls.b[1] = AUDIO_FU_BALANCE >> 8;
|
|
}
|
|
else
|
|
{
|
|
/* TODO: Do we need to provide specific info for the Feature
|
|
* Units, such as volume setting ranges, etc.?
|
|
*/
|
|
}
|
|
|
|
break;
|
|
|
|
/* Provide capabilities of our PROCESSING unit */
|
|
|
|
case AUDIO_TYPE_PROCESSING:
|
|
|
|
switch (caps->ac_subtype)
|
|
{
|
|
case AUDIO_PU_UNDEF:
|
|
|
|
/* Provide the type of Processing Units we support */
|
|
|
|
caps->ac_controls.b[0] = AUDIO_PU_STEREO_EXTENDER;
|
|
break;
|
|
|
|
case AUDIO_PU_STEREO_EXTENDER:
|
|
|
|
/* Provide capabilities of our Stereo Extender */
|
|
|
|
caps->ac_controls.b[0] =
|
|
AUDIO_STEXT_ENABLE | AUDIO_STEXT_WIDTH;
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Other types of processing uint we don't support */
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
/* All others we don't support */
|
|
|
|
default:
|
|
|
|
/* Zero out the fields to indicate no support */
|
|
|
|
caps->ac_subtype = 0;
|
|
caps->ac_channels = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
/* Return the length of the audio_caps_s struct for validation of
|
|
* proper Audio device type.
|
|
*/
|
|
|
|
return caps->ac_len;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_configure
|
|
*
|
|
* Description:
|
|
* audio driver configure aw88266a
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int
|
|
aw88266a_configure(FAR struct audio_lowerhalf_s *dev,
|
|
FAR void *session, FAR const struct audio_caps_s *caps)
|
|
#else
|
|
static int aw88266a_configure(FAR struct audio_lowerhalf_s *dev,
|
|
FAR const struct audio_caps_s *caps)
|
|
#endif
|
|
{
|
|
audinfo("ac_type: %d\n", caps->ac_type);
|
|
FAR struct aw88266a_dev_s *priv = (FAR struct aw88266a_dev_s *)dev;
|
|
|
|
/* Process the configure operation */
|
|
|
|
switch (caps->ac_type)
|
|
{
|
|
case AUDIO_TYPE_FEATURE:
|
|
{
|
|
audinfo(" AUDIO_TYPE_FEATURE\n");
|
|
|
|
/* Process based on Feature Unit */
|
|
|
|
switch (caps->ac_format.hw)
|
|
{
|
|
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
|
|
case AUDIO_FU_VOLUME:
|
|
{
|
|
audinfo("Volume: %d\n", caps->ac_controls.hw[0]);
|
|
}
|
|
break;
|
|
#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
|
|
|
|
#ifndef CONFIG_AUDIO_EXCLUDE_TONE
|
|
case AUDIO_FU_BASS:
|
|
{
|
|
audinfo("Bass: %d\n", caps->ac_controls.b[0]);
|
|
}
|
|
break;
|
|
|
|
case AUDIO_FU_TREBLE:
|
|
{
|
|
audinfo("Treble: %d\n", caps->ac_controls.b[0]);
|
|
}
|
|
break;
|
|
#endif /* CONFIG_AUDIO_EXCLUDE_TONE */
|
|
|
|
default:
|
|
{
|
|
auderr("ERROR: Unrecognized feature unit:0x%x\n",
|
|
caps->ac_format.hw);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AUDIO_TYPE_OUTPUT:
|
|
{
|
|
audinfo(" AUDIO_TYPE_OUTPUT:\n");
|
|
audinfo(" Number of channels: %u\n", caps->ac_channels);
|
|
audinfo(" Sample rate: %u\n", caps->ac_controls.hw[0]);
|
|
audinfo(" Sample width: %u\n", caps->ac_controls.b[2]);
|
|
audinfo(" Output channel map: 0x%x\n", caps->ac_chmap);
|
|
|
|
/* Verify that all of the requested values are supported */
|
|
|
|
priv->samprate = caps->ac_controls.hw[0];
|
|
priv->nchannels = caps->ac_channels;
|
|
priv->bpsamp = caps->ac_controls.b[2];
|
|
priv->bclk = caps->ac_controls.hw[0] *
|
|
(priv->lower->bclk_factor);
|
|
|
|
aw88266a_set_device(priv, DEFAULT_OUTPUT_SPEAKER);
|
|
|
|
aw88266a_set_channel(priv, priv->nchannels);
|
|
aw88266a_set_rate(priv, priv->samprate);
|
|
aw88266a_set_width(priv, priv->bpsamp);
|
|
aw88266a_set_blck(priv, priv->lower->bclk_factor);
|
|
}
|
|
break;
|
|
|
|
case AUDIO_TYPE_PROCESSING:
|
|
{
|
|
audinfo("AUDIO_TYPE_PROCESSING:\n");
|
|
}
|
|
break;
|
|
}
|
|
|
|
audinfo("Return OK\n");
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_shutdown
|
|
*
|
|
* Description:
|
|
* aduio driver of shutdown aw88266a
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int aw88266a_shutdown(FAR struct audio_lowerhalf_s *dev)
|
|
{
|
|
FAR struct aw88266a_dev_s *priv = (FAR struct aw88266a_dev_s *)dev;
|
|
|
|
aw88266a_run_mute(priv, true);
|
|
aw88266a_run_amp(priv, false);
|
|
aw88266a_run_pwd(priv, true);
|
|
|
|
audinfo("shutdown OK\n");
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_start
|
|
*
|
|
* Description:
|
|
* audio driver of start aw88266a
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int
|
|
aw88266a_start(FAR struct audio_lowerhalf_s *dev,
|
|
FAR void *session)
|
|
#else
|
|
static int aw88266a_start(FAR struct audio_lowerhalf_s *dev)
|
|
#endif
|
|
{
|
|
FAR struct aw88266a_dev_s *priv = (FAR struct aw88266a_dev_s *)dev;
|
|
|
|
aw88266a_run_pwd(priv, false);
|
|
|
|
if (aw88266a_pll_check(priv) < 0)
|
|
{
|
|
auderr("ERROR: aw88266a pll check failed\n");
|
|
}
|
|
|
|
aw88266a_run_amp(priv, true);
|
|
aw88266a_run_mute(priv, false);
|
|
aw88266a_set_volume(priv, 100);
|
|
|
|
audinfo("Return OK\n");
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_stop
|
|
*
|
|
* Description:
|
|
* audio driver of stop aw88266a
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifndef CONFIG_AUDIO_EXCLUDE_STOP
|
|
# ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int
|
|
aw88266a_stop(FAR struct audio_lowerhalf_s *dev, FAR void *session)
|
|
# else
|
|
static int aw88266a_stop(FAR struct audio_lowerhalf_s *dev)
|
|
# endif
|
|
{
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_pause
|
|
*
|
|
* Description:
|
|
* audio driver of pause aw88266a
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
|
|
# ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int aw88266a_pause(FAR struct audio_lowerhalf_s *dev,
|
|
FAR void *session)
|
|
# else
|
|
static int aw88266a_pause(FAR struct audio_lowerhalf_s *dev)
|
|
# endif
|
|
{
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_resume
|
|
*
|
|
* Description:
|
|
* audio driver of resume aw88266a
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
|
|
# ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int aw88266a_resume(FAR struct audio_lowerhalf_s *dev,
|
|
FAR void *session)
|
|
# else
|
|
static int aw88266a_resume(FAR struct audio_lowerhalf_s *dev)
|
|
# endif
|
|
{
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_ioctl
|
|
*
|
|
* Description:
|
|
* audio driver of ioctl aw88266a
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int aw88266a_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd,
|
|
unsigned long arg)
|
|
{
|
|
FAR const char *ptr = (FAR const char *)arg;
|
|
uint8_t i;
|
|
FAR struct aw88266a_dev_s *priv = (FAR struct aw88266a_dev_s *)dev;
|
|
|
|
switch (cmd)
|
|
{
|
|
case AUDIOIOC_SETPARAMTER:
|
|
if ((strncmp(ptr, "mode=", 5)) == 0)
|
|
{
|
|
ptr = ptr + 5;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(g_aw88266a_device_mode); i++)
|
|
{
|
|
if (strncmp(ptr, g_aw88266a_device_mode[i].dev_str,
|
|
strlen(g_aw88266a_device_mode[i].dev_str)) == 0)
|
|
{
|
|
aw88266a_set_device(priv, g_aw88266a_device_mode[i].dev);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AUDIOIOC_HWRESET:
|
|
audinfo("AUDIOIOC_HWRESET\n");
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
audinfo("Return OK\n");
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_reserve
|
|
*
|
|
* Description:
|
|
* audio driver of reserve
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int
|
|
aw88266a_reserve(FAR struct audio_lowerhalf_s *dev, FAR void **session)
|
|
#else
|
|
static int aw88266a_reserve(FAR struct audio_lowerhalf_s *dev)
|
|
#endif
|
|
{
|
|
audinfo("Reserve OK\n");
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_release
|
|
*
|
|
* Description:
|
|
* audio driver of release aw88266a
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
static int aw88266a_release(FAR struct audio_lowerhalf_s *dev,
|
|
FAR void *session)
|
|
#else
|
|
static int aw88266a_release(FAR struct audio_lowerhalf_s *dev)
|
|
#endif
|
|
{
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_write_reg
|
|
*
|
|
* Description:
|
|
* aw88266a write register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_write_reg(FAR struct aw88266a_dev_s *priv,
|
|
uint8_t regaddr, uint16_t regval)
|
|
{
|
|
struct i2c_config_s config;
|
|
int retries;
|
|
int ret;
|
|
|
|
/* Setup up the I2C configuration */
|
|
|
|
config.frequency = priv->lower->frequency;
|
|
config.address = priv->lower->address;
|
|
config.addrlen = 7;
|
|
|
|
for (retries = 0; retries < MAX_RETRIES; retries++)
|
|
{
|
|
uint8_t data[4];
|
|
|
|
/* Set up the data to write */
|
|
|
|
data[0] = regaddr & 0xff;
|
|
data[1] = regval >> 8;
|
|
data[2] = regval & 0xff;
|
|
|
|
/* Read the register data. The returned value is the number messages
|
|
* completed.
|
|
*/
|
|
|
|
ret = i2c_write(priv->i2c, &config, data, 3);
|
|
|
|
if (ret < 0)
|
|
{
|
|
auderr("ERROR: I2C_TRANSFER failed: %d\n", ret);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
audinfo("retries=%d regaddr=%02x\n", retries, regaddr);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_read_reg
|
|
*
|
|
* Description:
|
|
* aw88266a read register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int16_t aw88266a_read_reg(FAR struct aw88266a_dev_s *priv,
|
|
uint8_t regaddr)
|
|
{
|
|
int retries;
|
|
struct i2c_config_s config;
|
|
uint8_t buffer = regaddr;
|
|
uint8_t data[2];
|
|
int ret;
|
|
|
|
config.address = priv->lower->address;
|
|
config.frequency = priv->lower->frequency;
|
|
config.addrlen = 7;
|
|
|
|
for (retries = 0; retries < MAX_RETRIES; retries++)
|
|
{
|
|
ret = i2c_writeread(priv->i2c, &config, &buffer, 1, data, 2);
|
|
|
|
if (ret < 0)
|
|
{
|
|
auderr("ERROR: I2C_TRANSFER failed: %d\n", ret);
|
|
}
|
|
else
|
|
{
|
|
int16_t regval;
|
|
|
|
/* The I2C transfer was successful... break out of the loop and
|
|
* return the value read.
|
|
*/
|
|
|
|
regval = ((int16_t)data[0] << 8) | (int16_t)data[1];
|
|
return regval;
|
|
}
|
|
|
|
audinfo("retries=%d regaddr=%02x\n", retries, regaddr);
|
|
}
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_write_reg_bit
|
|
*
|
|
* Description:
|
|
* aw88266a write bit of register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void aw88266a_write_reg_bit(FAR struct aw88266a_dev_s *priv,
|
|
uint8_t regaddr, uint16_t mask,
|
|
uint16_t regval)
|
|
{
|
|
uint16_t t_val;
|
|
|
|
t_val = aw88266a_read_reg(priv, regaddr);
|
|
t_val &= mask;
|
|
t_val |= regval;
|
|
aw88266a_write_reg(priv, regaddr, t_val);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: aw88266a_initialize
|
|
*
|
|
* Description:
|
|
* Initializa awinic88266a smart pa
|
|
*
|
|
****************************************************************************/
|
|
|
|
FAR struct audio_lowerhalf_s *
|
|
aw88266a_initialize(FAR struct i2c_master_s *i2c,
|
|
FAR struct aw88266a_lower_s * lower)
|
|
{
|
|
FAR struct aw88266a_dev_s *priv;
|
|
uint16_t regval;
|
|
|
|
/* Allocate a WM8994 device structure */
|
|
|
|
priv = kmm_zalloc(sizeof(struct aw88266a_dev_s));
|
|
|
|
if (priv)
|
|
{
|
|
priv->dev.ops = &g_audioops;
|
|
priv->lower = lower;
|
|
priv->i2c = i2c;
|
|
|
|
regval = aw88266a_read_reg(priv, AW88266A_ID_REG);
|
|
if (regval != AW88266A_CHIPID)
|
|
{
|
|
auderr("ERROR: AW88266A not found: ID=%04x\n", regval);
|
|
goto errout_with_dev;
|
|
}
|
|
|
|
return &priv->dev;
|
|
}
|
|
|
|
errout_with_dev:
|
|
kmm_free(priv);
|
|
return NULL;
|
|
}
|