Blame view

sound/hda/hdac_device.c 27.4 KB
457c89965   Thomas Gleixner   treewide: Add SPD...
1
  // SPDX-License-Identifier: GPL-2.0-only
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
2
3
4
5
6
  /*
   * HD-audio codec core device
   */
  
  #include <linux/init.h>
097874925   Abhijeet Kumar   ALSA: hda: Copyin...
7
  #include <linux/delay.h>
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
8
9
10
11
12
13
  #include <linux/device.h>
  #include <linux/slab.h>
  #include <linux/module.h>
  #include <linux/export.h>
  #include <linux/pm_runtime.h>
  #include <sound/hdaudio.h>
01ed3c06c   Takashi Iwai   ALSA: hda - Use r...
14
  #include <sound/hda_regmap.h>
b7d023e11   Takashi Iwai   ALSA: hda - Move ...
15
  #include <sound/pcm.h>
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
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
  #include "local.h"
  
  static void setup_fg_nodes(struct hdac_device *codec);
  static int get_codec_vendor_name(struct hdac_device *codec);
  
  static void default_release(struct device *dev)
  {
  	snd_hdac_device_exit(container_of(dev, struct hdac_device, dev));
  }
  
  /**
   * snd_hdac_device_init - initialize the HD-audio codec base device
   * @codec: device to initialize
   * @bus: but to attach
   * @name: device name string
   * @addr: codec address
   *
   * Returns zero for success or a negative error code.
   *
   * This function increments the runtime PM counter and marks it active.
   * The caller needs to turn it off appropriately later.
   *
   * The caller needs to set the device's release op properly by itself.
   */
  int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
  			 const char *name, unsigned int addr)
  {
  	struct device *dev;
  	hda_nid_t fg;
  	int err;
  
  	dev = &codec->dev;
  	device_initialize(dev);
  	dev->parent = bus->dev;
  	dev->bus = &snd_hda_bus_type;
  	dev->release = default_release;
3256be653   Takashi Iwai   ALSA: hda - Add w...
52
  	dev->groups = hdac_dev_attr_groups;
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
53
54
55
56
57
58
  	dev_set_name(dev, "%s", name);
  	device_enable_async_suspend(dev);
  
  	codec->bus = bus;
  	codec->addr = addr;
  	codec->type = HDA_DEV_CORE;
ed180abba   Amadeusz Sławiński   ALSA: hda: Fix ra...
59
  	mutex_init(&codec->widget_lock);
1a462be52   Takashi Iwai   ALSA: hda: Manage...
60
  	mutex_init(&codec->regmap_lock);
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
61
62
63
  	pm_runtime_set_active(&codec->dev);
  	pm_runtime_get_noresume(&codec->dev);
  	atomic_set(&codec->in_pm, 0);
246bb4aaa   Takashi Iwai   Revert "ALSA: hda...
64
65
66
  	err = snd_hdac_bus_add_device(bus, codec);
  	if (err < 0)
  		goto error;
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
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
  	/* fill parameters */
  	codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
  					      AC_PAR_VENDOR_ID);
  	if (codec->vendor_id == -1) {
  		/* read again, hopefully the access method was corrected
  		 * in the last read...
  		 */
  		codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
  						      AC_PAR_VENDOR_ID);
  	}
  
  	codec->subsystem_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
  						 AC_PAR_SUBSYSTEM_ID);
  	codec->revision_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
  						AC_PAR_REV_ID);
  
  	setup_fg_nodes(codec);
  	if (!codec->afg && !codec->mfg) {
  		dev_err(dev, "no AFG or MFG node found
  ");
  		err = -ENODEV;
  		goto error;
  	}
  
  	fg = codec->afg ? codec->afg : codec->mfg;
774a075ab   Takashi Iwai   ALSA: hda: Simpli...
92
  	err = snd_hdac_refresh_widgets(codec);
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
93
94
95
96
97
  	if (err < 0)
  		goto error;
  
  	codec->power_caps = snd_hdac_read_parm(codec, fg, AC_PAR_POWER_STATE);
  	/* reread ssid if not set by parameter */
ffda568e8   David Henningsson   ALSA: hda - Fix s...
98
  	if (codec->subsystem_id == -1 || codec->subsystem_id == 0)
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  		snd_hdac_read(codec, fg, AC_VERB_GET_SUBSYSTEM_ID, 0,
  			      &codec->subsystem_id);
  
  	err = get_codec_vendor_name(codec);
  	if (err < 0)
  		goto error;
  
  	codec->chip_name = kasprintf(GFP_KERNEL, "ID %x",
  				     codec->vendor_id & 0xffff);
  	if (!codec->chip_name) {
  		err = -ENOMEM;
  		goto error;
  	}
  
  	return 0;
  
   error:
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
116
117
118
119
120
121
122
123
124
125
126
  	put_device(&codec->dev);
  	return err;
  }
  EXPORT_SYMBOL_GPL(snd_hdac_device_init);
  
  /**
   * snd_hdac_device_exit - clean up the HD-audio codec base device
   * @codec: device to clean up
   */
  void snd_hdac_device_exit(struct hdac_device *codec)
  {
c4c2533f8   Takashi Iwai   ALSA: hda - Fix p...
127
  	pm_runtime_put_noidle(&codec->dev);
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
128
129
130
131
132
133
134
  	snd_hdac_bus_remove_device(codec->bus, codec);
  	kfree(codec->vendor_name);
  	kfree(codec->chip_name);
  }
  EXPORT_SYMBOL_GPL(snd_hdac_device_exit);
  
  /**
3256be653   Takashi Iwai   ALSA: hda - Add w...
135
   * snd_hdac_device_register - register the hd-audio codec base device
6e57188f2   Keyon Jie   ALSA: hda: Update...
136
   * @codec: the device to register
3256be653   Takashi Iwai   ALSA: hda - Add w...
137
138
139
140
141
142
143
144
   */
  int snd_hdac_device_register(struct hdac_device *codec)
  {
  	int err;
  
  	err = device_add(&codec->dev);
  	if (err < 0)
  		return err;
ed180abba   Amadeusz Sławiński   ALSA: hda: Fix ra...
145
  	mutex_lock(&codec->widget_lock);
3256be653   Takashi Iwai   ALSA: hda - Add w...
146
  	err = hda_widget_sysfs_init(codec);
ed180abba   Amadeusz Sławiński   ALSA: hda: Fix ra...
147
  	mutex_unlock(&codec->widget_lock);
246bb4aaa   Takashi Iwai   Revert "ALSA: hda...
148
149
150
151
  	if (err < 0) {
  		device_del(&codec->dev);
  		return err;
  	}
3256be653   Takashi Iwai   ALSA: hda - Add w...
152
153
154
155
156
157
158
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(snd_hdac_device_register);
  
  /**
   * snd_hdac_device_unregister - unregister the hd-audio codec base device
6e57188f2   Keyon Jie   ALSA: hda: Update...
159
   * @codec: the device to unregister
3256be653   Takashi Iwai   ALSA: hda - Add w...
160
161
162
163
   */
  void snd_hdac_device_unregister(struct hdac_device *codec)
  {
  	if (device_is_registered(&codec->dev)) {
ed180abba   Amadeusz Sławiński   ALSA: hda: Fix ra...
164
  		mutex_lock(&codec->widget_lock);
3256be653   Takashi Iwai   ALSA: hda - Add w...
165
  		hda_widget_sysfs_exit(codec);
ed180abba   Amadeusz Sławiński   ALSA: hda: Fix ra...
166
  		mutex_unlock(&codec->widget_lock);
ee5f85d92   Takashi Iwai   ALSA: hda: Add co...
167
  		device_del(&codec->dev);
246bb4aaa   Takashi Iwai   Revert "ALSA: hda...
168
  		snd_hdac_bus_remove_device(codec->bus, codec);
3256be653   Takashi Iwai   ALSA: hda - Add w...
169
170
171
172
173
  	}
  }
  EXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
  
  /**
ded255be2   Takashi Iwai   ALSA: hda - conso...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
   * snd_hdac_device_set_chip_name - set/update the codec name
   * @codec: the HDAC device
   * @name: name string to set
   *
   * Returns 0 if the name is set or updated, or a negative error code.
   */
  int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name)
  {
  	char *newname;
  
  	if (!name)
  		return 0;
  	newname = kstrdup(name, GFP_KERNEL);
  	if (!newname)
  		return -ENOMEM;
  	kfree(codec->chip_name);
  	codec->chip_name = newname;
  	return 0;
  }
  EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
  
  /**
4f9e0c38c   Takashi Iwai   ALSA: hda - Add a...
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
   * snd_hdac_codec_modalias - give the module alias name
   * @codec: HDAC device
   * @buf: string buffer to store
   * @size: string buffer size
   *
   * Returns the size of string, like snprintf(), or a negative error code.
   */
  int snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size)
  {
  	return snprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X
  ",
  			codec->vendor_id, codec->revision_id, codec->type);
  }
  EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
  
  /**
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
212
213
214
215
216
217
218
219
220
   * snd_hdac_make_cmd - compose a 32bit command word to be sent to the
   *	HD-audio controller
   * @codec: the codec object
   * @nid: NID to encode
   * @verb: verb to encode
   * @parm: parameter to encode
   *
   * Return an encoded command verb or -1 for error.
   */
