sst-mfld-platform-pcm.c 20.5 KB
Newer Older
1
/*
2
 *  sst_mfld_platform.c - Intel MID Platform driver
3
 *
V
Vinod Koul 已提交
4
 *  Copyright (C) 2010-2014 Intel Corp
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *  Author: Vinod Koul <vinod.koul@intel.com>
 *  Author: Harsha Priya <priya.harsha@intel.com>
 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  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; version 2 of the License.
 *
 *  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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/slab.h>
#include <linux/io.h>
24
#include <linux/module.h>
25 26 27 28
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
29
#include <sound/compress_driver.h>
V
Vinod Koul 已提交
30
#include <asm/platform_sst_audio.h>
31
#include "sst-mfld-platform.h"
V
Vinod Koul 已提交
32
#include "sst-atom-controls.h"
33

34
struct sst_device *sst;
35
static DEFINE_MUTEX(sst_lock);
36
extern struct snd_compr_ops sst_platform_compr_ops;
37 38 39

int sst_register_dsp(struct sst_device *dev)
{
40 41
	if (WARN_ON(!dev))
		return -EINVAL;
42 43 44 45
	if (!try_module_get(dev->dev->driver->owner))
		return -ENODEV;
	mutex_lock(&sst_lock);
	if (sst) {
46
		dev_err(dev->dev, "we already have a device %s\n", sst->name);
47 48 49 50
		module_put(dev->dev->driver->owner);
		mutex_unlock(&sst_lock);
		return -EEXIST;
	}
51
	dev_dbg(dev->dev, "registering device %s\n", dev->name);
52 53 54 55 56 57 58 59
	sst = dev;
	mutex_unlock(&sst_lock);
	return 0;
}
EXPORT_SYMBOL_GPL(sst_register_dsp);

int sst_unregister_dsp(struct sst_device *dev)
{
60 61
	if (WARN_ON(!dev))
		return -EINVAL;
62 63 64 65 66 67 68 69 70 71 72
	if (dev != sst)
		return -EINVAL;

	mutex_lock(&sst_lock);

	if (!sst) {
		mutex_unlock(&sst_lock);
		return -EIO;
	}

	module_put(sst->dev->driver->owner);
73
	dev_dbg(dev->dev, "unreg %s\n", sst->name);
74 75 76 77 78 79
	sst = NULL;
	mutex_unlock(&sst_lock);
	return 0;
}
EXPORT_SYMBOL_GPL(sst_unregister_dsp);

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
static struct snd_pcm_hardware sst_platform_pcm_hw = {
	.info =	(SNDRV_PCM_INFO_INTERLEAVED |
			SNDRV_PCM_INFO_DOUBLE |
			SNDRV_PCM_INFO_PAUSE |
			SNDRV_PCM_INFO_RESUME |
			SNDRV_PCM_INFO_MMAP|
			SNDRV_PCM_INFO_MMAP_VALID |
			SNDRV_PCM_INFO_BLOCK_TRANSFER |
			SNDRV_PCM_INFO_SYNC_START),
	.buffer_bytes_max = SST_MAX_BUFFER,
	.period_bytes_min = SST_MIN_PERIOD_BYTES,
	.period_bytes_max = SST_MAX_PERIOD_BYTES,
	.periods_min = SST_MIN_PERIODS,
	.periods_max = SST_MAX_PERIODS,
	.fifo_size = SST_FIFO_SIZE,
};

V
Vinod Koul 已提交
97 98 99 100 101 102 103
static struct sst_dev_stream_map dpcm_strm_map[] = {
	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */
	{MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, 0},
	{MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, 0},
	{MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},
};

104
static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
105
{
106 107 108

	return sst_send_pipe_gains(dai, stream, mute);
}
109

110
/* helper functions */
111
void sst_set_stream_status(struct sst_runtime_stream *stream,
112 113
					int state)
{
114 115
	unsigned long flags;
	spin_lock_irqsave(&stream->status_lock, flags);
116
	stream->stream_status = state;
117
	spin_unlock_irqrestore(&stream->status_lock, flags);
118 119 120 121 122
}

static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
{
	int state;
123
	unsigned long flags;
124

125
	spin_lock_irqsave(&stream->status_lock, flags);
126
	state = stream->stream_status;
127
	spin_unlock_irqrestore(&stream->status_lock, flags);
128 129 130
	return state;
}

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
static void sst_fill_alloc_params(struct snd_pcm_substream *substream,
				struct snd_sst_alloc_params_ext *alloc_param)
{
	unsigned int channels;
	snd_pcm_uframes_t period_size;
	ssize_t periodbytes;
	ssize_t buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
	u32 buffer_addr = virt_to_phys(substream->dma_buffer.area);

