1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr> |
4 | */ |
5 | |
6 | #include <linux/list.h> |
7 | #include <linux/slab.h> |
8 | #include <sound/ac97/codec.h> |
9 | #include <sound/ac97/compat.h> |
10 | #include <sound/ac97/controller.h> |
11 | #include <sound/soc.h> |
12 | |
13 | #include "ac97_core.h" |
14 | |
15 | static void compat_ac97_release(struct device *dev) |
16 | { |
17 | kfree(to_ac97_t(dev)); |
18 | } |
19 | |
20 | static void compat_ac97_reset(struct snd_ac97 *ac97) |
21 | { |
22 | struct ac97_codec_device *adev = to_ac97_device(d: ac97->private_data); |
23 | struct ac97_controller *actrl = adev->ac97_ctrl; |
24 | |
25 | if (actrl->ops->reset) |
26 | actrl->ops->reset(actrl); |
27 | } |
28 | |
29 | static void compat_ac97_warm_reset(struct snd_ac97 *ac97) |
30 | { |
31 | struct ac97_codec_device *adev = to_ac97_device(d: ac97->private_data); |
32 | struct ac97_controller *actrl = adev->ac97_ctrl; |
33 | |
34 | if (actrl->ops->warm_reset) |
35 | actrl->ops->warm_reset(actrl); |
36 | } |
37 | |
38 | static void compat_ac97_write(struct snd_ac97 *ac97, unsigned short reg, |
39 | unsigned short val) |
40 | { |
41 | struct ac97_codec_device *adev = to_ac97_device(d: ac97->private_data); |
42 | struct ac97_controller *actrl = adev->ac97_ctrl; |
43 | |
44 | actrl->ops->write(actrl, ac97->num, reg, val); |
45 | } |
46 | |
47 | static unsigned short compat_ac97_read(struct snd_ac97 *ac97, |
48 | unsigned short reg) |
49 | { |
50 | struct ac97_codec_device *adev = to_ac97_device(d: ac97->private_data); |
51 | struct ac97_controller *actrl = adev->ac97_ctrl; |
52 | |
53 | return actrl->ops->read(actrl, ac97->num, reg); |
54 | } |
55 | |
56 | static const struct snd_ac97_bus_ops compat_snd_ac97_bus_ops = { |
57 | .reset = compat_ac97_reset, |
58 | .warm_reset = compat_ac97_warm_reset, |
59 | .write = compat_ac97_write, |
60 | .read = compat_ac97_read, |
61 | }; |
62 | |
63 | static struct snd_ac97_bus compat_soc_ac97_bus = { |
64 | .ops = &compat_snd_ac97_bus_ops, |
65 | }; |
66 | |
67 | struct snd_ac97 *snd_ac97_compat_alloc(struct ac97_codec_device *adev) |
68 | { |
69 | struct snd_ac97 *ac97; |
70 | int ret; |
71 | |
72 | ac97 = kzalloc(size: sizeof(struct snd_ac97), GFP_KERNEL); |
73 | if (ac97 == NULL) |
74 | return ERR_PTR(error: -ENOMEM); |
75 | |
76 | ac97->private_data = adev; |
77 | ac97->bus = &compat_soc_ac97_bus; |
78 | |
79 | ac97->dev.parent = &adev->dev; |
80 | ac97->dev.release = compat_ac97_release; |
81 | dev_set_name(dev: &ac97->dev, name: "%s-compat" , dev_name(dev: &adev->dev)); |
82 | ret = device_register(dev: &ac97->dev); |
83 | if (ret) { |
84 | put_device(dev: &ac97->dev); |
85 | return ERR_PTR(error: ret); |
86 | } |
87 | |
88 | return ac97; |
89 | } |
90 | EXPORT_SYMBOL_GPL(snd_ac97_compat_alloc); |
91 | |
92 | void snd_ac97_compat_release(struct snd_ac97 *ac97) |
93 | { |
94 | device_unregister(dev: &ac97->dev); |
95 | } |
96 | EXPORT_SYMBOL_GPL(snd_ac97_compat_release); |
97 | |
98 | int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id, |
99 | unsigned int id_mask) |
100 | { |
101 | struct ac97_codec_device *adev = to_ac97_device(d: ac97->private_data); |
102 | struct ac97_controller *actrl = adev->ac97_ctrl; |
103 | unsigned int scanned; |
104 | |
105 | if (try_warm) { |
106 | compat_ac97_warm_reset(ac97); |
107 | scanned = snd_ac97_bus_scan_one(adrv: actrl, codec_num: adev->num); |
108 | if (ac97_ids_match(id1: scanned, id2: adev->vendor_id, mask: id_mask)) |
109 | return 1; |
110 | } |
111 | |
112 | compat_ac97_reset(ac97); |
113 | compat_ac97_warm_reset(ac97); |
114 | scanned = snd_ac97_bus_scan_one(adrv: actrl, codec_num: adev->num); |
115 | if (ac97_ids_match(id1: scanned, id2: adev->vendor_id, mask: id_mask)) |
116 | return 0; |
117 | |
118 | return -ENODEV; |
119 | } |
120 | EXPORT_SYMBOL_GPL(snd_ac97_reset); |
121 | |