ddf7cb83b   Takashi Iwai   ALSA: hda: Unexpo...
221
222
  static unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
  				      unsigned int verb, unsigned int parm)
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
  {
  	u32 val, addr;
  
  	addr = codec->addr;
  	if ((addr & ~0xf) || (nid & ~0x7f) ||
  	    (verb & ~0xfff) || (parm & ~0xffff)) {
  		dev_err(&codec->dev, "out of range cmd %x:%x:%x:%x
  ",
  			addr, nid, verb, parm);
  		return -1;
  	}
  
  	val = addr << 28;
  	val |= (u32)nid << 20;
  	val |= verb << 8;
  	val |= parm;
  	return val;
  }
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
241
242
  
  /**
058524486   Takashi Iwai   ALSA: hda - Suppo...
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
   * snd_hdac_exec_verb - execute an encoded verb
   * @codec: the codec object
   * @cmd: encoded verb to execute
   * @flags: optional flags, pass zero for default
   * @res: the pointer to store the result, NULL if running async
   *
   * Returns zero if successful, or a negative error code.
   *
   * This calls the exec_verb op when set in hdac_codec.  If not,
   * call the default snd_hdac_bus_exec_verb().
   */
  int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
  		       unsigned int flags, unsigned int *res)
  {
  	if (codec->exec_verb)
  		return codec->exec_verb(codec, cmd, flags, res);
  	return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res);
  }