	channels = substream->runtime->channels;
	period_size = substream->runtime->period_size;
	periodbytes = samples_to_bytes(substream->runtime, period_size);
	alloc_param->ring_buf_info[0].addr = buffer_addr;
	alloc_param->ring_buf_info[0].size = buffer_bytes;
	alloc_param->sg_count = 1;
	alloc_param->reserved = 0;
	alloc_param->frag_size = periodbytes * channels;

}
150
static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
151
				struct snd_sst_stream_params *param)
152
{
153 154 155
	param->uc.pcm_params.num_chan = (u8) substream->runtime->channels;
	param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
	param->uc.pcm_params.sfreq = substream->runtime->rate;
156

157 158 159 160 161 162
	/* PCM stream via ALSA interface */
	param->uc.pcm_params.use_offload_path = 0;
	param->uc.pcm_params.reserved2 = 0;
	memset(param->uc.pcm_params.channel_map, 0, sizeof(u8));

}
V
Vinod Koul 已提交
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180

static int sst_get_stream_mapping(int dev, int sdev, int dir,
	struct sst_dev_stream_map *map, int size)
{
	int i;

	if (map == NULL)
		return -EINVAL;


	/* index 0 is not used in stream map */
	for (i = 1; i < size; i++) {
		if ((map[i].dev_num == dev) && (map[i].direction == dir))
			return i;
	}
	return 0;
}

181
int sst_fill_stream_params(void *substream,
V
Vinod Koul 已提交
182
	const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress)
183
{
V
Vinod Koul 已提交
184 185 186
	int map_size;
	int index;
	struct sst_dev_stream_map *map;
187 188 189
	struct snd_pcm_substream *pstream = NULL;
	struct snd_compr_stream *cstream = NULL;

V
Vinod Koul 已提交
190 191 192
	map = ctx->pdata->pdev_strm_map;
	map_size = ctx->pdata->strm_map_size;

193 194 195 196 197 198 199 200
	if (is_compress == true)
		cstream = (struct snd_compr_stream *)substream;
	else
		pstream = (struct snd_pcm_substream *)substream;

	str_params->stream_type = SST_STREAM_TYPE_MUSIC;

	/* For pcm streams */
V
Vinod Koul 已提交
201 202 203 204 205 206 207 208 209 210 211
	if (pstream) {
		index = sst_get_stream_mapping(pstream->pcm->device,
					  pstream->number, pstream->stream,
					  map, map_size);
		if (index <= 0)
			return -EINVAL;

		str_params->stream_id = index;
		str_params->device_type = map[index].device_id;
		str_params->task = map[index].task_id;

212
		str_params->ops = (u8)pstream->stream;
V
Vinod Koul 已提交
213 214 215 216 217 218 219 220 221 222 223
	}

	if (cstream) {
		index = sst_get_stream_mapping(cstream->device->device,
					       0, cstream->direction,
					       map, map_size);
		if (index <= 0)
			return -EINVAL;
		str_params->stream_id = index;
		str_params->device_type = map[index].device_id;
		str_params->task = map[index].task_id;
224

V
Vinod Koul 已提交
225 226
		str_params->ops = (u8)cstream->direction;
	}
227
	return 0;
228 229
}

230
static int sst_platform_alloc_stream(struct snd_pcm_substream *substream,
231
		struct snd_soc_dai *dai)
232 233 234
{
	struct sst_runtime_stream *stream =
			substream->runtime->private_data;
235 236 237 238
	struct snd_sst_stream_params param = {{{0,},},};
	struct snd_sst_params str_params = {0};
	struct snd_sst_alloc_params_ext alloc_params = {0};
	int ret_val = 0;
239
	struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
240 241

	/* set codec params and inform SST driver the same */
242
	sst_fill_pcm_params(substream, &param);
243
	sst_fill_alloc_params(substream, &alloc_params);
244 245
	substream->runtime->dma_area = substream->dma_buffer.area;
	str_params.sparams = param;
246 247 248 249
	str_params.aparams = alloc_params;
	str_params.codec = SST_CODEC_TYPE_PCM;

	/* fill the device type and stream id to pass to SST driver */
V
Vinod Koul 已提交
250
	ret_val = sst_fill_stream_params(substream, ctx, &str_params, false);
251 252 253
	if (ret_val < 0)
		return ret_val;

254 255
	stream->stream_info.str_id = str_params.stream_id;

256
	ret_val = stream->ops->open(sst->dev, &str_params);
257 258 259 260
	if (ret_val <= 0)
		return ret_val;


261 262 263
	return ret_val;
}

