diff options
author | Arun Kannan <akannan@nvidia.com> | 2017-05-12 20:03:36 -0400 |
---|---|---|
committer | mobile promotions <svcmobile_promotions@nvidia.com> | 2017-05-23 22:29:38 -0400 |
commit | 5043bb4772042eb78e9ab744d5a37fd4c0d6319c (patch) | |
tree | d46dd4a2ddcf068d82378379893db4290fa00651 /drivers/video/tegra/dc/dp.c | |
parent | f4835514cadb999aa1ba6f6a97847d8356e343ae (diff) |
video: tegra: dp: Add debugfs to r/w DPCD reg
The debugfs directory is located here:
/sys/kernel/debug/tegra_dp[n]/dpaux_dpcd
The nodes available are: addr, num_bytes and data.
User can read/write 'data' of size 'num_bytes' beginning
at DPCD register offset specified in 'addr'.
The format of data to be read/written is bytes separated by spaces.
Each byte should be represented using 2 hexadecimal digits.
Example read usage:
cd /sys/kernel/debug/tegra_dp[n]/dpaux_dpcd
echo 0x300 > addr
echo 4 > num_bytes
cat data
00 04 4b 00
Example write usage:
cd /sys/kernel/debug/tegra_dp[n]/dpaux_dpcd
echo 0x300 > addr
echo 4 > num_bytes
echo 00 04 4e 00 > data
cat data
00 04 4e 00
Bug 1900877
Change-Id: I4186a133645f54cec3c866b731dae0a796aba2c2
Signed-off-by: Arun Kannan <akannan@nvidia.com>
Reviewed-on: http://git-master/r/1481159
(cherry picked from commit 0fb6becd6b3100d727b63e8ef4544dbeaf7ecfac)
Reviewed-on: http://git-master/r/1486072
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Diffstat (limited to 'drivers/video/tegra/dc/dp.c')
-rw-r--r-- | drivers/video/tegra/dc/dp.c | 196 |
1 files changed, 170 insertions, 26 deletions
diff --git a/drivers/video/tegra/dc/dp.c b/drivers/video/tegra/dc/dp.c index 34309a440..c90c41f93 100644 --- a/drivers/video/tegra/dc/dp.c +++ b/drivers/video/tegra/dc/dp.c | |||
@@ -1097,55 +1097,92 @@ static const struct file_operations bits_per_pixel_fops = { | |||
1097 | .release = single_release, | 1097 | .release = single_release, |
1098 | }; | 1098 | }; |
1099 | 1099 | ||
1100 | static inline void dpaux_print_data(struct seq_file *s, u8 *data, u32 size) | ||
1101 | { | ||
1102 | u8 row_size = 16; | ||
1103 | u32 i, j; | ||
1104 | |||
1105 | for (i = 0; i < size; i += row_size) { | ||
1106 | for (j = i; j < i + row_size && j < size; j++) | ||
1107 | seq_printf(s, "%02x ", data[j]); | ||
1108 | seq_puts(s, "\n"); | ||
1109 | } | ||
1110 | } | ||
1111 | |||
1100 | static int dpaux_i2c_data_show(struct seq_file *s, void *unused) | 1112 | static int dpaux_i2c_data_show(struct seq_file *s, void *unused) |
1101 | { | 1113 | { |
1102 | struct tegra_dc_dp_data *dp = s->private; | 1114 | struct tegra_dc_dp_data *dp = s->private; |
1103 | u32 addr = dp->dpaux_i2c_dbg_addr; | 1115 | u32 addr = dp->dpaux_i2c_dbg_addr; |
1104 | u32 size = dp->dpaux_i2c_dbg_num_bytes; | 1116 | u32 size = dp->dpaux_i2c_dbg_num_bytes; |
1105 | u32 i, j, aux_stat; | 1117 | u32 aux_stat; |
1106 | u8 row_size = 16; | ||
1107 | u8 *data; | 1118 | u8 *data; |
1119 | int ret; | ||
1108 | 1120 | ||
1109 | data = kzalloc(size, GFP_KERNEL); | 1121 | data = kzalloc(size, GFP_KERNEL); |
1110 | if (!data) | 1122 | if (!data) |
1111 | return -ENOMEM; | 1123 | return -ENOMEM; |
1112 | 1124 | ||
1113 | tegra_dc_dp_i2c_read(dp, addr, data, &size, &aux_stat); | 1125 | ret = tegra_dc_dp_i2c_read(dp, addr, data, &size, &aux_stat); |
1114 | for (i = 0; i < size; i += row_size) { | 1126 | if (ret) { |
1115 | for (j = i; j < i + row_size && j < size; j++) | 1127 | seq_printf(s, "Error reading %d bytes from I2C reg %x", |
1116 | seq_printf(s, "%02x ", data[j]); | 1128 | dp->dpaux_i2c_dbg_num_bytes, |
1117 | seq_puts(s, "\n"); | 1129 | dp->dpaux_i2c_dbg_addr); |
1130 | goto free_mem; | ||
1118 | } | 1131 | } |
1119 | 1132 | ||
1133 | dpaux_print_data(s, data, size); | ||
1134 | |||
1135 | free_mem: | ||
1120 | kfree(data); | 1136 | kfree(data); |
1121 | return 0; | 1137 | return ret; |
1122 | } | 1138 | } |
1123 | 1139 | ||
1124 | static ssize_t dpaux_i2c_data_set(struct file *file, | 1140 | static int dpaux_dpcd_data_show(struct seq_file *s, void *unused) |
1125 | const char __user *user_buf, size_t count, loff_t *off) | ||
1126 | { | 1141 | { |
1127 | struct seq_file *s = file->private_data; | ||
1128 | struct tegra_dc_dp_data *dp = s->private; | 1142 | struct tegra_dc_dp_data *dp = s->private; |
1129 | u32 i = 0, size = 0; | 1143 | u32 addr = dp->dpaux_dpcd_dbg_addr; |
1130 | u32 aux_stat; | 1144 | u32 size = dp->dpaux_dpcd_dbg_num_bytes; |
1131 | u32 addr = dp->dpaux_i2c_dbg_addr; | 1145 | u32 i; |
1132 | u8 *data; | 1146 | u8 *data; |
1147 | int ret; | ||
1148 | |||
1149 | data = kzalloc(size, GFP_KERNEL); | ||
1150 | if (!data) | ||
1151 | return -ENOMEM; | ||
1152 | |||
1153 | for (i = 0; i < size; i++) { | ||
1154 | ret = tegra_dc_dp_dpcd_read(dp, addr+i, data+i); | ||
1155 | if (ret) { | ||
1156 | seq_printf(s, "Reading %d bytes from reg %x; " | ||
1157 | "Error at DPCD reg offset %x\n", | ||
1158 | dp->dpaux_dpcd_dbg_num_bytes, | ||
1159 | dp->dpaux_dpcd_dbg_addr, | ||
1160 | addr+i); | ||
1161 | goto free_mem; | ||
1162 | } | ||
1163 | } | ||
1164 | |||
1165 | dpaux_print_data(s, data, size); | ||
1166 | |||
1167 | free_mem: | ||
1168 | kfree(data); | ||
1169 | return ret; | ||
1170 | } | ||
1171 | |||
1172 | static inline int dpaux_parse_input(const char __user *user_buf, | ||
1173 | u8 *data, size_t count) | ||
1174 | { | ||
1175 | int size = 0; | ||
1176 | u32 i = 0; | ||
1133 | char tmp[3]; | 1177 | char tmp[3]; |
1134 | char *buf; | 1178 | char *buf; |
1135 | int ret = count; | ||
1136 | 1179 | ||
1137 | buf = kzalloc(count, GFP_KERNEL); | 1180 | buf = kzalloc(count, GFP_KERNEL); |
1138 | if (!buf) | 1181 | if (!buf) |
1139 | return -ENOMEM; | 1182 | return -ENOMEM; |
1140 | 1183 | ||
1141 | data = kzalloc(count, GFP_KERNEL); | ||
1142 | if (!data) { | ||
1143 | ret = -ENOMEM; | ||
1144 | goto free_mem; | ||
1145 | } | ||
1146 | |||
1147 | if (copy_from_user(buf, user_buf, count)) { | 1184 | if (copy_from_user(buf, user_buf, count)) { |
1148 | ret = -EINVAL; | 1185 | size = -EINVAL; |
1149 | goto free_mem; | 1186 | goto free_mem; |
1150 | } | 1187 | } |
1151 | 1188 | ||
@@ -1155,14 +1192,14 @@ static ssize_t dpaux_i2c_data_set(struct file *file, | |||
1155 | * amount of whitespace between each XX. | 1192 | * amount of whitespace between each XX. |
1156 | */ | 1193 | */ |
1157 | while (i + 1 < count) { | 1194 | while (i + 1 < count) { |
1158 | if (isspace(buf[i])) { | 1195 | if (buf[i] == ' ') { |
1159 | i += 1; | 1196 | i += 1; |
1160 | continue; | 1197 | continue; |
1161 | } | 1198 | } |
1162 | 1199 | ||
1163 | tmp[0] = buf[i]; tmp[1] = buf[i + 1]; tmp[2] = '\0'; | 1200 | tmp[0] = buf[i]; tmp[1] = buf[i + 1]; tmp[2] = '\0'; |
1164 | if (kstrtou8(tmp, 16, data + size)) { | 1201 | if (kstrtou8(tmp, 16, data + size)) { |
1165 | ret = -EINVAL; | 1202 | size = -EINVAL; |
1166 | goto free_mem; | 1203 | goto free_mem; |
1167 | } | 1204 | } |
1168 | 1205 | ||
@@ -1170,10 +1207,73 @@ static ssize_t dpaux_i2c_data_set(struct file *file, | |||
1170 | i += 2; | 1207 | i += 2; |
1171 | } | 1208 | } |
1172 | 1209 | ||
1173 | tegra_dc_dp_i2c_write(dp, addr, data, &size, &aux_stat); | ||
1174 | |||
1175 | free_mem: | 1210 | free_mem: |
1176 | kfree(buf); | 1211 | kfree(buf); |
1212 | return size; | ||
1213 | } | ||
1214 | |||
1215 | static ssize_t dpaux_i2c_data_set(struct file *file, | ||
1216 | const char __user *user_buf, size_t count, loff_t *off) | ||
1217 | { | ||
1218 | struct seq_file *s = file->private_data; | ||
1219 | struct tegra_dc_dp_data *dp = s->private; | ||
1220 | int size = 0; | ||
1221 | u32 aux_stat; | ||
1222 | u32 addr = dp->dpaux_i2c_dbg_addr; | ||
1223 | u8 *data; | ||
1224 | int ret = count; | ||
1225 | |||
1226 | data = kzalloc(count, GFP_KERNEL); | ||
1227 | if (!data) | ||
1228 | return -ENOMEM; | ||
1229 | |||
1230 | size = dpaux_parse_input(user_buf, data, count); | ||
1231 | if (size <= 0) { | ||
1232 | ret = -EINVAL; | ||
1233 | goto free_mem; | ||
1234 | } | ||
1235 | |||
1236 | ret = tegra_dc_dp_i2c_write(dp, addr, data, &size, &aux_stat); | ||
1237 | if (!ret) | ||
1238 | ret = count; | ||
1239 | else | ||
1240 | ret = -EIO; | ||
1241 | |||
1242 | free_mem: | ||
1243 | kfree(data); | ||
1244 | |||
1245 | return ret; | ||
1246 | } | ||
1247 | |||
1248 | static ssize_t dpaux_dpcd_data_set(struct file *file, | ||
1249 | const char __user *user_buf, size_t count, loff_t *off) | ||
1250 | { | ||
1251 | struct seq_file *s = file->private_data; | ||
1252 | struct tegra_dc_dp_data *dp = s->private; | ||
1253 | int size = 0; | ||
1254 | u32 aux_stat; | ||
1255 | u32 addr = dp->dpaux_dpcd_dbg_addr; | ||
1256 | u8 *data; | ||
1257 | int ret = count; | ||
1258 | |||
1259 | data = kzalloc(count, GFP_KERNEL); | ||
1260 | if (!data) | ||
1261 | return -ENOMEM; | ||
1262 | |||
1263 | size = dpaux_parse_input(user_buf, data, count); | ||
1264 | if (size <= 0) { | ||
1265 | ret = -EINVAL; | ||
1266 | goto free_mem; | ||
1267 | } | ||
1268 | |||
1269 | ret = tegra_dc_dpaux_write(dp, DPAUX_DP_AUXCTL_CMD_AUXWR, | ||
1270 | addr, data, &size, &aux_stat); | ||
1271 | if (!ret) | ||
1272 | ret = count; | ||
1273 | else | ||
1274 | ret = -EIO; | ||
1275 | |||
1276 | free_mem: | ||
1177 | kfree(data); | 1277 | kfree(data); |
1178 | 1278 | ||
1179 | return ret; | 1279 | return ret; |
@@ -1184,6 +1284,11 @@ static int dpaux_i2c_data_open(struct inode *inode, struct file *file) | |||
1184 | return single_open(file, dpaux_i2c_data_show, inode->i_private); | 1284 | return single_open(file, dpaux_i2c_data_show, inode->i_private); |
1185 | } | 1285 | } |
1186 | 1286 | ||
1287 | static int dpaux_dpcd_data_open(struct inode *inode, struct file *file) | ||
1288 | { | ||
1289 | return single_open(file, dpaux_dpcd_data_show, inode->i_private); | ||
1290 | } | ||
1291 | |||
1187 | static const struct file_operations dpaux_i2c_data_fops = { | 1292 | static const struct file_operations dpaux_i2c_data_fops = { |
1188 | .open = dpaux_i2c_data_open, | 1293 | .open = dpaux_i2c_data_open, |
1189 | .read = seq_read, | 1294 | .read = seq_read, |
@@ -1192,6 +1297,14 @@ static const struct file_operations dpaux_i2c_data_fops = { | |||
1192 | .release = single_release, | 1297 | .release = single_release, |
1193 | }; | 1298 | }; |
1194 | 1299 | ||
1300 | static const struct file_operations dpaux_dpcd_data_fops = { | ||
1301 | .open = dpaux_dpcd_data_open, | ||
1302 | .read = seq_read, | ||
1303 | .write = dpaux_dpcd_data_set, | ||
1304 | .llseek = seq_lseek, | ||
1305 | .release = single_release, | ||
1306 | }; | ||
1307 | |||
1195 | static struct dentry *tegra_dpaux_i2c_dir_create(struct tegra_dc_dp_data *dp, | 1308 | static struct dentry *tegra_dpaux_i2c_dir_create(struct tegra_dc_dp_data *dp, |
1196 | struct dentry *parent) | 1309 | struct dentry *parent) |
1197 | { | 1310 | { |
@@ -1220,6 +1333,34 @@ free_out: | |||
1220 | return retval; | 1333 | return retval; |
1221 | } | 1334 | } |
1222 | 1335 | ||
1336 | static struct dentry *tegra_dpaux_dpcd_dir_create(struct tegra_dc_dp_data *dp, | ||
1337 | struct dentry *parent) | ||
1338 | { | ||
1339 | struct dentry *dpaux_dir; | ||
1340 | struct dentry *retval = NULL; | ||
1341 | |||
1342 | dpaux_dir = debugfs_create_dir("dpaux_dpcd", parent); | ||
1343 | if (!dpaux_dir) | ||
1344 | return retval; | ||
1345 | retval = debugfs_create_u16("addr", S_IRUGO | S_IWUGO, dpaux_dir, | ||
1346 | &dp->dpaux_dpcd_dbg_addr); | ||
1347 | if (!retval) | ||
1348 | goto free_out; | ||
1349 | retval = debugfs_create_u32("num_bytes", S_IRUGO | S_IWUGO, | ||
1350 | dpaux_dir, &dp->dpaux_dpcd_dbg_num_bytes); | ||
1351 | if (!retval) | ||
1352 | goto free_out; | ||
1353 | retval = debugfs_create_file("data", S_IRUGO, dpaux_dir, dp, | ||
1354 | &dpaux_dpcd_data_fops); | ||
1355 | if (!retval) | ||
1356 | goto free_out; | ||
1357 | |||
1358 | return retval; | ||
1359 | free_out: | ||
1360 | debugfs_remove_recursive(dpaux_dir); | ||
1361 | return retval; | ||
1362 | } | ||
1363 | |||
1223 | static void tegra_dc_dp_debugfs_create(struct tegra_dc_dp_data *dp) | 1364 | static void tegra_dc_dp_debugfs_create(struct tegra_dc_dp_data *dp) |
1224 | { | 1365 | { |
1225 | struct dentry *retval; | 1366 | struct dentry *retval; |
@@ -1257,6 +1398,9 @@ static void tegra_dc_dp_debugfs_create(struct tegra_dc_dp_data *dp) | |||
1257 | retval = tegra_dpaux_i2c_dir_create(dp, dp->debugdir); | 1398 | retval = tegra_dpaux_i2c_dir_create(dp, dp->debugdir); |
1258 | if (!retval) | 1399 | if (!retval) |
1259 | goto free_out; | 1400 | goto free_out; |
1401 | retval = tegra_dpaux_dpcd_dir_create(dp, dp->debugdir); | ||
1402 | if (!retval) | ||
1403 | goto free_out; | ||
1260 | 1404 | ||
1261 | /* hotplug not allowed for eDP */ | 1405 | /* hotplug not allowed for eDP */ |
1262 | if (is_hotplug_supported(dp)) { | 1406 | if (is_hotplug_supported(dp)) { |