058524486   Takashi Iwai   ALSA: hda - Suppo...
261
262
263
  
  
  /**
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
264
265
266
267
268
269
270
271
272
273
274
275
276
   * snd_hdac_read - execute a verb
   * @codec: the codec object
   * @nid: NID to execute a verb
   * @verb: verb to execute
   * @parm: parameter for a verb
   * @res: the pointer to store the result, NULL if running async
   *
   * Returns zero if successful, or a negative error code.
   */
  int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
  		  unsigned int verb, unsigned int parm, unsigned int *res)
  {
  	unsigned int cmd = snd_hdac_make_cmd(codec, nid, verb, parm);
058524486   Takashi Iwai   ALSA: hda - Suppo...
277
  	return snd_hdac_exec_verb(codec, cmd, 0, res);
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
278
279
280
281
  }
  EXPORT_SYMBOL_GPL(snd_hdac_read);
  
  /**
01ed3c06c   Takashi Iwai   ALSA: hda - Use r...
282
   * _snd_hdac_read_parm - read a parmeter
6e57188f2   Keyon Jie   ALSA: hda: Update...
283
284
285
286
   * @codec: the codec object
   * @nid: NID to read a parameter
   * @parm: parameter to read
   * @res: pointer to store the read value
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
287
   *
01ed3c06c   Takashi Iwai   ALSA: hda - Use r...
288
   * This function returns zero or an error unlike snd_hdac_read_parm().
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
289
   */
01ed3c06c   Takashi Iwai   ALSA: hda - Use r...
290
291
  int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
  			unsigned int *res)
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
292
  {
01ed3c06c   Takashi Iwai   ALSA: hda - Use r...
293
  	unsigned int cmd;
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
294

01ed3c06c   Takashi Iwai   ALSA: hda - Use r...
295
296
  	cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
  	return snd_hdac_regmap_read_raw(codec, cmd, res);
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
297
  }
01ed3c06c   Takashi Iwai   ALSA: hda - Use r...
298
  EXPORT_SYMBOL_GPL(_snd_hdac_read_parm);
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
299
300
  
  /**
9ba17b4d1   Takashi Iwai   ALSA: hda - Imple...
301
   * snd_hdac_read_parm_uncached - read a codec parameter without caching
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
302
303
304
305
306
307
308
   * @codec: the codec object
   * @nid: NID to read a parameter
   * @parm: parameter to read
   *
   * Returns -1 for error.  If you need to distinguish the error more
   * strictly, use snd_hdac_read() directly.
   */
9ba17b4d1   Takashi Iwai   ALSA: hda - Imple...
309
310
  int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
  				int parm)
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
311
  {
3194ed497   Takashi Iwai   ALSA: hda - Fix p...
312
  	unsigned int cmd, val;
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
313

3194ed497   Takashi Iwai   ALSA: hda - Fix p...
314
315
316
  	cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
  	if (snd_hdac_regmap_read_raw_uncached(codec, cmd, &val) < 0)
  		return -1;
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
317
318
  	return val;
  }
9ba17b4d1   Takashi Iwai   ALSA: hda - Imple...
319
320
321
  EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached);
  
  /**
faa75f8a2   Takashi Iwai   ALSA: hda - Use r...
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
   * snd_hdac_override_parm - override read-only parameters
   * @codec: the codec object
   * @nid: NID for the parameter
   * @parm: the parameter to change
   * @val: the parameter value to overwrite
   */
  int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
  			   unsigned int parm, unsigned int val)
  {
  	unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm;
  	int err;
  
  	if (!codec->regmap)
  		return -EINVAL;
  
  	codec->caps_overwriting = true;
  	err = snd_hdac_regmap_write_raw(codec, verb, val);
  	codec->caps_overwriting = false;
  	return err;
  }
  EXPORT_SYMBOL_GPL(snd_hdac_override_parm);
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
343
344
345
346
347
348
349
350
  
  /**
   * snd_hdac_get_sub_nodes - get start NID and number of subtree nodes
   * @codec: the codec object
   * @nid: NID to inspect
   * @start_id: the pointer to store the starting NID
   *
   * Returns the number of subtree nodes or zero if not found.
9ba17b4d1   Takashi Iwai   ALSA: hda - Imple...
351
   * This function reads parameters always without caching.
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
352
353
354
355
356
   */
  int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
  			   hda_nid_t *start_id)
  {
  	unsigned int parm;
9ba17b4d1   Takashi Iwai   ALSA: hda - Imple...
357
  	parm = snd_hdac_read_parm_uncached(codec, nid, AC_PAR_NODE_COUNT);
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
  	if (parm == -1) {
  		*start_id = 0;
  		return 0;
  	}
  	*start_id = (parm >> 16) & 0x7fff;
  	return (int)(parm & 0x7fff);
  }
  EXPORT_SYMBOL_GPL(snd_hdac_get_sub_nodes);
  
  /*
   * look for an AFG and MFG nodes
   */
  static void setup_fg_nodes(struct hdac_device *codec)
  {
  	int i, total_nodes, function_id;
  	hda_nid_t nid;
  
  	total_nodes = snd_hdac_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
  	for (i = 0; i < total_nodes; i++, nid++) {
  		function_id = snd_hdac_read_parm(codec, nid,
  						 AC_PAR_FUNCTION_TYPE);
  		switch (function_id & 0xff) {
  		case AC_GRP_AUDIO_FUNCTION:
  			codec->afg = nid;
  			codec->afg_function_id = function_id & 0xff;
  			codec->afg_unsol = (function_id >> 8) & 1;
  			break;
  		case AC_GRP_MODEM_FUNCTION:
  			codec->mfg = nid;
  			codec->mfg_function_id = function_id & 0xff;
  			codec->mfg_unsol = (function_id >> 8) & 1;
  			break;
  		default:
  			break;
  		}
  	}
  }
  
  /**
   * snd_hdac_refresh_widgets - Reset the widget start/end nodes
   * @codec: the codec object
   */