264
static void sst_period_elapsed(void *arg)
265
{
266
	struct snd_pcm_substream *substream = arg;
267
	struct sst_runtime_stream *stream;
268
	int status;
269 270 271 272 273 274

	if (!substream || !substream->runtime)
		return;
	stream = substream->runtime->private_data;
	if (!stream)
		return;
275 276
	status = sst_get_stream_status(stream);
	if (status != SST_PLATFORM_RUNNING)
277 278 279 280 281 282 283 284
		return;
	snd_pcm_period_elapsed(substream);
}

static int sst_platform_init_stream(struct snd_pcm_substream *substream)
{
	struct sst_runtime_stream *stream =
			substream->runtime->private_data;
285
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
286 287
	int ret_val;

288
	dev_dbg(rtd->dev, "setting buffer ptr param\n");
289
	sst_set_stream_status(stream, SST_PLATFORM_INIT);
290
	stream->stream_info.period_elapsed = sst_period_elapsed;
291
	stream->stream_info.arg = substream;
292 293
	stream->stream_info.buffer_ptr = 0;
	stream->stream_info.sfreq = substream->runtime->rate;
294
	ret_val = stream->ops->stream_init(sst->dev, &stream->stream_info);
295
	if (ret_val)
296
		dev_err(rtd->dev, "control_set ret error %d\n", ret_val);
297 298 299 300
	return ret_val;

}

301 302 303 304 305 306 307 308 309 310
static int power_up_sst(struct sst_runtime_stream *stream)
{
	return stream->ops->power(sst->dev, true);
}

static void power_down_sst(struct sst_runtime_stream *stream)
{
	stream->ops->power(sst->dev, false);
}

311 312
static int sst_media_open(struct snd_pcm_substream *substream,
		struct snd_soc_dai *dai)
313
{
314
	int ret_val = 0;
315
	struct snd_pcm_runtime *runtime = substream->runtime;
316
	struct sst_runtime_stream *stream;
317

318 319 320 321
	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
	if (!stream)
		return -ENOMEM;
	spin_lock_init(&stream->status_lock);
322 323 324

	/* get the sst ops */
	mutex_lock(&sst_lock);
325 326
	if (!sst ||
	    !try_module_get(sst->dev->driver->owner)) {
327
		dev_err(dai->dev, "no device available to run\n");
328 329
		ret_val = -ENODEV;
		goto out_ops;
330
	}
331 332 333 334
	stream->ops = sst->ops;
	mutex_unlock(&sst_lock);

	stream->stream_info.str_id = 0;
335

336
	stream->stream_info.arg = substream;
337
	/* allocate memory for SST API set */
338
	runtime->private_data = stream;
L
Lu Guanqun 已提交
339

340 341 342 343
	ret_val = power_up_sst(stream);
	if (ret_val < 0)
		return ret_val;

344 345 346 347 348 349 350 351 352 353
	/* Make sure, that the period size is always even */
	snd_pcm_hw_constraint_step(substream->runtime, 0,
			   SNDRV_PCM_HW_PARAM_PERIODS, 2);

	return snd_pcm_hw_constraint_integer(runtime,
			 SNDRV_PCM_HW_PARAM_PERIODS);
out_ops:
	kfree(stream);
	mutex_unlock(&sst_lock);
	return ret_val;
354 355
}

356 357
static void sst_media_close(struct snd_pcm_substream *substream,
		struct snd_soc_dai *dai)
358 359 360 361 362
{
	struct sst_runtime_stream *stream;
	int ret_val = 0, str_id;

	stream = substream->runtime->private_data;
363 364
	power_down_sst(stream);

365 366
	str_id = stream->stream_info.str_id;
	if (str_id)
367
		ret_val = stream->ops->close(sst->dev, str_id);
368
	module_put(sst->dev->driver->owner);
369
	kfree(stream);
V
Vinod Koul 已提交
370 371
}

