305 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// Copyright(c) 2015-2020 Intel Corporation.3 4#include <linux/device.h>5#include <linux/mod_devicetable.h>6#include <linux/slab.h>7#include <linux/sysfs.h>8#include <linux/soundwire/sdw.h>9#include <linux/soundwire/sdw_type.h>10#include "bus.h"11#include "sysfs_local.h"12 13struct dpn_attribute {14 struct device_attribute dev_attr;15 int N;16 int dir;17 const char *format_string;18};19 20/*21 * Since we can't use ARRAY_SIZE, hard-code number of dpN attributes.22 * This needs to be updated when adding new attributes - an error will be23 * flagged on a mismatch.24 */25#define SDW_DPN_ATTRIBUTES 1526 27#define sdw_dpn_attribute_alloc(field) \28static int field##_attribute_alloc(struct device *dev, \29 struct attribute **res, \30 int N, int dir, \31 const char *format_string) \32{ \33 struct dpn_attribute *dpn_attr; \34 \35 dpn_attr = devm_kzalloc(dev, sizeof(*dpn_attr), GFP_KERNEL); \36 if (!dpn_attr) \37 return -ENOMEM; \38 dpn_attr->N = N; \39 dpn_attr->dir = dir; \40 sysfs_attr_init(&dpn_attr->dev_attr.attr); \41 dpn_attr->format_string = format_string; \42 dpn_attr->dev_attr.attr.name = __stringify(field); \43 dpn_attr->dev_attr.attr.mode = 0444; \44 dpn_attr->dev_attr.show = field##_show; \45 \46 *res = &dpn_attr->dev_attr.attr; \47 \48 return 0; \49}50 51#define sdw_dpn_attr(field) \52 \53static ssize_t field##_dpn_show(struct sdw_slave *slave, \54 int N, \55 int dir, \56 const char *format_string, \57 char *buf) \58{ \59 struct sdw_dpn_prop *dpn; \60 unsigned long mask; \61 int bit; \62 int i; \63 \64 if (dir) { \65 dpn = slave->prop.src_dpn_prop; \66 mask = slave->prop.source_ports; \67 } else { \68 dpn = slave->prop.sink_dpn_prop; \69 mask = slave->prop.sink_ports; \70 } \71 \72 i = 0; \73 for_each_set_bit(bit, &mask, 32) { \74 if (bit == N) { \75 return sprintf(buf, format_string, \76 dpn[i].field); \77 } \78 i++; \79 } \80 return -EINVAL; \81} \82 \83static ssize_t field##_show(struct device *dev, \84 struct device_attribute *attr, \85 char *buf) \86{ \87 struct sdw_slave *slave = dev_to_sdw_dev(dev); \88 struct dpn_attribute *dpn_attr = \89 container_of(attr, struct dpn_attribute, dev_attr); \90 \91 return field##_dpn_show(slave, \92 dpn_attr->N, dpn_attr->dir, \93 dpn_attr->format_string, \94 buf); \95} \96sdw_dpn_attribute_alloc(field)97 98sdw_dpn_attr(imp_def_interrupts);99sdw_dpn_attr(max_word);100sdw_dpn_attr(min_word);101sdw_dpn_attr(type);102sdw_dpn_attr(max_grouping);103sdw_dpn_attr(simple_ch_prep_sm);104sdw_dpn_attr(ch_prep_timeout);105sdw_dpn_attr(max_ch);106sdw_dpn_attr(min_ch);107sdw_dpn_attr(max_async_buffer);108sdw_dpn_attr(block_pack_mode);109sdw_dpn_attr(port_encoding);110 111#define sdw_dpn_array_attr(field) \112 \113static ssize_t field##_dpn_show(struct sdw_slave *slave, \114 int N, \115 int dir, \116 const char *format_string, \117 char *buf) \118{ \119 struct sdw_dpn_prop *dpn; \120 unsigned long mask; \121 ssize_t size = 0; \122 int bit; \123 int i; \124 int j; \125 \126 if (dir) { \127 dpn = slave->prop.src_dpn_prop; \128 mask = slave->prop.source_ports; \129 } else { \130 dpn = slave->prop.sink_dpn_prop; \131 mask = slave->prop.sink_ports; \132 } \133 \134 i = 0; \135 for_each_set_bit(bit, &mask, 32) { \136 if (bit == N) { \137 for (j = 0; j < dpn[i].num_##field; j++) \138 size += sprintf(buf + size, \139 format_string, \140 dpn[i].field[j]); \141 size += sprintf(buf + size, "\n"); \142 return size; \143 } \144 i++; \145 } \146 return -EINVAL; \147} \148static ssize_t field##_show(struct device *dev, \149 struct device_attribute *attr, \150 char *buf) \151{ \152 struct sdw_slave *slave = dev_to_sdw_dev(dev); \153 struct dpn_attribute *dpn_attr = \154 container_of(attr, struct dpn_attribute, dev_attr); \155 \156 return field##_dpn_show(slave, \157 dpn_attr->N, dpn_attr->dir, \158 dpn_attr->format_string, \159 buf); \160} \161sdw_dpn_attribute_alloc(field)162 163sdw_dpn_array_attr(words);164sdw_dpn_array_attr(ch_combinations);165sdw_dpn_array_attr(channels);166 167static int add_all_attributes(struct device *dev, int N, int dir)168{169 struct attribute **dpn_attrs;170 struct attribute_group *dpn_group;171 int i = 0;172 int ret;173 174 /* allocate attributes, last one is NULL */175 dpn_attrs = devm_kcalloc(dev, SDW_DPN_ATTRIBUTES + 1,176 sizeof(struct attribute *),177 GFP_KERNEL);178 if (!dpn_attrs)179 return -ENOMEM;180 181 ret = max_word_attribute_alloc(dev, &dpn_attrs[i++],182 N, dir, "%d\n");183 if (ret < 0)184 return ret;185 186 ret = min_word_attribute_alloc(dev, &dpn_attrs[i++],187 N, dir, "%d\n");188 if (ret < 0)189 return ret;190 191 ret = words_attribute_alloc(dev, &dpn_attrs[i++],192 N, dir, "%d\n");193 if (ret < 0)194 return ret;195 196 ret = type_attribute_alloc(dev, &dpn_attrs[i++],197 N, dir, "%d\n");198 if (ret < 0)199 return ret;200 201 ret = max_grouping_attribute_alloc(dev, &dpn_attrs[i++],202 N, dir, "%d\n");203 if (ret < 0)204 return ret;205 206 ret = simple_ch_prep_sm_attribute_alloc(dev, &dpn_attrs[i++],207 N, dir, "%d\n");208 if (ret < 0)209 return ret;210 211 ret = ch_prep_timeout_attribute_alloc(dev, &dpn_attrs[i++],212 N, dir, "%d\n");213 if (ret < 0)214 return ret;215 216 ret = imp_def_interrupts_attribute_alloc(dev, &dpn_attrs[i++],217 N, dir, "0x%x\n");218 if (ret < 0)219 return ret;220 221 ret = min_ch_attribute_alloc(dev, &dpn_attrs[i++],222 N, dir, "%d\n");223 if (ret < 0)224 return ret;225 226 ret = max_ch_attribute_alloc(dev, &dpn_attrs[i++],227 N, dir, "%d\n");228 if (ret < 0)229 return ret;230 231 ret = channels_attribute_alloc(dev, &dpn_attrs[i++],232 N, dir, "%d\n");233 if (ret < 0)234 return ret;235 236 ret = ch_combinations_attribute_alloc(dev, &dpn_attrs[i++],237 N, dir, "%d\n");238 if (ret < 0)239 return ret;240 241 ret = max_async_buffer_attribute_alloc(dev, &dpn_attrs[i++],242 N, dir, "%d\n");243 if (ret < 0)244 return ret;245 246 ret = block_pack_mode_attribute_alloc(dev, &dpn_attrs[i++],247 N, dir, "%d\n");248 if (ret < 0)249 return ret;250 251 ret = port_encoding_attribute_alloc(dev, &dpn_attrs[i++],252 N, dir, "%d\n");253 if (ret < 0)254 return ret;255 256 /* paranoia check for editing mistakes */257 if (i != SDW_DPN_ATTRIBUTES) {258 dev_err(dev, "mismatch in attributes, allocated %d got %d\n",259 SDW_DPN_ATTRIBUTES, i);260 return -EINVAL;261 }262 263 dpn_group = devm_kzalloc(dev, sizeof(*dpn_group), GFP_KERNEL);264 if (!dpn_group)265 return -ENOMEM;266 267 dpn_group->attrs = dpn_attrs;268 dpn_group->name = devm_kasprintf(dev, GFP_KERNEL, "dp%d_%s",269 N, dir ? "src" : "sink");270 if (!dpn_group->name)271 return -ENOMEM;272 273 ret = devm_device_add_group(dev, dpn_group);274 if (ret < 0)275 return ret;276 277 return 0;278}279 280int sdw_slave_sysfs_dpn_init(struct sdw_slave *slave)281{282 unsigned long mask;283 int ret;284 int i;285 286 if (!slave->prop.source_ports && !slave->prop.sink_ports)287 return 0;288 289 mask = slave->prop.source_ports;290 for_each_set_bit(i, &mask, 32) {291 ret = add_all_attributes(&slave->dev, i, 1);292 if (ret < 0)293 return ret;294 }295 296 mask = slave->prop.sink_ports;297 for_each_set_bit(i, &mask, 32) {298 ret = add_all_attributes(&slave->dev, i, 0);299 if (ret < 0)300 return ret;301 }302 303 return 0;304}305