774a075ab   Takashi Iwai   ALSA: hda: Simpli...
400
  int snd_hdac_refresh_widgets(struct hdac_device *codec)
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
401
402
  {
  	hda_nid_t start_nid;
98482377d   Evan Green   ALSA: hda: Fix wi...
403
  	int nums, err = 0;
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
404

98482377d   Evan Green   ALSA: hda: Fix wi...
405
406
407
408
409
  	/*
  	 * Serialize against multiple threads trying to update the sysfs
  	 * widgets array.
  	 */
  	mutex_lock(&codec->widget_lock);
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
410
411
412
413
414
  	nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid);
  	if (!start_nid || nums <= 0 || nums >= 0xff) {
  		dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x
  ",
  			codec->afg);
98482377d   Evan Green   ALSA: hda: Fix wi...
415
416
  		err = -EINVAL;
  		goto unlock;
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
417
  	}
774a075ab   Takashi Iwai   ALSA: hda: Simpli...
418
419
420
  	err = hda_widget_sysfs_reinit(codec, start_nid, nums);
  	if (err < 0)
  		goto unlock;
9780ded39   Takashi Iwai   ALSA: hda: Avoid ...
421

7639a06c2   Takashi Iwai   ALSA: hda - Move ...
422
423
424
  	codec->num_nodes = nums;
  	codec->start_nid = start_nid;
  	codec->end_nid = start_nid + nums;
98482377d   Evan Green   ALSA: hda: Fix wi...
425
426
427
  unlock:
  	mutex_unlock(&codec->widget_lock);
  	return err;
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
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
  }
  EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets);
  
  /* return CONNLIST_LEN parameter of the given widget */
  static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid)
  {
  	unsigned int wcaps = get_wcaps(codec, nid);
  	unsigned int parm;
  
  	if (!(wcaps & AC_WCAP_CONN_LIST) &&
  	    get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
  		return 0;
  
  	parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN);
  	if (parm == -1)
  		parm = 0;
  	return parm;
  }
  
  /**
   * snd_hdac_get_connections - get a widget connection list
   * @codec: the codec object
   * @nid: NID
   * @conn_list: the array to store the results, can be NULL
   * @max_conns: the max size of the given array
   *
   * Returns the number of connected widgets, zero for no connection, or a
   * negative error code.  When the number of elements don't fit with the
   * given array size, it returns -ENOSPC.
   *
   * When @conn_list is NULL, it just checks the number of connections.
   */
  int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
  			     hda_nid_t *conn_list, int max_conns)
  {
  	unsigned int parm;
  	int i, conn_len, conns, err;
  	unsigned int shift, num_elems, mask;
  	hda_nid_t prev_nid;
  	int null_count = 0;
  
  	parm = get_num_conns(codec, nid);
  	if (!parm)
  		return 0;
  
  	if (parm & AC_CLIST_LONG) {
  		/* long form */
  		shift = 16;
  		num_elems = 2;
  	} else {
  		/* short form */
  		shift = 8;
  		num_elems = 4;
  	}
  	conn_len = parm & AC_CLIST_LENGTH;
  	mask = (1 << (shift-1)) - 1;
  
  	if (!conn_len)
  		return 0; /* no connection */
  
  	if (conn_len == 1) {
  		/* single connection */
  		err = snd_hdac_read(codec, nid, AC_VERB_GET_CONNECT_LIST, 0,
  				    &parm);
  		if (err < 0)
  			return err;
  		if (conn_list)
  			conn_list[0] = parm & mask;
  		return 1;
  	}
  
  	/* multi connection */
  	conns = 0;
  	prev_nid = 0;
  	for (i = 0; i < conn_len; i++) {
  		int range_val;
  		hda_nid_t val, n;
  
  		if (i % num_elems == 0) {
  			err = snd_hdac_read(codec, nid,
  					    AC_VERB_GET_CONNECT_LIST, i,
  					    &parm);
  			if (err < 0)
  				return -EIO;
  		}
  		range_val = !!(parm & (1 << (shift-1))); /* ranges */
  		val = parm & mask;
  		if (val == 0 && null_count++) {  /* no second chance */
  			dev_dbg(&codec->dev,
  				"invalid CONNECT_LIST verb %x[%i]:%x
  ",
  				nid, i, parm);
  			return 0;
  		}
  		parm >>= shift;
  		if (range_val) {
  			/* ranges between the previous and this one */
  			if (!prev_nid || prev_nid >= val) {
  				dev_warn(&codec->dev,
  					 "invalid dep_range_val %x:%x
  ",
  					 prev_nid, val);
  				continue;
  			}
  			for (n = prev_nid + 1; n <= val; n++) {
  				if (conn_list) {
  					if (conns >= max_conns)
  						return -ENOSPC;
  					conn_list[conns] = n;
  				}
  				conns++;
  			}
  		} else {
  			if (conn_list) {
  				if (conns >= max_conns)
  					return -ENOSPC;
  				conn_list[conns] = val;
  			}
  			conns++;
  		}
  		prev_nid = val;
  	}
  	return conns;
  }
  EXPORT_SYMBOL_GPL(snd_hdac_get_connections);
  
  #ifdef CONFIG_PM
  /**
664c71557   Takashi Iwai   ALSA: hda - Work ...
556
   * snd_hdac_power_up - power up the codec
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
557
   * @codec: the codec object
664c71557   Takashi Iwai   ALSA: hda - Work ...
558
559
560
561
   *
   * This function calls the runtime PM helper to power up the given codec.
   * Unlike snd_hdac_power_up_pm(), you should call this only for the code
   * path that isn't included in PM path.  Otherwise it gets stuck.
fbce23a0b   Takashi Iwai   ALSA: hda - Check...
562
563
   *
   * Returns zero if successful, or a negative error code.
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
564
   */