372
static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai,
V
Vinod Koul 已提交
373 374
					       struct snd_pcm_substream *substream)
{
375
	struct sst_data *sst = snd_soc_dai_get_drvdata(dai);
V
Vinod Koul 已提交
376 377 378 379 380
	struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map;
	struct sst_runtime_stream *stream =
			substream->runtime->private_data;
	u32 str_id = stream->stream_info.str_id;
	unsigned int pipe_id;
381

V
Vinod Koul 已提交
382 383
	pipe_id = map[str_id].device_id;

384
	dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n",
385
			pipe_id, str_id);
V
Vinod Koul 已提交
386
	return pipe_id;
387 388
}

389 390
static int sst_media_prepare(struct snd_pcm_substream *substream,
		struct snd_soc_dai *dai)
391 392 393 394 395 396 397
{
	struct sst_runtime_stream *stream;
	int ret_val = 0, str_id;

	stream = substream->runtime->private_data;
	str_id = stream->stream_info.str_id;
	if (stream->stream_info.str_id) {
398
		ret_val = stream->ops->stream_drop(sst->dev, str_id);
399 400 401
		return ret_val;
	}

402
	ret_val = sst_platform_alloc_stream(substream, dai);
403
	if (ret_val <= 0)
404 405 406 407 408 409 410 411 412 413 414
		return ret_val;
	snprintf(substream->pcm->id, sizeof(substream->pcm->id),
			"%d", stream->stream_info.str_id);

	ret_val = sst_platform_init_stream(substream);
	if (ret_val)
		return ret_val;
	substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER;
	return ret_val;
}

415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
static int sst_media_hw_params(struct snd_pcm_substream *substream,
				struct snd_pcm_hw_params *params,
				struct snd_soc_dai *dai)
{
	snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
	memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
	return 0;
}

static int sst_media_hw_free(struct snd_pcm_substream *substream,
		struct snd_soc_dai *dai)
{
	return snd_pcm_lib_free_pages(substream);
}

430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
static int sst_enable_ssp(struct snd_pcm_substream *substream,
			struct snd_soc_dai *dai)
{
	int ret = 0;

	if (!dai->active) {
		ret = sst_handle_vb_timer(dai, true);
		if (ret)
			return ret;
		ret = send_ssp_cmd(dai, dai->name, 1);
	}
	return ret;
}

static void sst_disable_ssp(struct snd_pcm_substream *substream,
			struct snd_soc_dai *dai)
{
	if (!dai->active) {
		send_ssp_cmd(dai, dai->name, 0);
		sst_handle_vb_timer(dai, false);
	}
}

453 454 455 456 457 458
static struct snd_soc_dai_ops sst_media_dai_ops = {
	.startup = sst_media_open,
	.shutdown = sst_media_close,
	.prepare = sst_media_prepare,
	.hw_params = sst_media_hw_params,
	.hw_free = sst_media_hw_free,
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
	.mute_stream = sst_media_digital_mute,
};

static struct snd_soc_dai_ops sst_compr_dai_ops = {
	.mute_stream = sst_media_digital_mute,
};

static struct snd_soc_dai_ops sst_be_dai_ops = {
	.startup = sst_enable_ssp,
	.shutdown = sst_disable_ssp,
};

