summaryrefslogtreecommitdiff
path: root/chip/npcx/audio_codec.c
blob: 5273e89717b28c6f27c45633f66334281663b1f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* Copyright 2018 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* Audio codec interface for NPCX */

#include "common.h"
#include "console.h"
#include "ec_commands.h"
#include "host_command.h"
#include "wov_chip.h"

#define EC_WOV_I2S_SAMPLE_RATE 48000

typedef int (*codec_i2s_func)(struct host_cmd_handler_args *args);

static int codec_set_sample_depth(struct host_cmd_handler_args *args)
{
	struct ec_param_codec_i2s *param =
		(struct ec_param_codec_i2s *)args->params;
	/*
	 * The number of bits comes from the enum ec_sample_depth_value where
	 * the 1st value is for 16 bits and the 2nd value is for 24 bits.
	 */
	int bits_num = param->depth == EC_CODEC_SAMPLE_DEPTH_24 ? 24 : 16;

	args->response_size = 0;
	/* Sample depth can only be changed when mode is WOV_MODE_OFF */
	wov_set_mode(WOV_MODE_OFF);

	if (wov_set_sample_depth(bits_num))
		return EC_RES_ERROR;

	return EC_RES_SUCCESS;
}

static int codec_set_gain(struct host_cmd_handler_args *args)
{
	struct ec_param_codec_i2s *param =
		(struct ec_param_codec_i2s *)args->params;

	args->response_size = 0;
	wov_set_gain(param->gain.left, param->gain.right);

	return EC_RES_SUCCESS;
}

static int codec_get_gain(struct host_cmd_handler_args *args)
{
	struct ec_codec_i2s_gain *resp =
		(struct ec_codec_i2s_gain *)args->response;
	int left_gain;
	int right_gain;

	args->response_size = sizeof(struct ec_codec_i2s_gain);
	/* Get the gains set in the wov driver */
	wov_get_gain(&left_gain, &right_gain);
	resp->left = left_gain;
	resp->right = right_gain;

	return EC_RES_SUCCESS;
}

static int codec_i2s_enable(struct host_cmd_handler_args *args)
{
	struct ec_param_codec_i2s *param =
		(struct ec_param_codec_i2s *)args->params;
	int rv;

	args->response_size = 0;

	if (param->i2s_enable) {
		/*
		 * The mic source and sample rate don't need to be set each time
		 * an i2s stream is started, but the audio codec does not
		 * contain a method to select these as they must be the values
		 * set below for proper i2s operation. Since the default values
		 * set in wov.c are different than what's required, they are set
		 * each time an i2s stream is started.
		 */
		wov_set_mic_source(WOV_SRC_STEREO);
		/* Mode must be WOV_MODE_OFF to change sample rate */
		wov_set_mode(WOV_MODE_OFF);
		wov_set_sample_rate(EC_WOV_I2S_SAMPLE_RATE);
		/* Start i2s stream */
		rv = wov_set_mode(WOV_MODE_I2S);
	} else {
		/* Stop i2s stream */
		rv = wov_set_mode(WOV_MODE_OFF);
	}

	return rv ? EC_RES_ERROR : EC_RES_SUCCESS;
}

static int codec_i2s_set_fmt(struct host_cmd_handler_args *args)
{
	struct ec_param_codec_i2s *param =
		(struct ec_param_codec_i2s *)args->params;

	args->response_size = 0;

	/* To change mode setting it must be set to WOV_MODE_OFF */
	wov_set_mode(WOV_MODE_OFF);
	wov_set_i2s_fmt(param->i2s_config);

	return EC_RES_SUCCESS;
}

static int codec_i2s_set_tdm_config(struct host_cmd_handler_args *args)
{
	struct ec_param_codec_i2s *param =
		(struct ec_param_codec_i2s *)args->params;
	uint32_t flags;

	args->response_size = 0;

	flags = (!!param->tdm_param.adjacent_to_ch0) |
		((!!param->tdm_param.adjacent_to_ch1) << 1);

	if (wov_set_i2s_tdm_config(param->tdm_param.ch0_delay,
				   param->tdm_param.ch1_delay, flags))
		return EC_RES_ERROR;

	return EC_RES_SUCCESS;
}

static int codec_i2s_set_bclk(struct host_cmd_handler_args *args)
{
	struct ec_param_codec_i2s *param =
		(struct ec_param_codec_i2s *)args->params;

	args->response_size = 0;
	/* To change bclk setting it must be set to WOV_MODE_OFF */
	wov_set_mode(WOV_MODE_OFF);
	wov_set_i2s_bclk(param->bclk);

	return EC_RES_SUCCESS;
}

static int codec_i2s(struct host_cmd_handler_args *args)
{
	struct ec_param_codec_i2s *param =
		(struct ec_param_codec_i2s *)args->params;

	codec_i2s_func codec_i2s_func_tbl[] = {
		codec_set_sample_depth,
		codec_set_gain,
		codec_get_gain,
		codec_i2s_enable,
		codec_i2s_set_fmt,
		codec_i2s_set_tdm_config,
		codec_i2s_set_bclk,
	};

	if (param->cmd < EC_CODEC_I2S_SUBCMD_COUNT)
		return codec_i2s_func_tbl[param->cmd](args);
	else
		return EC_RES_INVALID_PARAM;
}

DECLARE_HOST_COMMAND(EC_CMD_CODEC_I2S,
		     codec_i2s,
		     EC_VER_MASK(0));