fbce23a0b   Takashi Iwai   ALSA: hda - Check...
565
  int snd_hdac_power_up(struct hdac_device *codec)
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
566
  {
fbce23a0b   Takashi Iwai   ALSA: hda - Check...
567
  	return pm_runtime_get_sync(&codec->dev);
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
568
569
570
571
  }
  EXPORT_SYMBOL_GPL(snd_hdac_power_up);
  
  /**
664c71557   Takashi Iwai   ALSA: hda - Work ...
572
   * snd_hdac_power_down - power down the codec
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
573
   * @codec: the codec object
fbce23a0b   Takashi Iwai   ALSA: hda - Check...
574
575
   *
   * Returns zero if successful, or a negative error code.
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
576
   */
fbce23a0b   Takashi Iwai   ALSA: hda - Check...
577
  int snd_hdac_power_down(struct hdac_device *codec)
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
578
579
  {
  	struct device *dev = &codec->dev;
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
580
  	pm_runtime_mark_last_busy(dev);
fbce23a0b   Takashi Iwai   ALSA: hda - Check...
581
  	return pm_runtime_put_autosuspend(dev);
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
582
583
  }
  EXPORT_SYMBOL_GPL(snd_hdac_power_down);
c3aeda628   Takashi Iwai   ALSA: hda - Fix a...
584
585
586
587
588
589
590
591
592
  
  /**
   * snd_hdac_power_up_pm - power up the codec
   * @codec: the codec object
   *
   * This function can be called in a recursive code path like init code
   * which may be called by PM suspend/resume again.  OTOH, if a power-up
   * call must wake up the sleeper (e.g. in a kctl callback), use
   * snd_hdac_power_up() instead.
fbce23a0b   Takashi Iwai   ALSA: hda - Check...
593
594
   *
   * Returns zero if successful, or a negative error code.
c3aeda628   Takashi Iwai   ALSA: hda - Fix a...
595
   */
fbce23a0b   Takashi Iwai   ALSA: hda - Check...
596
  int snd_hdac_power_up_pm(struct hdac_device *codec)
c3aeda628   Takashi Iwai   ALSA: hda - Fix a...
597
598
  {
  	if (!atomic_inc_not_zero(&codec->in_pm))
fbce23a0b   Takashi Iwai   ALSA: hda - Check...
599
600
  		return snd_hdac_power_up(codec);
  	return 0;
c3aeda628   Takashi Iwai   ALSA: hda - Fix a...
601
602
  }
  EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
fc4f000bf   Takashi Iwai   ALSA: hda - Fix u...
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
  /* like snd_hdac_power_up_pm(), but only increment the pm count when
   * already powered up.  Returns -1 if not powered up, 1 if incremented
   * or 0 if unchanged.  Only used in hdac_regmap.c
   */
  int snd_hdac_keep_power_up(struct hdac_device *codec)
  {
  	if (!atomic_inc_not_zero(&codec->in_pm)) {
  		int ret = pm_runtime_get_if_in_use(&codec->dev);
  		if (!ret)
  			return -1;
  		if (ret < 0)
  			return 0;
  	}
  	return 1;
  }
c3aeda628   Takashi Iwai   ALSA: hda - Fix a...
618
619
620
621
622
623
  /**
   * snd_hdac_power_down_pm - power down the codec
   * @codec: the codec object
   *
   * Like snd_hdac_power_up_pm(), this function is used in a recursive
   * code path like init code which may be called by PM suspend/resume again.
fbce23a0b   Takashi Iwai   ALSA: hda - Check...
624
625
   *
   * Returns zero if successful, or a negative error code.
c3aeda628   Takashi Iwai   ALSA: hda - Fix a...
626
   */
