#include <linux/err.h>
#include <linux/export.h>
+#include <drm/drm_edid.h>
#include <drm/drm_sysfs.h>
#include <drm/drm_core.h>
#include <drm/drmP.h>
.name = "drm_minor"
};
+struct class *drm_class;
+
+static const char * const audioformatstr[] = {
+ "",
+ "LPCM", /*AUDIO_LPCM = 1,*/
+ "AC3", /*AUDIO_AC3,*/
+ "MPEG1", /*AUDIO_MPEG1,*/
+ "MP3", /*AUDIO_MP3,*/
+ "MPEG2", /*AUDIO_MPEG2,*/
+ "AAC-LC", /*AUDIO_AAC_LC, AAC*/
+ "DTS", /*AUDIO_DTS,*/
+ "ATARC", /*AUDIO_ATARC,*/
+ "DSD", /*AUDIO_DSD, One bit Audio */
+ "E-AC3", /*AUDIO_E_AC3,*/
+ "DTS-HD", /*AUDIO_DTS_HD,*/
+ "MLP", /*AUDIO_MLP,*/
+ "DST", /*AUDIO_DST,*/
+ "WMA-PRO", /*AUDIO_WMA_PRO*/
+};
+
/**
* __drm_class_suspend - internal DRM class suspend routine
* @dev: Linux device to suspend
CORE_DATE);
/**
- * drm_sysfs_create - create a struct drm_sysfs_class structure
- * @owner: pointer to the module that is to "own" this struct drm_sysfs_class
- * @name: pointer to a string for the name of this class.
+ * drm_sysfs_init - initialize sysfs helpers
+ *
+ * This is used to create the DRM class, which is the implicit parent of any
+ * other top-level DRM sysfs objects.
*
- * This is used to create DRM class pointer that can then be used
- * in calls to drm_sysfs_device_add().
+ * You must call drm_sysfs_destroy() to release the allocated resources.
*
- * Note, the pointer created here is to be destroyed when finished by making a
- * call to drm_sysfs_destroy().
+ * Return: 0 on success, negative error code on failure.
*/
-struct class *drm_sysfs_create(struct module *owner, char *name)
+int drm_sysfs_init(void)
{
- struct class *class;
int err;
- class = class_create(owner, name);
- if (IS_ERR(class)) {
- err = PTR_ERR(class);
- goto err_out;
- }
-
- class->pm = &drm_class_dev_pm_ops;
-
- err = class_create_file(class, &class_attr_version.attr);
- if (err)
- goto err_out_class;
+ drm_class = class_create(THIS_MODULE, "drm");
+ if (IS_ERR(drm_class))
+ return PTR_ERR(drm_class);
- class->devnode = drm_devnode;
+ drm_class->pm = &drm_class_dev_pm_ops;
- return class;
+ err = class_create_file(drm_class, &class_attr_version.attr);
+ if (err) {
+ class_destroy(drm_class);
+ drm_class = NULL;
+ return err;
+ }
-err_out_class:
- class_destroy(class);
-err_out:
- return ERR_PTR(err);
+ drm_class->devnode = drm_devnode;
+ return 0;
}
/**
*/
void drm_sysfs_destroy(void)
{
- if ((drm_class == NULL) || (IS_ERR(drm_class)))
+ if (IS_ERR_OR_NULL(drm_class))
return;
class_remove_file(drm_class, &class_attr_version.attr);
class_destroy(drm_class);
"disabled");
}
+static int drm_get_audio_format(struct edid *edid,
+ char *audioformat, int len)
+{
+ int i, size = 0, num = 0;
+ struct cea_sad *sads = NULL;
+
+ memset(audioformat, 0, len);
+ num = drm_edid_to_sad(edid, &sads);
+ if (num <= 0)
+ return 0;
+
+ for (i = 0; i < num; i++) {
+ if (sads[i].format < 1 || sads[i].format > 14) {
+ DRM_ERROR("audio type unsupported.\n");
+ continue;
+ }
+ size = strlen(audioformatstr[sads[i].format]);
+ memcpy(audioformat, audioformatstr[sads[i].format], size);
+ audioformat[size] = ',';
+ audioformat += (size + 1);
+ }
+ kfree(sads);
+
+ return num;
+}
+
+static ssize_t audioformat_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ char audioformat[200];
+ int ret = 0;
+ struct edid *edid;
+ struct drm_connector *connector = to_drm_connector(device);
+
+ if (!connector->edid_blob_ptr)
+ return 0;
+
+ edid = (struct edid *)connector->edid_blob_ptr->data;
+ ret = drm_get_audio_format(edid, audioformat, 200);
+ if (ret)
+ return snprintf(buf, PAGE_SIZE, "%s\n", audioformat);
+
+ return 0;
+}
+
static ssize_t edid_show(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off,
size_t count)
int written = 0;
list_for_each_entry(mode, &connector->modes, head) {
- written += snprintf(buf + written, PAGE_SIZE - written, "%s\n",
- mode->name);
+ bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+
+ written += snprintf(buf + written, PAGE_SIZE - written,
+ "%dx%d%s%d\n",
+ mode->hdisplay, mode->vdisplay,
+ interlaced ? "i" : "p",
+ drm_mode_vrefresh(mode));
}
return written;
}
+static ssize_t mode_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ struct drm_display_mode *mode;
+ struct drm_crtc_state *crtc_state;
+ bool interlaced;
+ int written = 0;
+
+ if (!connector->state || !connector->state->crtc)
+ return written;
+
+ crtc_state = connector->state->crtc->state;
+ if (!crtc_state)
+ return written;
+
+ mode = &crtc_state->mode;
+
+ interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+ written += snprintf(buf + written, PAGE_SIZE - written,
+ "%dx%d%s%d\n",
+ mode->hdisplay, mode->vdisplay,
+ interlaced ? "i" : "p",
+ drm_mode_vrefresh(mode));
+
+ return written;
+}
+
static ssize_t tv_subconnector_show(struct device *device,
struct device_attribute *attr,
char *buf)
static DEVICE_ATTR_RO(enabled);
static DEVICE_ATTR_RO(dpms);
static DEVICE_ATTR_RO(modes);
+static DEVICE_ATTR_RO(mode);
+static DEVICE_ATTR_RO(audioformat);
static struct attribute *connector_dev_attrs[] = {
&dev_attr_status.attr,
&dev_attr_enabled.attr,
&dev_attr_dpms.attr,
&dev_attr_modes.attr,
+ &dev_attr_mode.attr,
+ &dev_attr_audioformat.attr,
NULL
};