static struct snd_soc_dai_driver sst_platform_dai[] = {
{
	.name = "media-cpu-dai",
	.ops = &sst_media_dai_ops,
	.playback = {
		.stream_name = "Headset Playback",
		.channels_min = SST_STEREO,
		.channels_max = SST_STEREO,
		.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
	.capture = {
		.stream_name = "Headset Capture",
		.channels_min = 1,
		.channels_max = 2,
		.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
},
{
	.name = "compress-cpu-dai",
	.compress_dai = 1,
	.ops = &sst_compr_dai_ops,
	.playback = {
		.stream_name = "Compress Playback",
		.channels_min = SST_STEREO,
		.channels_max = SST_STEREO,
		.rates = SNDRV_PCM_RATE_48000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
},
/* BE CPU  Dais */
{
	.name = "ssp0-port",
	.ops = &sst_be_dai_ops,
	.playback = {
		.stream_name = "ssp0 Tx",
		.channels_min = SST_STEREO,
		.channels_max = SST_STEREO,
		.rates = SNDRV_PCM_RATE_48000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
	.capture = {
		.stream_name = "ssp0 Rx",
		.channels_min = SST_STEREO,
		.channels_max = SST_STEREO,
		.rates = SNDRV_PCM_RATE_48000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
},
{
	.name = "ssp1-port",
	.ops = &sst_be_dai_ops,
	.playback = {
		.stream_name = "ssp1 Tx",
		.channels_min = SST_STEREO,
		.channels_max = SST_STEREO,
		.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
	.capture = {
		.stream_name = "ssp1 Rx",
		.channels_min = SST_STEREO,
		.channels_max = SST_STEREO,
		.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
},
{
	.name = "ssp2-port",
	.ops = &sst_be_dai_ops,
	.playback = {
		.stream_name = "ssp2 Tx",
		.channels_min = SST_STEREO,
		.channels_max = SST_STEREO,
		.rates = SNDRV_PCM_RATE_48000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
	.capture = {
		.stream_name = "ssp2 Rx",
		.channels_min = SST_STEREO,
		.channels_max = SST_STEREO,
		.rates = SNDRV_PCM_RATE_48000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
},
557 558 559 560 561 562 563 564 565 566 567 568 569 570
};

static int sst_platform_open(struct snd_pcm_substream *substream)
{
	struct snd_pcm_runtime *runtime;

	if (substream->pcm->internal)
		return 0;

	runtime = substream->runtime;
	runtime->hw = sst_platform_pcm_hw;
	return 0;
}

571
static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
572 573 574 575
					int cmd)
{
	int ret_val = 0, str_id;
	struct sst_runtime_stream *stream;
576
	int status;
577
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
578

579
	dev_dbg(rtd->dev, "sst_platform_pcm_trigger called\n");
580 581
	if (substream->pcm->internal)
		return 0;
582 583 584 585
	stream = substream->runtime->private_data;
	str_id = stream->stream_info.str_id;
	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
586
		dev_dbg(rtd->dev, "sst: Trigger Start\n");
587
		status = SST_PLATFORM_RUNNING;
588
		stream->stream_info.arg = substream;
589
		ret_val = stream->ops->stream_start(sst->dev, str_id);
590 591
		break;
	case SNDRV_PCM_TRIGGER_STOP:
592
		dev_dbg(rtd->dev, "sst: in stop\n");
593
		status = SST_PLATFORM_DROPPED;
594
		ret_val = stream->ops->stream_drop(sst->dev, str_id);
595 596
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
597
	case SNDRV_PCM_TRIGGER_SUSPEND:
598
		dev_dbg(rtd->dev, "sst: in pause\n");
599
		status = SST_PLATFORM_PAUSED;
600
		ret_val = stream->ops->stream_pause(sst->dev, str_id);
601 602
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
603
	case SNDRV_PCM_TRIGGER_RESUME:
604
		dev_dbg(rtd->dev, "sst: in pause release\n");
605
		status = SST_PLATFORM_RUNNING;
606
		ret_val = stream->ops->stream_pause_release(sst->dev, str_id);
607 608
		break;
	default:
609
		return -EINVAL;
610
	}
611

612 613 614
	if (!ret_val)
		sst_set_stream_status(stream, status);

615 616 617 618
	return ret_val;
}


619
static snd_pcm_uframes_t sst_platform_pcm_pointer
620 621 622
			(struct snd_pcm_substream *substream)
{
	struct sst_runtime_stream *stream;
623
	int ret_val, status;
624
	struct pcm_stream_info *str_info;
625
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
626 627

	stream = substream->runtime->private_data;
628 629
	status = sst_get_stream_status(stream);
	if (status == SST_PLATFORM_INIT)
630 631
		return 0;
	str_info = &stream->stream_info;
632
	ret_val = stream->ops->stream_read_tstamp(sst->dev, str_info);
633
	if (ret_val) {
634
		dev_err(rtd->dev, "sst: error code = %d\n", ret_val);
635 636
		return ret_val;
	}
637
	substream->runtime->delay = str_info->pcm_delay;
638
	return str_info->buffer_ptr;
639 640
}

641
static struct snd_pcm_ops sst_platform_ops = {
642
	.open = sst_platform_open,
643
	.ioctl = snd_pcm_lib_ioctl,
644 645
	.trigger = sst_platform_pcm_trigger,
	.pointer = sst_platform_pcm_pointer,
646 647
};

648
static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
649
{
650
	struct snd_soc_dai *dai = rtd->cpu_dai;
651
	struct snd_pcm *pcm = rtd->pcm;
652 653
	int retval = 0;

654 655
	if (dai->driver->playback.channels_min ||
			dai->driver->capture.channels_min) {
656 657
		retval =  snd_pcm_lib_preallocate_pages_for_all(pcm,
			SNDRV_DMA_TYPE_CONTINUOUS,
658
			snd_dma_continuous_data(GFP_DMA),
659 660
			SST_MIN_BUFFER, SST_MAX_BUFFER);
		if (retval) {
661
			dev_err(rtd->dev, "dma buffer allocationf fail\n");
662 663 664 665 666
			return retval;
		}
	}
	return retval;
}
667

V
Vinod Koul 已提交
668 669
static int sst_soc_probe(struct snd_soc_platform *platform)
{
670 671 672
	struct sst_data *drv = dev_get_drvdata(platform->dev);

	drv->soc_card = platform->component.card;
V
Vinod Koul 已提交
673 674 675 676 677
	return sst_dsp_init_v2_dpcm(platform);
}

static struct snd_soc_platform_driver sst_soc_platform_drv  = {
	.probe		= sst_soc_probe,
678
	.ops		= &sst_platform_ops,
679
	.compr_ops	= &sst_platform_compr_ops,
680 681 682
	.pcm_new	= sst_pcm_new,
};

683 684 685 686 687
static const struct snd_soc_component_driver sst_component = {
	.name		= "sst",
};


688 689
static int sst_platform_probe(struct platform_device *pdev)
{
V
Vinod Koul 已提交
690
	struct sst_data *drv;
691
	int ret;
692
	struct sst_platform_data *pdata;
V
Vinod Koul 已提交
693 694

	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
695
	if (drv == NULL) {
V
Vinod Koul 已提交
696 697 698
		return -ENOMEM;
	}

699 700 701 702 703
	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
	if (pdata == NULL) {
		return -ENOMEM;
	}

V
Vinod Koul 已提交
704 705 706
	pdata->pdev_strm_map = dpcm_strm_map;
	pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
	drv->pdata = pdata;
707
	drv->pdev = pdev;
V
Vinod Koul 已提交
708 709
	mutex_init(&drv->lock);
	dev_set_drvdata(&pdev->dev, drv);
710 711 712

	ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
	if (ret) {
713
		dev_err(&pdev->dev, "registering soc platform failed\n");
714 715
		return ret;
	}
716

717
	ret = snd_soc_register_component(&pdev->dev, &sst_component,
718 719
				sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
	if (ret) {
720
		dev_err(&pdev->dev, "registering cpu dais failed\n");
721 722 723 724 725 726 727 728
		snd_soc_unregister_platform(&pdev->dev);
	}
	return ret;
}

static int sst_platform_remove(struct platform_device *pdev)
{

729
	snd_soc_unregister_component(&pdev->dev);
730
	snd_soc_unregister_platform(&pdev->dev);
731
	dev_dbg(&pdev->dev, "sst_platform_remove success\n");
732 733 734
	return 0;
}

735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
#ifdef CONFIG_PM_SLEEP

static int sst_soc_prepare(struct device *dev)
{
	struct sst_data *drv = dev_get_drvdata(dev);
	int i;

	/* suspend all pcms first */
	snd_soc_suspend(drv->soc_card->dev);
	snd_soc_poweroff(drv->soc_card->dev);

	/* set the SSPs to idle */
	for (i = 0; i < drv->soc_card->num_rtd; i++) {
		struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai;

		if (dai->active) {
			send_ssp_cmd(dai, dai->name, 0);
			sst_handle_vb_timer(dai, false);
		}
	}

	return 0;
}

static void sst_soc_complete(struct device *dev)
{
	struct sst_data *drv = dev_get_drvdata(dev);
	int i;

	/* restart SSPs */
	for (i = 0; i < drv->soc_card->num_rtd; i++) {
		struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai;

		if (dai->active) {
			sst_handle_vb_timer(dai, true);
			send_ssp_cmd(dai, dai->name, 1);
		}
	}
	snd_soc_resume(drv->soc_card->dev);
}

#else

#define sst_soc_prepare NULL
#define sst_soc_complete NULL

#endif


static const struct dev_pm_ops sst_platform_pm = {
	.prepare	= sst_soc_prepare,
	.complete	= sst_soc_complete,
};

789 790
static struct platform_driver sst_platform_driver = {
	.driver		= {
791
		.name		= "sst-mfld-platform",
792
		.pm             = &sst_platform_pm,
793 794 795 796 797
	},
	.probe		= sst_platform_probe,
	.remove		= sst_platform_remove,
};

798
module_platform_driver(sst_platform_driver);
799 800 801 802 803

MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
MODULE_LICENSE("GPL v2");
804
MODULE_ALIAS("platform:sst-mfld-platform");