OSDN Git Service

8960bf4ff459c28c8aee44259767cb49ba9d3eb2
[uclinux-h8/linux.git] / drivers / gpu / drm / nouveau / nvkm / engine / pm / base.c
1 /*
2  * Copyright 2013 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Ben Skeggs
23  */
24 #include "priv.h"
25
26 #include <core/client.h>
27 #include <core/device.h>
28 #include <core/option.h>
29
30 #include <nvif/class.h>
31 #include <nvif/ioctl.h>
32 #include <nvif/unpack.h>
33
34 static u8
35 nvkm_pm_count_perfdom(struct nvkm_pm *ppm)
36 {
37         struct nvkm_perfdom *dom;
38         u8 domain_nr = 0;
39
40         list_for_each_entry(dom, &ppm->domains, head)
41                 domain_nr++;
42         return domain_nr;
43 }
44
45 static u16
46 nvkm_perfdom_count_perfsig(struct nvkm_perfdom *dom)
47 {
48         u16 signal_nr = 0;
49         int i;
50
51         if (dom) {
52                 for (i = 0; i < dom->signal_nr; i++) {
53                         if (dom->signal[i].name)
54                                 signal_nr++;
55                 }
56         }
57         return signal_nr;
58 }
59
60 static struct nvkm_perfdom *
61 nvkm_perfdom_find(struct nvkm_pm *ppm, int di)
62 {
63         struct nvkm_perfdom *dom;
64         int tmp = 0;
65
66         list_for_each_entry(dom, &ppm->domains, head) {
67                 if (tmp++ == di)
68                         return dom;
69         }
70         return NULL;
71 }
72
73 struct nvkm_perfsig *
74 nvkm_perfsig_find(struct nvkm_pm *ppm, uint8_t di, uint8_t si,
75                   struct nvkm_perfdom **pdom)
76 {
77         struct nvkm_perfdom *dom = *pdom;
78
79         if (dom == NULL) {
80                 dom = nvkm_perfdom_find(ppm, di);
81                 if (dom == NULL)
82                         return NULL;
83                 *pdom = dom;
84         }
85
86         if (!dom->signal[si].name)
87                 return NULL;
88         return &dom->signal[si];
89 }
90
91 static u8
92 nvkm_perfsig_count_perfsrc(struct nvkm_perfsig *sig)
93 {
94         u8 source_nr = 0, i;
95
96         for (i = 0; i < ARRAY_SIZE(sig->source); i++) {
97                 if (sig->source[i])
98                         source_nr++;
99         }
100         return source_nr;
101 }
102
103 static struct nvkm_perfsrc *
104 nvkm_perfsrc_find(struct nvkm_pm *ppm, struct nvkm_perfsig *sig, int si)
105 {
106         struct nvkm_perfsrc *src;
107         bool found = false;
108         int tmp = 1; /* Sources ID start from 1 */
109         u8 i;
110
111         for (i = 0; i < ARRAY_SIZE(sig->source) && sig->source[i]; i++) {
112                 if (sig->source[i] == si) {
113                         found = true;
114                         break;
115                 }
116         }
117
118         if (found) {
119                 list_for_each_entry(src, &ppm->sources, head) {
120                         if (tmp++ == si)
121                                 return src;
122                 }
123         }
124
125         return NULL;
126 }
127
128 /*******************************************************************************
129  * Perfmon object classes
130  ******************************************************************************/
131 static int
132 nvkm_perfmon_mthd_query_domain(struct nvkm_object *object, void *data, u32 size)
133 {
134         union {
135                 struct nvif_perfmon_query_domain_v0 v0;
136         } *args = data;
137         struct nvkm_pm *ppm = (void *)object->engine;
138         struct nvkm_perfdom *dom;
139         u8 domain_nr;
140         int di, ret;
141
142         nv_ioctl(object, "perfmon query domain size %d\n", size);
143         if (nvif_unpack(args->v0, 0, 0, false)) {
144                 nv_ioctl(object, "perfmon domain vers %d iter %02x\n",
145                          args->v0.version, args->v0.iter);
146                 di = (args->v0.iter & 0xff) - 1;
147         } else
148                 return ret;
149
150         domain_nr = nvkm_pm_count_perfdom(ppm);
151         if (di >= (int)domain_nr)
152                 return -EINVAL;
153
154         if (di >= 0) {
155                 dom = nvkm_perfdom_find(ppm, di);
156                 if (dom == NULL)
157                         return -EINVAL;
158
159                 args->v0.id         = di;
160                 args->v0.signal_nr  = nvkm_perfdom_count_perfsig(dom);
161
162                 /* Currently only global counters (PCOUNTER) are implemented
163                  * but this will be different for local counters (MP). */
164                 args->v0.counter_nr = 4;
165         }
166
167         if (++di < domain_nr) {
168                 args->v0.iter = ++di;
169                 return 0;
170         }
171
172         args->v0.iter = 0xff;
173         return 0;
174 }
175
176 static int
177 nvkm_perfmon_mthd_query_signal(struct nvkm_object *object, void *data, u32 size)
178 {
179         union {
180                 struct nvif_perfmon_query_signal_v0 v0;
181         } *args = data;
182         struct nvkm_device *device = nv_device(object);
183         struct nvkm_pm *ppm = (void *)object->engine;
184         struct nvkm_perfdom *dom;
185         struct nvkm_perfsig *sig;
186         const bool all = nvkm_boolopt(device->cfgopt, "NvPmShowAll", false);
187         const bool raw = nvkm_boolopt(device->cfgopt, "NvPmUnnamed", all);
188         int ret, si;
189
190         nv_ioctl(object, "perfmon query signal size %d\n", size);
191         if (nvif_unpack(args->v0, 0, 0, false)) {
192                 nv_ioctl(object,
193                          "perfmon query signal vers %d dom %d iter %04x\n",
194                          args->v0.version, args->v0.domain, args->v0.iter);
195                 si = (args->v0.iter & 0xffff) - 1;
196         } else
197                 return ret;
198
199         dom = nvkm_perfdom_find(ppm, args->v0.domain);
200         if (dom == NULL || si >= (int)dom->signal_nr)
201                 return -EINVAL;
202
203         if (si >= 0) {
204                 sig = &dom->signal[si];
205                 if (raw || !sig->name) {
206                         snprintf(args->v0.name, sizeof(args->v0.name),
207                                  "/%s/%02x", dom->name, si);
208                 } else {
209                         strncpy(args->v0.name, sig->name,
210                                 sizeof(args->v0.name));
211                 }
212
213                 args->v0.signal = si;
214                 args->v0.source_nr = nvkm_perfsig_count_perfsrc(sig);
215         }
216
217         while (++si < dom->signal_nr) {
218                 if (all || dom->signal[si].name) {
219                         args->v0.iter = ++si;
220                         return 0;
221                 }
222         }
223
224         args->v0.iter = 0xffff;
225         return 0;
226 }
227
228 static int
229 nvkm_perfmon_mthd_query_source(struct nvkm_object *object, void *data, u32 size)
230 {
231         union {
232                 struct nvif_perfmon_query_source_v0 v0;
233         } *args = data;
234         struct nvkm_pm *ppm = (void *)object->engine;
235         struct nvkm_perfdom *dom = NULL;
236         struct nvkm_perfsig *sig;
237         struct nvkm_perfsrc *src;
238         u8 source_nr = 0;
239         int si, ret;
240
241         nv_ioctl(object, "perfmon query source size %d\n", size);
242         if (nvif_unpack(args->v0, 0, 0, false)) {
243                 nv_ioctl(object,
244                          "perfmon source vers %d dom %d sig %02x iter %02x\n",
245                          args->v0.version, args->v0.domain, args->v0.signal,
246                          args->v0.iter);
247                 si = (args->v0.iter & 0xff) - 1;
248         } else
249                 return ret;
250
251         sig = nvkm_perfsig_find(ppm, args->v0.domain, args->v0.signal, &dom);
252         if (!sig)
253                 return -EINVAL;
254
255         source_nr = nvkm_perfsig_count_perfsrc(sig);
256         if (si >= (int)source_nr)
257                 return -EINVAL;
258
259         if (si >= 0) {
260                 src = nvkm_perfsrc_find(ppm, sig, sig->source[si]);
261                 if (!src)
262                         return -EINVAL;
263
264                 args->v0.source = sig->source[si];
265                 args->v0.mask   = src->mask;
266                 strncpy(args->v0.name, src->name, sizeof(args->v0.name));
267         }
268
269         if (++si < source_nr) {
270                 args->v0.iter = ++si;
271                 return 0;
272         }
273
274         args->v0.iter = 0xff;
275         return 0;
276 }
277
278 static int
279 nvkm_perfmon_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
280 {
281         switch (mthd) {
282         case NVIF_PERFMON_V0_QUERY_DOMAIN:
283                 return nvkm_perfmon_mthd_query_domain(object, data, size);
284         case NVIF_PERFMON_V0_QUERY_SIGNAL:
285                 return nvkm_perfmon_mthd_query_signal(object, data, size);
286         case NVIF_PERFMON_V0_QUERY_SOURCE:
287                 return nvkm_perfmon_mthd_query_source(object, data, size);
288         default:
289                 break;
290         }
291         return -EINVAL;
292 }
293
294 static struct nvkm_ofuncs
295 nvkm_perfmon_ofuncs = {
296         .ctor = _nvkm_object_ctor,
297         .dtor = nvkm_object_destroy,
298         .init = nvkm_object_init,
299         .fini = nvkm_object_fini,
300         .mthd = nvkm_perfmon_mthd,
301 };
302
303 /*******************************************************************************
304  * Perfdom object classes
305  ******************************************************************************/
306 static int
307 nvkm_perfdom_init(struct nvkm_object *object, void *data, u32 size)
308 {
309         union {
310                 struct nvif_perfdom_init none;
311         } *args = data;
312         struct nvkm_pm *ppm = (void *)object->engine;
313         struct nvkm_perfdom *dom = (void *)object;
314         int ret, i;
315
316         nv_ioctl(object, "perfdom init size %d\n", size);
317         if (nvif_unvers(args->none)) {
318                 nv_ioctl(object, "perfdom init\n");
319         } else
320                 return ret;
321
322         for (i = 0; i < 4; i++)
323                 if (dom->ctr[i])
324                         dom->func->init(ppm, dom, dom->ctr[i]);
325
326         /* start next batch of counters for sampling */
327         dom->func->next(ppm, dom);
328         return 0;
329 }
330
331 static int
332 nvkm_perfdom_sample(struct nvkm_object *object, void *data, u32 size)
333 {
334         union {
335                 struct nvif_perfdom_sample none;
336         } *args = data;
337         struct nvkm_pm *ppm = (void *)object->engine;
338         struct nvkm_perfdom *dom;
339         int ret;
340
341         nv_ioctl(object, "perfdom sample size %d\n", size);
342         if (nvif_unvers(args->none)) {
343                 nv_ioctl(object, "perfdom sample\n");
344         } else
345                 return ret;
346         ppm->sequence++;
347
348         /* sample previous batch of counters */
349         list_for_each_entry(dom, &ppm->domains, head)
350                 dom->func->next(ppm, dom);
351
352         return 0;
353 }
354
355 static int
356 nvkm_perfdom_read(struct nvkm_object *object, void *data, u32 size)
357 {
358         union {
359                 struct nvif_perfdom_read_v0 v0;
360         } *args = data;
361         struct nvkm_pm *ppm = (void *)object->engine;
362         struct nvkm_perfdom *dom = (void *)object;
363         int ret, i;
364
365         nv_ioctl(object, "perfdom read size %d\n", size);
366         if (nvif_unpack(args->v0, 0, 0, false)) {
367                 nv_ioctl(object, "perfdom read vers %d\n", args->v0.version);
368         } else
369                 return ret;
370
371         for (i = 0; i < 4; i++) {
372                 if (dom->ctr[i])
373                         dom->func->read(ppm, dom, dom->ctr[i]);
374         }
375
376         if (!dom->clk)
377                 return -EAGAIN;
378
379         for (i = 0; i < 4; i++)
380                 if (dom->ctr[i])
381                         args->v0.ctr[i] = dom->ctr[i]->ctr;
382         args->v0.clk = dom->clk;
383         return 0;
384 }
385
386 static int
387 nvkm_perfdom_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
388 {
389         switch (mthd) {
390         case NVIF_PERFDOM_V0_INIT:
391                 return nvkm_perfdom_init(object, data, size);
392         case NVIF_PERFDOM_V0_SAMPLE:
393                 return nvkm_perfdom_sample(object, data, size);
394         case NVIF_PERFDOM_V0_READ:
395                 return nvkm_perfdom_read(object, data, size);
396         default:
397                 break;
398         }
399         return -EINVAL;
400 }
401
402 static void
403 nvkm_perfdom_dtor(struct nvkm_object *object)
404 {
405         struct nvkm_perfdom *dom = (void *)object;
406         int i;
407
408         for (i = 0; i < 4; i++) {
409                 struct nvkm_perfctr *ctr = dom->ctr[i];
410                 if (ctr && ctr->head.next)
411                         list_del(&ctr->head);
412                 kfree(ctr);
413         }
414         nvkm_object_destroy(&dom->base);
415 }
416
417 static int
418 nvkm_perfctr_new(struct nvkm_perfdom *dom, int slot,
419                  struct nvkm_perfsig *signal[4], uint16_t logic_op,
420                  struct nvkm_perfctr **pctr)
421 {
422         struct nvkm_perfctr *ctr;
423         int i;
424
425         if (!dom)
426                 return -EINVAL;
427
428         ctr = *pctr = kzalloc(sizeof(*ctr), GFP_KERNEL);
429         if (!ctr)
430                 return -ENOMEM;
431
432         ctr->logic_op = logic_op;
433         ctr->slot     = slot;
434         for (i = 0; i < 4; i++) {
435                 if (signal[i])
436                         ctr->signal[i] = signal[i] - dom->signal;
437         }
438         list_add_tail(&ctr->head, &dom->list);
439
440         return 0;
441 }
442
443 static int
444 nvkm_perfdom_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
445                   struct nvkm_oclass *oclass, void *data, u32 size,
446                   struct nvkm_object **pobject)
447 {
448         union {
449                 struct nvif_perfdom_v0 v0;
450         } *args = data;
451         struct nvkm_pm *ppm = (void *)engine;
452         struct nvkm_perfdom *sdom = NULL;
453         struct nvkm_perfctr *ctr[4] = {};
454         struct nvkm_perfdom *dom;
455         int c, s;
456         int ret;
457
458         nv_ioctl(parent, "create perfdom size %d\n", size);
459         if (nvif_unpack(args->v0, 0, 0, false)) {
460                 nv_ioctl(parent, "create perfdom vers %d dom %d mode %02x\n",
461                          args->v0.version, args->v0.domain, args->v0.mode);
462         } else
463                 return ret;
464
465         for (c = 0; c < ARRAY_SIZE(args->v0.ctr); c++) {
466                 struct nvkm_perfsig *sig[4] = {};
467                 for (s = 0; s < ARRAY_SIZE(args->v0.ctr[c].signal); s++) {
468                         sig[s] = nvkm_perfsig_find(ppm, args->v0.domain,
469                                                    args->v0.ctr[c].signal[s],
470                                                    &sdom);
471                         if (args->v0.ctr[c].signal[s] && !sig[s])
472                                 return -EINVAL;
473                 }
474
475                 ret = nvkm_perfctr_new(sdom, c, sig,
476                                        args->v0.ctr[c].logic_op, &ctr[c]);
477                 if (ret)
478                         return ret;
479         }
480
481         if (!sdom)
482                 return -EINVAL;
483
484         ret = nvkm_object_create(parent, engine, oclass, 0, &dom);
485         *pobject = nv_object(dom);
486         if (ret)
487                 return ret;
488
489         dom->func = sdom->func;
490         dom->addr = sdom->addr;
491         dom->mode = args->v0.mode;
492         for (c = 0; c < ARRAY_SIZE(ctr); c++)
493                 dom->ctr[c] = ctr[c];
494         return 0;
495 }
496
497 static struct nvkm_ofuncs
498 nvkm_perfdom_ofuncs = {
499         .ctor = nvkm_perfdom_ctor,
500         .dtor = nvkm_perfdom_dtor,
501         .init = nvkm_object_init,
502         .fini = nvkm_object_fini,
503         .mthd = nvkm_perfdom_mthd,
504 };
505
506 struct nvkm_oclass
507 nvkm_pm_sclass[] = {
508         {
509           .handle = NVIF_IOCTL_NEW_V0_PERFMON,
510           .ofuncs = &nvkm_perfmon_ofuncs,
511         },
512         { .handle = NVIF_IOCTL_NEW_V0_PERFDOM,
513           .ofuncs = &nvkm_perfdom_ofuncs,
514         },
515         {},
516 };
517
518 /*******************************************************************************
519  * PPM context
520  ******************************************************************************/
521 static void
522 nvkm_perfctx_dtor(struct nvkm_object *object)
523 {
524         struct nvkm_pm *ppm = (void *)object->engine;
525         struct nvkm_perfctx *ctx = (void *)object;
526
527         mutex_lock(&nv_subdev(ppm)->mutex);
528         nvkm_engctx_destroy(&ctx->base);
529         if (ppm->context == ctx)
530                 ppm->context = NULL;
531         mutex_unlock(&nv_subdev(ppm)->mutex);
532 }
533
534 static int
535 nvkm_perfctx_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
536                   struct nvkm_oclass *oclass, void *data, u32 size,
537                   struct nvkm_object **pobject)
538 {
539         struct nvkm_pm *ppm = (void *)engine;
540         struct nvkm_perfctx *ctx;
541         int ret;
542
543         ret = nvkm_engctx_create(parent, engine, oclass, NULL, 0, 0, 0, &ctx);
544         *pobject = nv_object(ctx);
545         if (ret)
546                 return ret;
547
548         mutex_lock(&nv_subdev(ppm)->mutex);
549         if (ppm->context == NULL)
550                 ppm->context = ctx;
551         if (ctx != ppm->context)
552                 ret = -EBUSY;
553         mutex_unlock(&nv_subdev(ppm)->mutex);
554
555         return ret;
556 }
557
558 struct nvkm_oclass
559 nvkm_pm_cclass = {
560         .handle = NV_ENGCTX(PM, 0x00),
561         .ofuncs = &(struct nvkm_ofuncs) {
562                 .ctor = nvkm_perfctx_ctor,
563                 .dtor = nvkm_perfctx_dtor,
564                 .init = _nvkm_engctx_init,
565                 .fini = _nvkm_engctx_fini,
566         },
567 };
568
569 /*******************************************************************************
570  * PPM engine/subdev functions
571  ******************************************************************************/
572 int
573 nvkm_perfsrc_new(struct nvkm_pm *ppm, struct nvkm_perfsig *sig,
574                  const struct nvkm_specsrc *spec)
575 {
576         const struct nvkm_specsrc *ssrc;
577         const struct nvkm_specmux *smux;
578         struct nvkm_perfsrc *src;
579         u8 source_nr = 0;
580
581         if (!spec) {
582                 /* No sources are defined for this signal. */
583                 return 0;
584         }
585
586         ssrc = spec;
587         while (ssrc->name) {
588                 smux = ssrc->mux;
589                 while (smux->name) {
590                         bool found = false;
591                         u8 source_id = 0;
592                         u32 len;
593
594                         list_for_each_entry(src, &ppm->sources, head) {
595                                 if (src->addr == ssrc->addr &&
596                                     src->shift == smux->shift) {
597                                         found = true;
598                                         break;
599                                 }
600                                 source_id++;
601                         }
602
603                         if (!found) {
604                                 src = kzalloc(sizeof(*src), GFP_KERNEL);
605                                 if (!src)
606                                         return -ENOMEM;
607
608                                 src->addr   = ssrc->addr;
609                                 src->mask   = smux->mask;
610                                 src->shift  = smux->shift;
611                                 src->enable = smux->enable;
612
613                                 len = strlen(ssrc->name) +
614                                       strlen(smux->name) + 2;
615                                 src->name = kzalloc(len, GFP_KERNEL);
616                                 if (!src->name)
617                                         return -ENOMEM;
618                                 snprintf(src->name, len, "%s_%s", ssrc->name,
619                                          smux->name);
620
621                                 list_add_tail(&src->head, &ppm->sources);
622                         }
623
624                         sig->source[source_nr++] = source_id + 1;
625                         smux++;
626                 }
627                 ssrc++;
628         }
629
630         return 0;
631 }
632
633 int
634 nvkm_perfdom_new(struct nvkm_pm *ppm, const char *name, u32 mask,
635                  u32 base, u32 size_unit, u32 size_domain,
636                  const struct nvkm_specdom *spec)
637 {
638         const struct nvkm_specdom *sdom;
639         const struct nvkm_specsig *ssig;
640         struct nvkm_perfdom *dom;
641         int ret, i;
642
643         for (i = 0; i == 0 || mask; i++) {
644                 u32 addr = base + (i * size_unit);
645                 if (i && !(mask & (1 << i)))
646                         continue;
647
648                 sdom = spec;
649                 while (sdom->signal_nr) {
650                         dom = kzalloc(sizeof(*dom) + sdom->signal_nr *
651                                       sizeof(*dom->signal), GFP_KERNEL);
652                         if (!dom)
653                                 return -ENOMEM;
654
655                         if (mask) {
656                                 snprintf(dom->name, sizeof(dom->name),
657                                          "%s/%02x/%02x", name, i,
658                                          (int)(sdom - spec));
659                         } else {
660                                 snprintf(dom->name, sizeof(dom->name),
661                                          "%s/%02x", name, (int)(sdom - spec));
662                         }
663
664                         list_add_tail(&dom->head, &ppm->domains);
665                         INIT_LIST_HEAD(&dom->list);
666                         dom->func = sdom->func;
667                         dom->addr = addr;
668                         dom->signal_nr = sdom->signal_nr;
669
670                         ssig = (sdom++)->signal;
671                         while (ssig->name) {
672                                 struct nvkm_perfsig *sig =
673                                         &dom->signal[ssig->signal];
674                                 sig->name = ssig->name;
675                                 ret = nvkm_perfsrc_new(ppm, sig, ssig->source);
676                                 if (ret)
677                                         return ret;
678                                 ssig++;
679                         }
680
681                         addr += size_domain;
682                 }
683
684                 mask &= ~(1 << i);
685         }
686
687         return 0;
688 }
689
690 int
691 _nvkm_pm_fini(struct nvkm_object *object, bool suspend)
692 {
693         struct nvkm_pm *ppm = (void *)object;
694         return nvkm_engine_fini(&ppm->base, suspend);
695 }
696
697 int
698 _nvkm_pm_init(struct nvkm_object *object)
699 {
700         struct nvkm_pm *ppm = (void *)object;
701         return nvkm_engine_init(&ppm->base);
702 }
703
704 void
705 _nvkm_pm_dtor(struct nvkm_object *object)
706 {
707         struct nvkm_pm *ppm = (void *)object;
708         struct nvkm_perfdom *dom, *next_dom;
709         struct nvkm_perfsrc *src, *next_src;
710
711         list_for_each_entry_safe(dom, next_dom, &ppm->domains, head) {
712                 list_del(&dom->head);
713                 kfree(dom);
714         }
715
716         list_for_each_entry_safe(src, next_src, &ppm->sources, head) {
717                 list_del(&src->head);
718                 kfree(src->name);
719                 kfree(src);
720         }
721
722         nvkm_engine_destroy(&ppm->base);
723 }
724
725 int
726 nvkm_pm_create_(struct nvkm_object *parent, struct nvkm_object *engine,
727                 struct nvkm_oclass *oclass, int length, void **pobject)
728 {
729         struct nvkm_pm *ppm;
730         int ret;
731
732         ret = nvkm_engine_create_(parent, engine, oclass, true, "PPM",
733                                   "pm", length, pobject);
734         ppm = *pobject;
735         if (ret)
736                 return ret;
737
738         INIT_LIST_HEAD(&ppm->domains);
739         INIT_LIST_HEAD(&ppm->sources);
740         return 0;
741 }