9
9
#include <linux/pm_domain.h>
10
10
#include <linux/pm_opp.h>
11
11
#include <soc/qcom/cmd-db.h>
12
+ #include <soc/qcom/tcs.h>
12
13
#include <drm/drm_gem.h>
13
14
14
15
#include "a6xx_gpu.h"
@@ -1294,6 +1295,104 @@ static int a6xx_gmu_memory_probe(struct a6xx_gmu *gmu)
1294
1295
return 0 ;
1295
1296
}
1296
1297
1298
+ /**
1299
+ * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager (BCM)
1300
+ * @unit: divisor used to convert bytes/sec bw value to an RPMh msg
1301
+ * @width: multiplier used to convert bytes/sec bw value to an RPMh msg
1302
+ * @vcd: virtual clock domain that this bcm belongs to
1303
+ * @reserved: reserved field
1304
+ */
1305
+ struct bcm_db {
1306
+ __le32 unit ;
1307
+ __le16 width ;
1308
+ u8 vcd ;
1309
+ u8 reserved ;
1310
+ };
1311
+
1312
+ static int a6xx_gmu_rpmh_bw_votes_init (struct adreno_gpu * adreno_gpu ,
1313
+ const struct a6xx_info * info ,
1314
+ struct a6xx_gmu * gmu )
1315
+ {
1316
+ const struct bcm_db * bcm_data [GMU_MAX_BCMS ] = { 0 };
1317
+ unsigned int bcm_index , bw_index , bcm_count = 0 ;
1318
+
1319
+ /* Retrieve BCM data from cmd-db */
1320
+ for (bcm_index = 0 ; bcm_index < GMU_MAX_BCMS ; bcm_index ++ ) {
1321
+ const struct a6xx_bcm * bcm = & info -> bcms [bcm_index ];
1322
+ size_t count ;
1323
+
1324
+ /* Stop at NULL terminated bcm entry */
1325
+ if (!bcm -> name )
1326
+ break ;
1327
+
1328
+ bcm_data [bcm_index ] = cmd_db_read_aux_data (bcm -> name , & count );
1329
+ if (IS_ERR (bcm_data [bcm_index ]))
1330
+ return PTR_ERR (bcm_data [bcm_index ]);
1331
+
1332
+ if (!count ) {
1333
+ dev_err (gmu -> dev , "invalid BCM '%s' aux data size\n" ,
1334
+ bcm -> name );
1335
+ return - EINVAL ;
1336
+ }
1337
+
1338
+ bcm_count ++ ;
1339
+ }
1340
+
1341
+ /* Generate BCM votes values for each bandwidth & BCM */
1342
+ for (bw_index = 0 ; bw_index < gmu -> nr_gpu_bws ; bw_index ++ ) {
1343
+ u32 * data = gmu -> gpu_ib_votes [bw_index ];
1344
+ u32 bw = gmu -> gpu_bw_table [bw_index ];
1345
+
1346
+ /* Calculations loosely copied from bcm_aggregate() & tcs_cmd_gen() */
1347
+ for (bcm_index = 0 ; bcm_index < bcm_count ; bcm_index ++ ) {
1348
+ const struct a6xx_bcm * bcm = & info -> bcms [bcm_index ];
1349
+ bool commit = false;
1350
+ u64 peak ;
1351
+ u32 vote ;
1352
+
1353
+ if (bcm_index == bcm_count - 1 ||
1354
+ (bcm_data [bcm_index + 1 ] &&
1355
+ bcm_data [bcm_index ]-> vcd != bcm_data [bcm_index + 1 ]-> vcd ))
1356
+ commit = true;
1357
+
1358
+ if (!bw ) {
1359
+ data [bcm_index ] = BCM_TCS_CMD (commit , false, 0 , 0 );
1360
+ continue ;
1361
+ }
1362
+
1363
+ if (bcm -> fixed ) {
1364
+ u32 perfmode = 0 ;
1365
+
1366
+ /* GMU on A6xx votes perfmode on all valid bandwidth */
1367
+ if (!adreno_is_a7xx (adreno_gpu ) ||
1368
+ (bcm -> perfmode_bw && bw >= bcm -> perfmode_bw ))
1369
+ perfmode = bcm -> perfmode ;
1370
+
1371
+ data [bcm_index ] = BCM_TCS_CMD (commit , true, 0 , perfmode );
1372
+ continue ;
1373
+ }
1374
+
1375
+ /* Multiply the bandwidth by the width of the connection */
1376
+ peak = (u64 )bw * le16_to_cpu (bcm_data [bcm_index ]-> width );
1377
+ do_div (peak , bcm -> buswidth );
1378
+
1379
+ /* Input bandwidth value is in KBps, scale the value to BCM unit */
1380
+ peak *= 1000 ;
1381
+ do_div (peak , le32_to_cpu (bcm_data [bcm_index ]-> unit ));
1382
+
1383
+ vote = clamp (peak , 1 , BCM_TCS_CMD_VOTE_MASK );
1384
+
1385
+ /* GMUs on A7xx votes on both x & y */
1386
+ if (adreno_is_a7xx (adreno_gpu ))
1387
+ data [bcm_index ] = BCM_TCS_CMD (commit , true, vote , vote );
1388
+ else
1389
+ data [bcm_index ] = BCM_TCS_CMD (commit , true, 0 , vote );
1390
+ }
1391
+ }
1392
+
1393
+ return 0 ;
1394
+ }
1395
+
1297
1396
/* Return the 'arc-level' for the given frequency */
1298
1397
static unsigned int a6xx_gmu_get_arc_level (struct device * dev ,
1299
1398
unsigned long freq )
@@ -1397,12 +1496,15 @@ static int a6xx_gmu_rpmh_arc_votes_init(struct device *dev, u32 *votes,
1397
1496
* The GMU votes with the RPMh for itself and on behalf of the GPU but we need
1398
1497
* to construct the list of votes on the CPU and send it over. Query the RPMh
1399
1498
* voltage levels and build the votes
1499
+ * The GMU can also vote for DDR interconnects, use the OPP bandwidth entries
1500
+ * and BCM parameters to build the votes.
1400
1501
*/
1401
1502
1402
1503
static int a6xx_gmu_rpmh_votes_init (struct a6xx_gmu * gmu )
1403
1504
{
1404
1505
struct a6xx_gpu * a6xx_gpu = container_of (gmu , struct a6xx_gpu , gmu );
1405
1506
struct adreno_gpu * adreno_gpu = & a6xx_gpu -> base ;
1507
+ const struct a6xx_info * info = adreno_gpu -> info -> a6xx ;
1406
1508
struct msm_gpu * gpu = & adreno_gpu -> base ;
1407
1509
int ret ;
1408
1510
@@ -1414,6 +1516,10 @@ static int a6xx_gmu_rpmh_votes_init(struct a6xx_gmu *gmu)
1414
1516
ret |= a6xx_gmu_rpmh_arc_votes_init (gmu -> dev , gmu -> cx_arc_votes ,
1415
1517
gmu -> gmu_freqs , gmu -> nr_gmu_freqs , "cx.lvl" );
1416
1518
1519
+ /* Build the interconnect votes */
1520
+ if (info -> bcms && gmu -> nr_gpu_bws > 1 )
1521
+ ret |= a6xx_gmu_rpmh_bw_votes_init (adreno_gpu , info , gmu );
1522
+
1417
1523
return ret ;
1418
1524
}
1419
1525
@@ -1449,10 +1555,43 @@ static int a6xx_gmu_build_freq_table(struct device *dev, unsigned long *freqs,
1449
1555
return index ;
1450
1556
}
1451
1557
1558
+ static int a6xx_gmu_build_bw_table (struct device * dev , unsigned long * bandwidths ,
1559
+ u32 size )
1560
+ {
1561
+ int count = dev_pm_opp_get_opp_count (dev );
1562
+ struct dev_pm_opp * opp ;
1563
+ int i , index = 0 ;
1564
+ unsigned int bandwidth = 1 ;
1565
+
1566
+ /*
1567
+ * The OPP table doesn't contain the "off" bandwidth level so we need to
1568
+ * add 1 to the table size to account for it
1569
+ */
1570
+
1571
+ if (WARN (count + 1 > size ,
1572
+ "The GMU bandwidth table is being truncated\n" ))
1573
+ count = size - 1 ;
1574
+
1575
+ /* Set the "off" bandwidth */
1576
+ bandwidths [index ++ ] = 0 ;
1577
+
1578
+ for (i = 0 ; i < count ; i ++ ) {
1579
+ opp = dev_pm_opp_find_bw_ceil (dev , & bandwidth , 0 );
1580
+ if (IS_ERR (opp ))
1581
+ break ;
1582
+
1583
+ dev_pm_opp_put (opp );
1584
+ bandwidths [index ++ ] = bandwidth ++ ;
1585
+ }
1586
+
1587
+ return index ;
1588
+ }
1589
+
1452
1590
static int a6xx_gmu_pwrlevels_probe (struct a6xx_gmu * gmu )
1453
1591
{
1454
1592
struct a6xx_gpu * a6xx_gpu = container_of (gmu , struct a6xx_gpu , gmu );
1455
1593
struct adreno_gpu * adreno_gpu = & a6xx_gpu -> base ;
1594
+ const struct a6xx_info * info = adreno_gpu -> info -> a6xx ;
1456
1595
struct msm_gpu * gpu = & adreno_gpu -> base ;
1457
1596
1458
1597
int ret = 0 ;
@@ -1479,6 +1618,14 @@ static int a6xx_gmu_pwrlevels_probe(struct a6xx_gmu *gmu)
1479
1618
1480
1619
gmu -> current_perf_index = gmu -> nr_gpu_freqs - 1 ;
1481
1620
1621
+ /*
1622
+ * The GMU also handles GPU Interconnect Votes so build a list
1623
+ * of DDR bandwidths from the GPU OPP table
1624
+ */
1625
+ if (info -> bcms )
1626
+ gmu -> nr_gpu_bws = a6xx_gmu_build_bw_table (& gpu -> pdev -> dev ,
1627
+ gmu -> gpu_bw_table , ARRAY_SIZE (gmu -> gpu_bw_table ));
1628
+
1482
1629
/* Build the list of RPMh votes that we'll send to the GMU */
1483
1630
return a6xx_gmu_rpmh_votes_init (gmu );
1484
1631
}
0 commit comments