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