fbce23a0b   Takashi Iwai   ALSA: hda - Check...
627
  int snd_hdac_power_down_pm(struct hdac_device *codec)
c3aeda628   Takashi Iwai   ALSA: hda - Fix a...
628
629
  {
  	if (atomic_dec_if_positive(&codec->in_pm) < 0)
fbce23a0b   Takashi Iwai   ALSA: hda - Check...
630
631
  		return snd_hdac_power_down(codec);
  	return 0;
c3aeda628   Takashi Iwai   ALSA: hda - Fix a...
632
633
  }
  EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
634
635
636
637
638
639
640
  #endif
  
  /* codec vendor labels */
  struct hda_vendor_id {
  	unsigned int id;
  	const char *name;
  };
bf82326fc   Takashi Iwai   ALSA: hda: More c...
641
  static const struct hda_vendor_id hda_vendor_ids[] = {
7639a06c2   Takashi Iwai   ALSA: hda - Move ...
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
  	{ 0x1002, "ATI" },
  	{ 0x1013, "Cirrus Logic" },
  	{ 0x1057, "Motorola" },
  	{ 0x1095, "Silicon Image" },
  	{ 0x10de, "Nvidia" },
  	{ 0x10ec, "Realtek" },
  	{ 0x1102, "Creative" },
  	{ 0x1106, "VIA" },
  	{ 0x111d, "IDT" },
  	{ 0x11c1, "LSI" },
  	{ 0x11d4, "Analog Devices" },
  	{ 0x13f6, "C-Media" },
  	{ 0x14f1, "Conexant" },
  	{ 0x17e8, "Chrontel" },
  	{ 0x1854, "LG" },
  	{ 0x1aec, "Wolfson Microelectronics" },
  	{ 0x1af4, "QEMU" },
  	{ 0x434d, "C-Media" },
  	{ 0x8086, "Intel" },
  	{ 0x8384, "SigmaTel" },
  	{} /* terminator */
  };
  
  /* store the codec vendor name */
  static int get_codec_vendor_name(struct hdac_device *codec)
  {
  	const struct hda_vendor_id *c;
  	u16 vendor_id = codec->vendor_id >> 16;
  
  	for (c = hda_vendor_ids; c->id; c++) {
  		if (c->id == vendor_id) {
  			codec->vendor_name = kstrdup(c->name, GFP_KERNEL);
  			return codec->vendor_name ? 0 : -ENOMEM;
  		}
  	}
  
  	codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id);
  	return codec->vendor_name ? 0 : -ENOMEM;
  }
b7d023e11   Takashi Iwai   ALSA: hda - Move ...
681
682
683
684
685
686
687
688
689
690
691
692
693
694
  
  /*
   * stream formats
   */
  struct hda_rate_tbl {
  	unsigned int hz;
  	unsigned int alsa_bits;
  	unsigned int hda_fmt;
  };
  
  /* rate = base * mult / div */
  #define HDA_RATE(base, mult, div) \
  	(AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
  	 (((div) - 1) << AC_FMT_DIV_SHIFT))
