blob: 691ca218f8553b444b92ffd3b40f2328417c388a [file] [log] [blame]
Bharath Ramachandramurthy2e3168f2012-05-03 16:29:09 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/slab.h>
14#include <linux/wait.h>
15#include <linux/sched.h>
16#include <linux/jiffies.h>
17#include <linux/uaccess.h>
18#include <linux/atomic.h>
19
20
21#include <mach/qdsp6v2/audio_acdb.h>
22#include <mach/qdsp6v2/rtac.h>
23
24#include <sound/apr_audio-v2.h>
25#include <mach/qdsp6v2/apr.h>
26#include <sound/q6adm-v2.h>
27#include <sound/q6audio-v2.h>
28
29
30#define TIMEOUT_MS 1000
31
32#define RESET_COPP_ID 99
33#define INVALID_COPP_ID 0xFF
34
35struct adm_ctl {
36 void *apr;
37 atomic_t copp_id[Q6_AFE_MAX_PORTS];
38 atomic_t copp_cnt[Q6_AFE_MAX_PORTS];
39 atomic_t copp_stat[Q6_AFE_MAX_PORTS];
40 u32 mem_map_handle[Q6_AFE_MAX_PORTS];
41 wait_queue_head_t wait[Q6_AFE_MAX_PORTS];
42};
43
44static struct adm_ctl this_adm;
45
46static int32_t adm_callback(struct apr_client_data *data, void *priv)
47{
48 uint32_t *payload;
49 int i, index;
50 payload = data->payload;
51
52 if (data->opcode == RESET_EVENTS) {
53 pr_debug("adm_callback: Reset event is received: %d %d apr[%p]\n",
54 data->reset_event, data->reset_proc,
55 this_adm.apr);
56 if (this_adm.apr) {
57 apr_reset(this_adm.apr);
58 for (i = 0; i < Q6_AFE_MAX_PORTS; i++) {
59 atomic_set(&this_adm.copp_id[i],
60 RESET_COPP_ID);
61 atomic_set(&this_adm.copp_cnt[i], 0);
62 atomic_set(&this_adm.copp_stat[i], 0);
63 }
64 this_adm.apr = NULL;
65 }
66 return 0;
67 }
68
69 pr_debug("%s: code = 0x%x PL#0[%x], PL#1[%x], size = %d\n", __func__,
70 data->opcode, payload[0], payload[1],
71 data->payload_size);
72
73 if (data->payload_size) {
74 index = q6audio_get_port_index(data->token);
75 if (index < 0 || index >= Q6_AFE_MAX_PORTS) {
76 pr_err("%s: invalid port idx %d token %d\n",
77 __func__, index, data->token);
78 return 0;
79 }
80 if (data->opcode == APR_BASIC_RSP_RESULT) {
81 pr_debug("APR_BASIC_RSP_RESULT\n");
82 switch (payload[0]) {
83 case ADM_CMD_SET_PP_PARAMS_V5:
84 if (rtac_make_adm_callback(
85 payload, data->payload_size))
86 pr_debug("%s: payload[0]: 0x%x\n",
87 __func__, payload[0]);
88 break;
89 case ADM_CMD_DEVICE_CLOSE_V5:
90 case ADM_CMD_SHARED_MEM_UNMAP_REGIONS:
91 case ADM_CMD_SHARED_MEM_MAP_REGIONS:
92 case ADM_CMD_MATRIX_MAP_ROUTINGS_V5:
93 pr_debug("ADM_CMD_MATRIX_MAP_ROUTINGS\n");
94 atomic_set(&this_adm.copp_stat[index], 1);
95 wake_up(&this_adm.wait[index]);
96 break;
97 default:
98 pr_err("%s: Unknown Cmd: 0x%x\n", __func__,
99 payload[0]);
100 break;
101 }
102 return 0;
103 }
104
105 switch (data->opcode) {
106 case ADM_CMDRSP_DEVICE_OPEN_V5: {
107 struct adm_cmd_rsp_device_open_v5 *open =
108 (struct adm_cmd_rsp_device_open_v5 *)data->payload;
109 if (open->copp_id == INVALID_COPP_ID) {
110 pr_err("%s: invalid coppid rxed %d\n",
111 __func__, open->copp_id);
112 atomic_set(&this_adm.copp_stat[index], 1);
113 wake_up(&this_adm.wait[index]);
114 break;
115 }
116 atomic_set(&this_adm.copp_id[index], open->copp_id);
117 atomic_set(&this_adm.copp_stat[index], 1);
118 pr_debug("%s: coppid rxed=%d\n", __func__,
119 open->copp_id);
120 wake_up(&this_adm.wait[index]);
121 }
122 break;
123 case ADM_CMD_GET_PP_PARAMS_V5:
124 pr_debug("%s: ADM_CMD_GET_PP_PARAMS_V5\n", __func__);
125 rtac_make_adm_callback(payload,
126 data->payload_size);
127 break;
128 default:
129 pr_err("%s: Unknown cmd:0x%x\n", __func__,
130 data->opcode);
131 break;
132 }
133 }
134 return 0;
135}
136
137/* TODO: send_adm_cal_block function to be defined
138 when calibration available for 8974 */
139static void send_adm_cal(int port_id, int path)
140{
141 /* function to be defined when calibration available for 8974 */
142 pr_debug("%s\n", __func__);
143}
144
145int adm_open(int port_id, int path, int rate, int channel_mode, int topology)
146{
147 struct adm_cmd_device_open_v5 open;
148 int ret = 0;
149 int index;
150 int tmp_port = q6audio_get_port_id(port_id);
151
152 pr_debug("%s: port %d path:%d rate:%d mode:%d\n", __func__,
153 port_id, path, rate, channel_mode);
154
155 port_id = q6audio_convert_virtual_to_portid(port_id);
156
157 if (q6audio_validate_port(port_id) < 0) {
158 pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
159 return -ENODEV;
160 }
161
162 index = q6audio_get_port_index(port_id);
163 pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
164
165 if (this_adm.apr == NULL) {
166 this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
167 0xFFFFFFFF, &this_adm);
168 if (this_adm.apr == NULL) {
169 pr_err("%s: Unable to register ADM\n", __func__);
170 ret = -ENODEV;
171 return ret;
172 }
173 rtac_set_adm_handle(this_adm.apr);
174 }
175
176
177 /* Create a COPP if port id are not enabled */
178 if (atomic_read(&this_adm.copp_cnt[index]) == 0) {
179
180 open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
181 APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
182 open.hdr.pkt_size = sizeof(open);
183 open.hdr.src_svc = APR_SVC_ADM;
184 open.hdr.src_domain = APR_DOMAIN_APPS;
185 open.hdr.src_port = tmp_port;
186 open.hdr.dest_svc = APR_SVC_ADM;
187 open.hdr.dest_domain = APR_DOMAIN_ADSP;
188 open.hdr.dest_port = tmp_port;
189 open.hdr.token = port_id;
190 open.hdr.opcode = ADM_CMD_DEVICE_OPEN_V5;
191
192 open.mode_of_operation = path;
193 /* Reserved for future use, need to set this to 0 */
194 open.flags = 0x00;
195 open.endpoint_id_1 = tmp_port;
196 open.endpoint_id_2 = 0xFFFF;
197
198 /* convert path to acdb path */
199 if (path == ADM_PATH_PLAYBACK)
200 open.topology_id = get_adm_rx_topology();
201 else {
202 open.topology_id = get_adm_tx_topology();
203 if ((open.topology_id ==
204 VPM_TX_SM_ECNS_COPP_TOPOLOGY) ||
205 (open.topology_id ==
206 VPM_TX_DM_FLUENCE_COPP_TOPOLOGY))
207 rate = 16000;
208 }
209
210 if (open.topology_id == 0)
211 open.topology_id = topology;
212
213 open.dev_num_channel = channel_mode & 0x00FF;
214 open.bit_width = 16;
215 open.sample_rate = rate;
216 memset(open.dev_channel_mapping, 0, 8);
217
218 if (channel_mode == 1) {
219 open.dev_channel_mapping[0] = PCM_CHANNEL_FC;
220 } else if (channel_mode == 2) {
221 open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
222 open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
223 } else if (channel_mode == 6) {
224 open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
225 open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
226 open.dev_channel_mapping[2] = PCM_CHANNEL_LFE;
227 open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
228 open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
229 open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
230 } else {
231 pr_err("%s invalid num_chan %d\n", __func__,
232 channel_mode);
233 return -EINVAL;
234 }
235
236 pr_debug("%s: port_id=%d rate=%d"
237 "topology_id=0x%X\n", __func__, open.endpoint_id_1, \
238 open.sample_rate, open.topology_id);
239
240 atomic_set(&this_adm.copp_stat[index], 0);
241
242 ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
243 if (ret < 0) {
244 pr_err("%s:ADM enable for port %d for[%d] failed\n",
245 __func__, tmp_port, port_id);
246 ret = -EINVAL;
247 goto fail_cmd;
248 }
249 /* Wait for the callback with copp id */
250 ret = wait_event_timeout(this_adm.wait[index],
251 atomic_read(&this_adm.copp_stat[index]),
252 msecs_to_jiffies(TIMEOUT_MS));
253 if (!ret) {
254 pr_err("%s ADM open failed for port %d"
255 "for [%d]\n", __func__, tmp_port, port_id);
256 ret = -EINVAL;
257 goto fail_cmd;
258 }
259 }
260 atomic_inc(&this_adm.copp_cnt[index]);
261 return 0;
262
263fail_cmd:
264
265 return ret;
266}
267
268
269int adm_multi_ch_copp_open(int port_id, int path, int rate, int channel_mode,
270 int topology)
271{
272 int ret = 0;
273
274 ret = adm_open(port_id, path, rate, channel_mode, topology);
275
276 return ret;
277}
278
279int adm_matrix_map(int session_id, int path, int num_copps,
280 unsigned int *port_id, int copp_id)
281{
282 struct adm_cmd_matrix_map_routings_v5 *route;
283 struct adm_session_map_node_v5 *node;
284 uint32_t *copps_list;
285 int cmd_size = 0;
286 int ret = 0, i = 0;
287 void *payload = NULL;
288 void *matrix_map = NULL;
289
290 /* Assumes port_ids have already been validated during adm_open */
291 int index = q6audio_get_port_index(copp_id);
292 if (index < 0 || index >= Q6_AFE_MAX_PORTS) {
293 pr_err("%s: invalid port idx %d token %d\n",
294 __func__, index, copp_id);
295 return 0;
296 }
297 cmd_size = (sizeof(struct adm_cmd_matrix_map_routings_v5) +
298 sizeof(struct adm_session_map_node_v5) +
299 (sizeof(uint32_t) * num_copps));
300 matrix_map = kzalloc(cmd_size, GFP_KERNEL);
301 if (matrix_map == NULL) {
302 pr_err("%s: Mem alloc failed\n", __func__);
303 ret = -EINVAL;
304 return ret;
305 }
306 route = (struct adm_cmd_matrix_map_routings_v5 *)matrix_map;
307
308 pr_debug("%s: session 0x%x path:%d num_copps:%d port_id[0] :%d coppid[%d]\n",
309 __func__, session_id, path, num_copps, port_id[0], copp_id);
310
311 route->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
312 APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
313 route->hdr.pkt_size = cmd_size;
314 route->hdr.src_svc = 0;
315 route->hdr.src_domain = APR_DOMAIN_APPS;
316 route->hdr.src_port = copp_id;
317 route->hdr.dest_svc = APR_SVC_ADM;
318 route->hdr.dest_domain = APR_DOMAIN_ADSP;
319 route->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
320 route->hdr.token = copp_id;
321 route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
322 route->num_sessions = 1;
323
324 switch (path) {
325 case 0x1:
326 route->matrix_id = ADM_MATRIX_ID_AUDIO_RX;
327 break;
328 case 0x2:
329 case 0x3:
330 route->matrix_id = ADM_MATRIX_ID_AUDIO_TX;
331 break;
332 default:
333 pr_err("%s: Wrong path set[%d]\n", __func__, path);
334 break;
335 }
336 payload = ((u8 *)matrix_map +
337 sizeof(struct adm_cmd_matrix_map_routings_v5));
338 node = (struct adm_session_map_node_v5 *)payload;
339
340 node->session_id = session_id;
341 node->num_copps = num_copps;
342 payload = (u8 *)node + sizeof(struct adm_session_map_node_v5);
343 copps_list = (uint32_t *)payload;
344 for (i = 0; i < num_copps; i++) {
345 int tmp;
346 port_id[i] = q6audio_convert_virtual_to_portid(port_id[i]);
347
348 tmp = q6audio_get_port_index(port_id[i]);
349
350
351 if (tmp >= 0 && tmp < Q6_AFE_MAX_PORTS)
352 copps_list[i] =
353 atomic_read(&this_adm.copp_id[tmp]);
354 pr_debug("%s: port_id[%d]: %d, index: %d act coppid[0x%x]\n",
355 __func__, i, port_id[i], tmp,
356 atomic_read(&this_adm.copp_id[tmp]));
357 }
358 atomic_set(&this_adm.copp_stat[index], 0);
359
360 ret = apr_send_pkt(this_adm.apr, (uint32_t *)matrix_map);
361 if (ret < 0) {
362 pr_err("%s: ADM routing for port %d failed\n",
363 __func__, port_id[0]);
364 ret = -EINVAL;
365 goto fail_cmd;
366 }
367 ret = wait_event_timeout(this_adm.wait[index],
368 atomic_read(&this_adm.copp_stat[index]),
369 msecs_to_jiffies(TIMEOUT_MS));
370 if (!ret) {
371 pr_err("%s: ADM cmd Route failed for port %d\n",
372 __func__, port_id[0]);
373 ret = -EINVAL;
374 goto fail_cmd;
375 }
376 for (i = 0; i < num_copps; i++)
377 send_adm_cal(port_id[i], path);
378
379fail_cmd:
380 kfree(matrix_map);
381 return ret;
382}
383
384int adm_memory_map_regions(int port_id,
385 uint32_t *buf_add, uint32_t mempool_id,
386 uint32_t *bufsz, uint32_t bufcnt)
387{
388 struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
389 struct avs_shared_map_region_payload *mregions = NULL;
390 void *mmap_region_cmd = NULL;
391 void *payload = NULL;
392 int ret = 0;
393 int i = 0;
394 int cmd_size = 0;
395 int index = 0;
396
397 pr_debug("%s\n", __func__);
398 if (this_adm.apr == NULL) {
399 this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
400 0xFFFFFFFF, &this_adm);
401 if (this_adm.apr == NULL) {
402 pr_err("%s: Unable to register ADM\n", __func__);
403 ret = -ENODEV;
404 return ret;
405 }
406 rtac_set_adm_handle(this_adm.apr);
407 }
408
409 port_id = q6audio_convert_virtual_to_portid(port_id);
410
411 if (q6audio_validate_port(port_id) < 0) {
412 pr_err("%s port id[%d] is invalid\n", __func__, port_id);
413 return -ENODEV;
414 }
415
416 index = q6audio_get_port_index(port_id);
417
418 cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions)
419 + sizeof(struct avs_shared_map_region_payload)
420 * bufcnt;
421
422 mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
423 if (!mmap_region_cmd) {
424 pr_err("%s: allocate mmap_region_cmd failed\n", __func__);
425 return -ENOMEM;
426 }
427 mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd;
428 mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
429 APR_HDR_LEN(APR_HDR_SIZE),
430 APR_PKT_VER);
431 mmap_regions->hdr.pkt_size = cmd_size;
432 mmap_regions->hdr.src_port = 0;
433 mmap_regions->hdr.dest_port = 0;
434 mmap_regions->hdr.token = 0;
435 mmap_regions->hdr.opcode = ADM_CMD_SHARED_MEM_MAP_REGIONS;
436 mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_EBI_POOL & 0x00ff;
437 mmap_regions->num_regions = bufcnt & 0x00ff;
438 mmap_regions->property_flag = 0x00;
439
440 pr_debug("%s: map_regions->num_regions = %d\n", __func__,
441 mmap_regions->num_regions);
442 payload = ((u8 *) mmap_region_cmd +
443 sizeof(struct avs_cmd_shared_mem_map_regions));
444 mregions = (struct avs_shared_map_region_payload *)payload;
445
446 for (i = 0; i < bufcnt; i++) {
447 mregions->shm_addr_lsw = buf_add[i];
448 mregions->shm_addr_msw = 0x00;
449 mregions->mem_size_bytes = bufsz[i];
450 ++mregions;
451 }
452
453 atomic_set(&this_adm.copp_stat[0], 0);
454 ret = apr_send_pkt(this_adm.apr, (uint32_t *) mmap_region_cmd);
455 if (ret < 0) {
456 pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
457 mmap_regions->hdr.opcode, ret);
458 ret = -EINVAL;
459 goto fail_cmd;
460 }
461
462 ret = wait_event_timeout(this_adm.wait[index],
463 atomic_read(&this_adm.copp_stat[0]), 5 * HZ);
464 if (!ret) {
465 pr_err("%s: timeout. waited for memory_map\n", __func__);
466 ret = -EINVAL;
467 goto fail_cmd;
468 }
469fail_cmd:
470 kfree(mmap_region_cmd);
471 return ret;
472}
473
474int adm_memory_unmap_regions(int32_t port_id, uint32_t *buf_add,
475 uint32_t *bufsz, uint32_t bufcnt)
476{
477 struct avs_cmd_shared_mem_unmap_regions unmap_regions;
478 int ret = 0;
479 int cmd_size = 0;
480 int index = 0;
481
482 pr_debug("%s\n", __func__);
483
484 if (this_adm.apr == NULL) {
485 pr_err("%s APR handle NULL\n", __func__);
486 return -EINVAL;
487 }
488 port_id = q6audio_convert_virtual_to_portid(port_id);
489
490 if (q6audio_validate_port(port_id) < 0) {
491 pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
492 return -ENODEV;
493 }
494
495 index = q6audio_get_port_index(port_id);
496
497 unmap_regions.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
498 APR_HDR_LEN(APR_HDR_SIZE),
499 APR_PKT_VER);
500 unmap_regions.hdr.pkt_size = cmd_size;
501 unmap_regions.hdr.src_port = 0;
502 unmap_regions.hdr.dest_port = 0;
503 unmap_regions.hdr.token = 0;
504 unmap_regions.hdr.opcode = ADM_CMD_SHARED_MEM_UNMAP_REGIONS;
505 unmap_regions.mem_map_handle = this_adm.mem_map_handle[index];
506 atomic_set(&this_adm.copp_stat[0], 0);
507 ret = apr_send_pkt(this_adm.apr, (uint32_t *) &unmap_regions);
508 if (ret < 0) {
509 pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
510 unmap_regions.hdr.opcode, ret);
511 ret = -EINVAL;
512 goto fail_cmd;
513 }
514
515 ret = wait_event_timeout(this_adm.wait[index],
516 atomic_read(&this_adm.copp_stat[0]), 5 * HZ);
517 if (!ret) {
518 pr_err("%s: timeout. waited for memory_unmap\n", __func__);
519 ret = -EINVAL;
520 goto fail_cmd;
521 }
522fail_cmd:
523 return ret;
524}
525
526int adm_get_copp_id(int port_index)
527{
528 pr_debug("%s\n", __func__);
529
530 if (port_index < 0) {
531 pr_err("%s: invalid port_id = %d\n", __func__, port_index);
532 return -EINVAL;
533 }
534
535 return atomic_read(&this_adm.copp_id[port_index]);
536}
537
538int adm_close(int port_id)
539{
540 struct apr_hdr close;
541
542 int ret = 0;
543 int index = 0;
544
545 port_id = q6audio_convert_virtual_to_portid(port_id);
546
547 index = q6audio_get_port_index(port_id);
548 if (q6audio_validate_port(port_id) < 0)
549 return -EINVAL;
550
551 pr_debug("%s port_id=%d index %d\n", __func__, port_id, index);
552
553 if (!(atomic_read(&this_adm.copp_cnt[index]))) {
554 pr_err("%s: copp count for port[%d]is 0\n", __func__, port_id);
555
556 goto fail_cmd;
557 }
558 atomic_dec(&this_adm.copp_cnt[index]);
559 if (!(atomic_read(&this_adm.copp_cnt[index]))) {
560
561 close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
562 APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
563 close.pkt_size = sizeof(close);
564 close.src_svc = APR_SVC_ADM;
565 close.src_domain = APR_DOMAIN_APPS;
566 close.src_port = port_id;
567 close.dest_svc = APR_SVC_ADM;
568 close.dest_domain = APR_DOMAIN_ADSP;
569 close.dest_port = atomic_read(&this_adm.copp_id[index]);
570 close.token = port_id;
571 close.opcode = ADM_CMD_DEVICE_CLOSE_V5;
572
573 atomic_set(&this_adm.copp_id[index], RESET_COPP_ID);
574 atomic_set(&this_adm.copp_stat[index], 0);
575
576
577 pr_debug("%s:coppid %d portid=%d index=%d coppcnt=%d\n",
578 __func__,
579 atomic_read(&this_adm.copp_id[index]),
580 port_id, index,
581 atomic_read(&this_adm.copp_cnt[index]));
582
583 ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close);
584 if (ret < 0) {
585 pr_err("%s ADM close failed\n", __func__);
586 ret = -EINVAL;
587 goto fail_cmd;
588 }
589
590 ret = wait_event_timeout(this_adm.wait[index],
591 atomic_read(&this_adm.copp_stat[index]),
592 msecs_to_jiffies(TIMEOUT_MS));
593 if (!ret) {
594 pr_err("%s: ADM cmd Route failed for port %d\n",
595 __func__, port_id);
596 ret = -EINVAL;
597 goto fail_cmd;
598 }
599
600 rtac_remove_adm_device(port_id);
601 }
602
603fail_cmd:
604 return ret;
605}
606
607static int __init adm_init(void)
608{
609 int i = 0;
610 this_adm.apr = NULL;
611
612 for (i = 0; i < Q6_AFE_MAX_PORTS; i++) {
613 atomic_set(&this_adm.copp_id[i], RESET_COPP_ID);
614 atomic_set(&this_adm.copp_cnt[i], 0);
615 atomic_set(&this_adm.copp_stat[i], 0);
616 init_waitqueue_head(&this_adm.wait[i]);
617 }
618 return 0;
619}
620
621device_initcall(adm_init);