blob: 9b05c9e25ebe48a7d135ff45ad4b3af0fb1cb2aa [file] [log] [blame]
Pierre-Louis Bossart7ceaa402020-05-19 01:43:21 +08001// SPDX-License-Identifier: GPL-2.0-only
2// Copyright(c) 2019-2020 Intel Corporation.
3
4#include <linux/device.h>
5#include <linux/acpi.h>
Bard Liao26d970222020-05-19 01:43:22 +08006#include <linux/pm_runtime.h>
Pierre-Louis Bossart7ceaa402020-05-19 01:43:21 +08007#include <linux/soundwire/sdw.h>
8#include <linux/soundwire/sdw_type.h>
9#include "bus.h"
10
Pierre-Louis Bossartc5778ca2020-05-19 04:35:50 +080011/*
Pierre-Louis Bossarte04e60f2020-11-24 21:07:42 +080012 * The 3s value for autosuspend will only be used if there are no
13 * devices physically attached on a bus segment. In practice enabling
14 * the bus operation will result in children devices become active and
15 * the master device will only suspend when all its children are no
16 * longer active.
17 */
18#define SDW_MASTER_SUSPEND_DELAY_MS 3000
19
20/*
Pierre-Louis Bossartc5778ca2020-05-19 04:35:50 +080021 * The sysfs for properties reflects the MIPI description as given
22 * in the MIPI DisCo spec
23 *
24 * Base file is:
25 * sdw-master-N
26 * |---- revision
27 * |---- clk_stop_modes
28 * |---- max_clk_freq
29 * |---- clk_freq
30 * |---- clk_gears
31 * |---- default_row
32 * |---- default_col
33 * |---- dynamic_shape
34 * |---- err_threshold
35 */
36
37#define sdw_master_attr(field, format_string) \
38static ssize_t field##_show(struct device *dev, \
39 struct device_attribute *attr, \
40 char *buf) \
41{ \
42 struct sdw_master_device *md = dev_to_sdw_master_device(dev); \
43 return sprintf(buf, format_string, md->bus->prop.field); \
44} \
45static DEVICE_ATTR_RO(field)
46
47sdw_master_attr(revision, "0x%x\n");
48sdw_master_attr(clk_stop_modes, "0x%x\n");
49sdw_master_attr(max_clk_freq, "%d\n");
50sdw_master_attr(default_row, "%d\n");
51sdw_master_attr(default_col, "%d\n");
52sdw_master_attr(default_frame_rate, "%d\n");
53sdw_master_attr(dynamic_frame, "%d\n");
54sdw_master_attr(err_threshold, "%d\n");
55
56static ssize_t clock_frequencies_show(struct device *dev,
57 struct device_attribute *attr, char *buf)
58{
59 struct sdw_master_device *md = dev_to_sdw_master_device(dev);
60 ssize_t size = 0;
61 int i;
62
63 for (i = 0; i < md->bus->prop.num_clk_freq; i++)
64 size += sprintf(buf + size, "%8d ",
65 md->bus->prop.clk_freq[i]);
66 size += sprintf(buf + size, "\n");
67
68 return size;
69}
70static DEVICE_ATTR_RO(clock_frequencies);
71
72static ssize_t clock_gears_show(struct device *dev,
73 struct device_attribute *attr, char *buf)
74{
75 struct sdw_master_device *md = dev_to_sdw_master_device(dev);
76 ssize_t size = 0;
77 int i;
78
79 for (i = 0; i < md->bus->prop.num_clk_gears; i++)
80 size += sprintf(buf + size, "%8d ",
81 md->bus->prop.clk_gears[i]);
82 size += sprintf(buf + size, "\n");
83
84 return size;
85}
86static DEVICE_ATTR_RO(clock_gears);
87
88static struct attribute *master_node_attrs[] = {
89 &dev_attr_revision.attr,
90 &dev_attr_clk_stop_modes.attr,
91 &dev_attr_max_clk_freq.attr,
92 &dev_attr_default_row.attr,
93 &dev_attr_default_col.attr,
94 &dev_attr_default_frame_rate.attr,
95 &dev_attr_dynamic_frame.attr,
96 &dev_attr_err_threshold.attr,
97 &dev_attr_clock_frequencies.attr,
98 &dev_attr_clock_gears.attr,
99 NULL,
100};
101ATTRIBUTE_GROUPS(master_node);
102
Pierre-Louis Bossart7ceaa402020-05-19 01:43:21 +0800103static void sdw_master_device_release(struct device *dev)
104{
105 struct sdw_master_device *md = dev_to_sdw_master_device(dev);
106
107 kfree(md);
108}
109
Bard Liao26d970222020-05-19 01:43:22 +0800110static const struct dev_pm_ops master_dev_pm = {
111 SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend,
112 pm_generic_runtime_resume, NULL)
113};
114
Pierre-Louis Bossart7ceaa402020-05-19 01:43:21 +0800115struct device_type sdw_master_type = {
116 .name = "soundwire_master",
117 .release = sdw_master_device_release,
Bard Liao26d970222020-05-19 01:43:22 +0800118 .pm = &master_dev_pm,
Pierre-Louis Bossart7ceaa402020-05-19 01:43:21 +0800119};
120
121/**
122 * sdw_master_device_add() - create a Linux Master Device representation.
123 * @bus: SDW bus instance
124 * @parent: parent device
125 * @fwnode: firmware node handle
126 */
127int sdw_master_device_add(struct sdw_bus *bus, struct device *parent,
128 struct fwnode_handle *fwnode)
129{
130 struct sdw_master_device *md;
131 int ret;
132
133 if (!parent)
134 return -EINVAL;
135
136 md = kzalloc(sizeof(*md), GFP_KERNEL);
137 if (!md)
138 return -ENOMEM;
139
140 md->dev.bus = &sdw_bus_type;
141 md->dev.type = &sdw_master_type;
142 md->dev.parent = parent;
Pierre-Louis Bossartc5778ca2020-05-19 04:35:50 +0800143 md->dev.groups = master_node_groups;
Pierre-Louis Bossart7ceaa402020-05-19 01:43:21 +0800144 md->dev.of_node = parent->of_node;
145 md->dev.fwnode = fwnode;
146 md->dev.dma_mask = parent->dma_mask;
147
148 dev_set_name(&md->dev, "sdw-master-%d", bus->id);
149
150 ret = device_register(&md->dev);
151 if (ret) {
152 dev_err(parent, "Failed to add master: ret %d\n", ret);
153 /*
154 * On err, don't free but drop ref as this will be freed
155 * when release method is invoked.
156 */
157 put_device(&md->dev);
158 goto device_register_err;
159 }
160
161 /* add shortcuts to improve code readability/compactness */
162 md->bus = bus;
163 bus->dev = &md->dev;
164 bus->md = md;
165
Pierre-Louis Bossarte04e60f2020-11-24 21:07:42 +0800166 pm_runtime_set_autosuspend_delay(&bus->md->dev, SDW_MASTER_SUSPEND_DELAY_MS);
167 pm_runtime_use_autosuspend(&bus->md->dev);
168 pm_runtime_mark_last_busy(&bus->md->dev);
169 pm_runtime_set_active(&bus->md->dev);
Bard Liaobd842562020-07-27 05:59:45 +0800170 pm_runtime_enable(&bus->md->dev);
Pierre-Louis Bossarte04e60f2020-11-24 21:07:42 +0800171 pm_runtime_idle(&bus->md->dev);
Pierre-Louis Bossart7ceaa402020-05-19 01:43:21 +0800172device_register_err:
173 return ret;
174}
175
176/**
177 * sdw_master_device_del() - delete a Linux Master Device representation.
178 * @bus: bus handle
179 *
180 * This function is the dual of sdw_master_device_add()
181 */
182int sdw_master_device_del(struct sdw_bus *bus)
183{
Bard Liaobd842562020-07-27 05:59:45 +0800184 pm_runtime_disable(&bus->md->dev);
Pierre-Louis Bossart7ceaa402020-05-19 01:43:21 +0800185 device_unregister(bus->dev);
186
187 return 0;
188}