bf82326fc   Takashi Iwai   ALSA: hda: More c...
695
  static const struct hda_rate_tbl rate_bits[] = {
b7d023e11   Takashi Iwai   ALSA: hda - Move ...
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
  	/* rate in Hz, ALSA rate bitmask, HDA format value */
  
  	/* autodetected value used in snd_hda_query_supported_pcm */
  	{ 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) },
  	{ 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) },
  	{ 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) },
  	{ 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) },
  	{ 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) },
  	{ 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) },
  	{ 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) },
  	{ 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) },
  	{ 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) },
  	{ 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) },
  	{ 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) },
  #define AC_PAR_PCM_RATE_BITS	11
  	/* up to bits 10, 384kHZ isn't supported properly */
  
  	/* not autodetected value */
  	{ 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) },
  
  	{ 0 } /* terminator */
  };
  
  /**
   * snd_hdac_calc_stream_format - calculate the format bitset
   * @rate: the sample rate
   * @channels: the number of channels
   * @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
   * @maxbps: the max. bps
   * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant)
   *
   * Calculate the format bitset from the given rate, channels and th PCM format.
   *
   * Return zero if invalid.
   */
  unsigned int snd_hdac_calc_stream_format(unsigned int rate,
  					 unsigned int channels,
a6ea5fe95   Takashi Iwai   ALSA: hda: Fix im...
733
  					 snd_pcm_format_t format,
b7d023e11   Takashi Iwai   ALSA: hda - Move ...
734
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
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
  					 unsigned int maxbps,
  					 unsigned short spdif_ctls)
  {
  	int i;
  	unsigned int val = 0;
  
  	for (i = 0; rate_bits[i].hz; i++)
  		if (rate_bits[i].hz == rate) {
  			val = rate_bits[i].hda_fmt;
  			break;
  		}
  	if (!rate_bits[i].hz)
  		return 0;
  
  	if (channels == 0 || channels > 8)
  		return 0;
  	val |= channels - 1;
  
  	switch (snd_pcm_format_width(format)) {
  	case 8:
  		val |= AC_FMT_BITS_8;
  		break;
  	case 16:
  		val |= AC_FMT_BITS_16;
  		break;
  	case 20:
  	case 24:
  	case 32:
  		if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
  			val |= AC_FMT_BITS_32;
  		else if (maxbps >= 24)
  			val |= AC_FMT_BITS_24;
  		else
  			val |= AC_FMT_BITS_20;
  		break;
  	default:
  		return 0;
  	}
  
  	if (spdif_ctls & AC_DIG1_NONAUDIO)
  		val |= AC_FMT_TYPE_NON_PCM;
  
  	return val;
  }
  EXPORT_SYMBOL_GPL(snd_hdac_calc_stream_format);
  
  static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid)
  {
  	unsigned int val = 0;
  
  	if (nid != codec->afg &&
  	    (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
  		val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM);
  	if (!val || val == -1)
  		val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM);
  	if (!val || val == -1)
  		return 0;
  	return val;
  }
  
  static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid)
  {
  	unsigned int streams = snd_hdac_read_parm(codec, nid, AC_PAR_STREAM);
  
  	if (!streams || streams == -1)
  		streams = snd_hdac_read_parm(codec, codec->afg, AC_PAR_STREAM);
  	if (!streams || streams == -1)
  		return 0;
  	return streams;
  }
  
  /**
   * snd_hdac_query_supported_pcm - query the supported PCM rates and formats
   * @codec: the codec object
   * @nid: NID to query
   * @ratesp: the pointer to store the detected rate bitflags
   * @formatsp: the pointer to store the detected formats
   * @bpsp: the pointer to store the detected format widths
   *
   * Queries the supported PCM rates and formats.  The NULL @ratesp, @formatsp
   * or @bsps argument is ignored.
   *
   * Returns 0 if successful, otherwise a negative error code.
   */
  int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
  				 u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
  {
  	unsigned int i, val, wcaps;
  
  	wcaps = get_wcaps(codec, nid);
  	val = query_pcm_param(codec, nid);
  
  	if (ratesp) {
  		u32 rates = 0;
  		for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) {
  			if (val & (1 << i))
  				rates |= rate_bits[i].alsa_bits;
  		}
  		if (rates == 0) {
  			dev_err(&codec->dev,
  				"rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)
  ",
  				nid, val,
  				(wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
  			return -EIO;
  		}
  		*ratesp = rates;
  	}
  
  	if (formatsp || bpsp) {
  		u64 formats = 0;
  		unsigned int streams, bps;
  
  		streams = query_stream_param(codec, nid);
  		if (!streams)
  			return -EIO;
  
  		bps = 0;
  		if (streams & AC_SUPFMT_PCM) {
  			if (val & AC_SUPPCM_BITS_8) {
  				formats |= SNDRV_PCM_FMTBIT_U8;
  				bps = 8;
  			}
  			if (val & AC_SUPPCM_BITS_16) {
  				formats |= SNDRV_PCM_FMTBIT_S16_LE;
  				bps = 16;
  			}
  			if (wcaps & AC_WCAP_DIGITAL) {
  				if (val & AC_SUPPCM_BITS_32)
  					formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
  				if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))
  					formats |= SNDRV_PCM_FMTBIT_S32_LE;
  				if (val & AC_SUPPCM_BITS_24)
  					bps = 24;
  				else if (val & AC_SUPPCM_BITS_20)
  					bps = 20;
  			} else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|
  					  AC_SUPPCM_BITS_32)) {
  				formats |= SNDRV_PCM_FMTBIT_S32_LE;
  				if (val & AC_SUPPCM_BITS_32)
  					bps = 32;
  				else if (val & AC_SUPPCM_BITS_24)
  					bps = 24;
  				else if (val & AC_SUPPCM_BITS_20)
  					bps = 20;
  			}
  		}
  #if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
  		if (streams & AC_SUPFMT_FLOAT32) {
  			formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
  			if (!bps)
  				bps = 32;
  		}
  #endif
  		if (streams == AC_SUPFMT_AC3) {
  			/* should be exclusive */
  			/* temporary hack: we have still no proper support
  			 * for the direct AC3 stream...
  			 */
  			formats |= SNDRV_PCM_FMTBIT_U8;
  			bps = 8;
  		}
  		if (formats == 0) {
  			dev_err(&codec->dev,
  				"formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)
  ",
  				nid, val,
  				(wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
  				streams);
  			return -EIO;
  		}
  		if (formatsp)
  			*formatsp = formats;
  		if (bpsp)
  			*bpsp = bps;
  	}
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(snd_hdac_query_supported_pcm);
  
  /**
   * snd_hdac_is_supported_format - Check the validity of the format
   * @codec: the codec object
   * @nid: NID to check
   * @format: the HD-audio format value to check
   *
   * Check whether the given node supports the format value.
   *
   * Returns true if supported, false if not.
   */
  bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
  				  unsigned int format)
  {
  	int i;
  	unsigned int val = 0, rate, stream;
  
  	val = query_pcm_param(codec, nid);
  	if (!val)
  		return false;
  
  	rate = format & 0xff00;
  	for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
  		if (rate_bits[i].hda_fmt == rate) {
  			if (val & (1 << i))
  				break;
  			return false;
  		}
  	if (i >= AC_PAR_PCM_RATE_BITS)
  		return false;
  
  	stream = query_stream_param(codec, nid);
  	if (!stream)
  		return false;
  
  	if (stream & AC_SUPFMT_PCM) {
  		switch (format & 0xf0) {
  		case 0x00:
  			if (!(val & AC_SUPPCM_BITS_8))
  				return false;
  			break;
  		case 0x10:
  			if (!(val & AC_SUPPCM_BITS_16))
  				return false;
  			break;
  		case 0x20:
  			if (!(val & AC_SUPPCM_BITS_20))
  				return false;
  			break;
  		case 0x30:
  			if (!(val & AC_SUPPCM_BITS_24))
  				return false;
  			break;
  		case 0x40:
  			if (!(val & AC_SUPPCM_BITS_32))
  				return false;
  			break;
  		default:
  			return false;
  		}
  	} else {
  		/* FIXME: check for float32 and AC3? */
  	}
  
  	return true;
  }
  EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
1b5e6167c   Subhransu S. Prusty   ALSA: hdac: Copy ...
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
  
  static unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid,
  			int flags, unsigned int verb, unsigned int parm)
  {
  	unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
  	unsigned int res;
  
  	if (snd_hdac_exec_verb(hdac, cmd, flags, &res))
  		return -1;
  
  	return res;
  }
  
  static int codec_write(struct hdac_device *hdac, hda_nid_t nid,
  			int flags, unsigned int verb, unsigned int parm)
  {
  	unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
  
  	return snd_hdac_exec_verb(hdac, cmd, flags, NULL);
  }
  
  /**
   * snd_hdac_codec_read - send a command and get the response
   * @hdac: the HDAC device
   * @nid: NID to send the command
   * @flags: optional bit flags
   * @verb: the verb to send
   * @parm: the parameter for the verb
   *
   * Send a single command and read the corresponding response.
   *
   * Returns the obtained response value, or -1 for an error.
   */
  int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
  			int flags, unsigned int verb, unsigned int parm)
  {
  	return codec_read(hdac, nid, flags, verb, parm);
  }
  EXPORT_SYMBOL_GPL(snd_hdac_codec_read);
  
  /**
   * snd_hdac_codec_write - send a single command without waiting for response
   * @hdac: the HDAC device
   * @nid: NID to send the command
   * @flags: optional bit flags
   * @verb: the verb to send
   * @parm: the parameter for the verb
   *
   * Send a single command without waiting for response.
   *
   * Returns 0 if successful, or a negative error code.
   */
  int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
  			int flags, unsigned int verb, unsigned int parm)
  {
  	return codec_write(hdac, nid, flags, verb, parm);
  }
  EXPORT_SYMBOL_GPL(snd_hdac_codec_write);
78dd5e21b   Takashi Iwai   ALSA: hda - Add /...
1039
1040
  /**
   * snd_hdac_check_power_state - check whether the actual power state matches
1b5e6167c   Subhransu S. Prusty   ALSA: hdac: Copy ...
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
   * with the target state
   *
   * @hdac: the HDAC device
   * @nid: NID to send the command
   * @target_state: target state to check for
   *
   * Return true if state matches, false if not
   */
  bool snd_hdac_check_power_state(struct hdac_device *hdac,
  		hda_nid_t nid, unsigned int target_state)
  {
  	unsigned int state = codec_read(hdac, nid, 0,
  				AC_VERB_GET_POWER_STATE, 0);
  
  	if (state & AC_PWRST_ERROR)
  		return true;
  	state = (state >> 4) & 0x0f;
  	return (state == target_state);
  }
  EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
097874925   Abhijeet Kumar   ALSA: hda: Copyin...
1061
1062
1063
1064
  /**
   * snd_hdac_sync_power_state - wait until actual power state matches
   * with the target state
   *
6e57188f2   Keyon Jie   ALSA: hda: Update...
1065
   * @codec: the HDAC device
097874925   Abhijeet Kumar   ALSA: hda: Copyin...
1066
   * @nid: NID to send the command
6e57188f2   Keyon Jie   ALSA: hda: Update...
1067
   * @power_state: target power state to wait for
097874925   Abhijeet Kumar   ALSA: hda: Copyin...
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
   *
   * Return power state or PS_ERROR if codec rejects GET verb.
   */
  unsigned int snd_hdac_sync_power_state(struct hdac_device *codec,
  			hda_nid_t nid, unsigned int power_state)
  {
  	unsigned long end_time = jiffies + msecs_to_jiffies(500);
  	unsigned int state, actual_state, count;
  
  	for (count = 0; count < 500; count++) {
  		state = snd_hdac_codec_read(codec, nid, 0,
  				AC_VERB_GET_POWER_STATE, 0);
  		if (state & AC_PWRST_ERROR) {
  			msleep(20);
  			break;
  		}
  		actual_state = (state >> 4) & 0x0f;
  		if (actual_state == power_state)
  			break;
  		if (time_after_eq(jiffies, end_time))
  			break;
  		/* wait until the codec reachs to the target state */
  		msleep(1);
  	}
  	return state;
  }
  EXPORT_SYMBOL_GPL(snd_hdac_sync_power_state);