diff --git a/apollolake-4.4.180/b44.ko b/apollolake-4.4.180/b44.ko index a0acadf0b..df0030c91 100644 Binary files a/apollolake-4.4.180/b44.ko and b/apollolake-4.4.180/b44.ko differ diff --git a/apollolake-4.4.180/be2net.ko b/apollolake-4.4.180/be2net.ko index b233e9375..b760db437 100644 Binary files a/apollolake-4.4.180/be2net.ko and b/apollolake-4.4.180/be2net.ko differ diff --git a/apollolake-4.4.180/bnx2.ko b/apollolake-4.4.180/bnx2.ko index 8a7f00516..eb33f9ed2 100644 Binary files a/apollolake-4.4.180/bnx2.ko and b/apollolake-4.4.180/bnx2.ko differ diff --git a/apollolake-4.4.180/bnx2x.ko b/apollolake-4.4.180/bnx2x.ko index c02c18136..6aa09b236 100644 Binary files a/apollolake-4.4.180/bnx2x.ko and b/apollolake-4.4.180/bnx2x.ko differ diff --git a/apollolake-4.4.180/bnxt_en.ko b/apollolake-4.4.180/bnxt_en.ko index 8310cb265..405d71a41 100644 Binary files a/apollolake-4.4.180/bnxt_en.ko and b/apollolake-4.4.180/bnxt_en.ko differ diff --git a/apollolake-4.4.180/cxgb.ko b/apollolake-4.4.180/cxgb.ko index bce49e4aa..11f22e3a1 100644 Binary files a/apollolake-4.4.180/cxgb.ko and b/apollolake-4.4.180/cxgb.ko differ diff --git a/apollolake-4.4.180/cxgb3.ko b/apollolake-4.4.180/cxgb3.ko index 7c0f9c44d..ed1b9edb7 100644 Binary files a/apollolake-4.4.180/cxgb3.ko and b/apollolake-4.4.180/cxgb3.ko differ diff --git a/apollolake-4.4.180/cxgb4.ko b/apollolake-4.4.180/cxgb4.ko index 6b8c2c2c6..a7c0e02ce 100644 Binary files a/apollolake-4.4.180/cxgb4.ko and b/apollolake-4.4.180/cxgb4.ko differ diff --git a/apollolake-4.4.180/cxgb4vf.ko b/apollolake-4.4.180/cxgb4vf.ko index 6bb2381d9..fe02746d6 100644 Binary files a/apollolake-4.4.180/cxgb4vf.ko and b/apollolake-4.4.180/cxgb4vf.ko differ diff --git a/apollolake-4.4.180/i40e.ko b/apollolake-4.4.180/i40e.ko index 83a03ed5b..0f64d5f97 100644 Binary files a/apollolake-4.4.180/i40e.ko and b/apollolake-4.4.180/i40e.ko differ diff --git a/apollolake-4.4.180/igbvf.ko b/apollolake-4.4.180/igbvf.ko index 52d70a9f1..21526c6eb 100644 Binary files a/apollolake-4.4.180/igbvf.ko and b/apollolake-4.4.180/igbvf.ko differ diff --git a/apollolake-4.4.180/ixgb.ko b/apollolake-4.4.180/ixgb.ko index dabec96b5..b957c732e 100644 Binary files a/apollolake-4.4.180/ixgb.ko and b/apollolake-4.4.180/ixgb.ko differ diff --git a/apollolake-4.4.180/ixgbe.ko b/apollolake-4.4.180/ixgbe.ko index 4ac263e38..040591f64 100644 Binary files a/apollolake-4.4.180/ixgbe.ko and b/apollolake-4.4.180/ixgbe.ko differ diff --git a/apollolake-4.4.180/ixgbevf.ko b/apollolake-4.4.180/ixgbevf.ko index 2b07ec507..a3ad2f819 100644 Binary files a/apollolake-4.4.180/ixgbevf.ko and b/apollolake-4.4.180/ixgbevf.ko differ diff --git a/apollolake-4.4.180/mlx4_core.ko b/apollolake-4.4.180/mlx4_core.ko index 63579d42f..3bc293c89 100644 Binary files a/apollolake-4.4.180/mlx4_core.ko and b/apollolake-4.4.180/mlx4_core.ko differ diff --git a/apollolake-4.4.180/mlx4_en.ko b/apollolake-4.4.180/mlx4_en.ko index 26f7cdb37..57bfa1ebe 100644 Binary files a/apollolake-4.4.180/mlx4_en.ko and b/apollolake-4.4.180/mlx4_en.ko differ diff --git a/apollolake-4.4.180/mlx5_core.ko b/apollolake-4.4.180/mlx5_core.ko index 1107b7373..023005e17 100644 Binary files a/apollolake-4.4.180/mlx5_core.ko and b/apollolake-4.4.180/mlx5_core.ko differ diff --git a/apollolake-4.4.180/mlxsw_core.ko b/apollolake-4.4.180/mlxsw_core.ko index 015a8ec9e..561531911 100644 Binary files a/apollolake-4.4.180/mlxsw_core.ko and b/apollolake-4.4.180/mlxsw_core.ko differ diff --git a/apollolake-4.4.180/mlxsw_pci.ko b/apollolake-4.4.180/mlxsw_pci.ko index 8fafef078..394599861 100644 Binary files a/apollolake-4.4.180/mlxsw_pci.ko and b/apollolake-4.4.180/mlxsw_pci.ko differ diff --git a/apollolake-4.4.180/mpt3sas.ko b/apollolake-4.4.180/mpt3sas.ko index 03e0e8a53..315c47261 100644 Binary files a/apollolake-4.4.180/mpt3sas.ko and b/apollolake-4.4.180/mpt3sas.ko differ diff --git a/apollolake-4.4.180/r8169.ko b/apollolake-4.4.180/r8169.ko index 210749ce4..a2843ee26 100644 Binary files a/apollolake-4.4.180/r8169.ko and b/apollolake-4.4.180/r8169.ko differ diff --git a/apollolake-4.4.180/tg3.ko b/apollolake-4.4.180/tg3.ko index e7bbe4164..d60ddc586 100644 Binary files a/apollolake-4.4.180/tg3.ko and b/apollolake-4.4.180/tg3.ko differ diff --git a/apollolake-4.4.180/virtio_pci.ko b/apollolake-4.4.180/virtio_pci.ko index 31b7c245d..f114e0333 100644 Binary files a/apollolake-4.4.180/virtio_pci.ko and b/apollolake-4.4.180/virtio_pci.ko differ diff --git a/broadwell-4.4.180/b44.ko b/broadwell-4.4.180/b44.ko index eb11edf1d..6d51eb70b 100644 Binary files a/broadwell-4.4.180/b44.ko and b/broadwell-4.4.180/b44.ko differ diff --git a/broadwell-4.4.180/be2net.ko b/broadwell-4.4.180/be2net.ko index 8b4afd119..3a38cf68e 100644 Binary files a/broadwell-4.4.180/be2net.ko and b/broadwell-4.4.180/be2net.ko differ diff --git a/broadwell-4.4.180/bnxt_en.ko b/broadwell-4.4.180/bnxt_en.ko index 91c0670e5..e993d1984 100644 Binary files a/broadwell-4.4.180/bnxt_en.ko and b/broadwell-4.4.180/bnxt_en.ko differ diff --git a/broadwell-4.4.180/cxgb.ko b/broadwell-4.4.180/cxgb.ko index da701bb59..8fcd1e0e9 100644 Binary files a/broadwell-4.4.180/cxgb.ko and b/broadwell-4.4.180/cxgb.ko differ diff --git a/broadwell-4.4.180/cxgb3.ko b/broadwell-4.4.180/cxgb3.ko index 118c718ee..ca1e2f9f1 100644 Binary files a/broadwell-4.4.180/cxgb3.ko and b/broadwell-4.4.180/cxgb3.ko differ diff --git a/broadwell-4.4.180/cxgb4.ko b/broadwell-4.4.180/cxgb4.ko index 84b010bd4..058389580 100644 Binary files a/broadwell-4.4.180/cxgb4.ko and b/broadwell-4.4.180/cxgb4.ko differ diff --git a/broadwell-4.4.180/cxgb4vf.ko b/broadwell-4.4.180/cxgb4vf.ko index e5922b50d..f334fea6f 100644 Binary files a/broadwell-4.4.180/cxgb4vf.ko and b/broadwell-4.4.180/cxgb4vf.ko differ diff --git a/broadwell-4.4.180/e1000e.ko b/broadwell-4.4.180/e1000e.ko index da4a74064..31bae07cf 100644 Binary files a/broadwell-4.4.180/e1000e.ko and b/broadwell-4.4.180/e1000e.ko differ diff --git a/broadwell-4.4.180/i40e.ko b/broadwell-4.4.180/i40e.ko index b1534b98e..cd45d833d 100644 Binary files a/broadwell-4.4.180/i40e.ko and b/broadwell-4.4.180/i40e.ko differ diff --git a/broadwell-4.4.180/igbvf.ko b/broadwell-4.4.180/igbvf.ko index 3b84c4f1f..6d0658306 100644 Binary files a/broadwell-4.4.180/igbvf.ko and b/broadwell-4.4.180/igbvf.ko differ diff --git a/broadwell-4.4.180/ixgb.ko b/broadwell-4.4.180/ixgb.ko index 449709c69..0d047f1a9 100644 Binary files a/broadwell-4.4.180/ixgb.ko and b/broadwell-4.4.180/ixgb.ko differ diff --git a/broadwell-4.4.180/ixgbe.ko b/broadwell-4.4.180/ixgbe.ko index 49c2ce857..2c6fdcf82 100644 Binary files a/broadwell-4.4.180/ixgbe.ko and b/broadwell-4.4.180/ixgbe.ko differ diff --git a/broadwell-4.4.180/ixgbevf.ko b/broadwell-4.4.180/ixgbevf.ko index 0fa7c15fc..6a12a56e1 100644 Binary files a/broadwell-4.4.180/ixgbevf.ko and b/broadwell-4.4.180/ixgbevf.ko differ diff --git a/broadwell-4.4.180/mlx4_core.ko b/broadwell-4.4.180/mlx4_core.ko index 9b253ae06..ccdc1442a 100644 Binary files a/broadwell-4.4.180/mlx4_core.ko and b/broadwell-4.4.180/mlx4_core.ko differ diff --git a/broadwell-4.4.180/mlx4_en.ko b/broadwell-4.4.180/mlx4_en.ko index b9b054e44..ac2f3869c 100644 Binary files a/broadwell-4.4.180/mlx4_en.ko and b/broadwell-4.4.180/mlx4_en.ko differ diff --git a/broadwell-4.4.180/mlx5_core.ko b/broadwell-4.4.180/mlx5_core.ko index 090fbdeac..90e0f2edb 100644 Binary files a/broadwell-4.4.180/mlx5_core.ko and b/broadwell-4.4.180/mlx5_core.ko differ diff --git a/broadwell-4.4.180/mlxsw_core.ko b/broadwell-4.4.180/mlxsw_core.ko index f66d60092..361bd8acb 100644 Binary files a/broadwell-4.4.180/mlxsw_core.ko and b/broadwell-4.4.180/mlxsw_core.ko differ diff --git a/broadwell-4.4.180/mlxsw_pci.ko b/broadwell-4.4.180/mlxsw_pci.ko index c1ff4b33d..773c488e2 100644 Binary files a/broadwell-4.4.180/mlxsw_pci.ko and b/broadwell-4.4.180/mlxsw_pci.ko differ diff --git a/broadwell-4.4.180/mpt3sas.ko b/broadwell-4.4.180/mpt3sas.ko index c1758cb59..fe523cb9e 100644 Binary files a/broadwell-4.4.180/mpt3sas.ko and b/broadwell-4.4.180/mpt3sas.ko differ diff --git a/broadwell-4.4.180/r8169.ko b/broadwell-4.4.180/r8169.ko index f8508defa..dfbf82022 100644 Binary files a/broadwell-4.4.180/r8169.ko and b/broadwell-4.4.180/r8169.ko differ diff --git a/broadwell-4.4.180/tg3.ko b/broadwell-4.4.180/tg3.ko index 85154c802..b9defe774 100644 Binary files a/broadwell-4.4.180/tg3.ko and b/broadwell-4.4.180/tg3.ko differ diff --git a/broadwell-4.4.180/virtio_pci.ko b/broadwell-4.4.180/virtio_pci.ko index b1982097e..e52a18829 100644 Binary files a/broadwell-4.4.180/virtio_pci.ko and b/broadwell-4.4.180/virtio_pci.ko differ diff --git a/broadwellnk-4.4.180/b44.ko b/broadwellnk-4.4.180/b44.ko index eb11edf1d..96ce7e73a 100644 Binary files a/broadwellnk-4.4.180/b44.ko and b/broadwellnk-4.4.180/b44.ko differ diff --git a/broadwellnk-4.4.180/be2net.ko b/broadwellnk-4.4.180/be2net.ko index 8b4afd119..3a38cf68e 100644 Binary files a/broadwellnk-4.4.180/be2net.ko and b/broadwellnk-4.4.180/be2net.ko differ diff --git a/broadwellnk-4.4.180/bnx2.ko b/broadwellnk-4.4.180/bnx2.ko index e69b80c71..47adfa30a 100644 Binary files a/broadwellnk-4.4.180/bnx2.ko and b/broadwellnk-4.4.180/bnx2.ko differ diff --git a/broadwellnk-4.4.180/bnxt_en.ko b/broadwellnk-4.4.180/bnxt_en.ko index 91c0670e5..dd0d2db68 100644 Binary files a/broadwellnk-4.4.180/bnxt_en.ko and b/broadwellnk-4.4.180/bnxt_en.ko differ diff --git a/broadwellnk-4.4.180/cxgb.ko b/broadwellnk-4.4.180/cxgb.ko index da701bb59..8a69f4cf7 100644 Binary files a/broadwellnk-4.4.180/cxgb.ko and b/broadwellnk-4.4.180/cxgb.ko differ diff --git a/broadwellnk-4.4.180/cxgb3.ko b/broadwellnk-4.4.180/cxgb3.ko index 118c718ee..26db51bb9 100644 Binary files a/broadwellnk-4.4.180/cxgb3.ko and b/broadwellnk-4.4.180/cxgb3.ko differ diff --git a/broadwellnk-4.4.180/cxgb4.ko b/broadwellnk-4.4.180/cxgb4.ko index 84b010bd4..1ca91b30a 100644 Binary files a/broadwellnk-4.4.180/cxgb4.ko and b/broadwellnk-4.4.180/cxgb4.ko differ diff --git a/broadwellnk-4.4.180/cxgb4vf.ko b/broadwellnk-4.4.180/cxgb4vf.ko index e5922b50d..b370b18a3 100644 Binary files a/broadwellnk-4.4.180/cxgb4vf.ko and b/broadwellnk-4.4.180/cxgb4vf.ko differ diff --git a/broadwellnk-4.4.180/e1000e.ko b/broadwellnk-4.4.180/e1000e.ko index da4a74064..df5ed4426 100644 Binary files a/broadwellnk-4.4.180/e1000e.ko and b/broadwellnk-4.4.180/e1000e.ko differ diff --git a/broadwellnk-4.4.180/i40e.ko b/broadwellnk-4.4.180/i40e.ko index b1534b98e..cd45d833d 100644 Binary files a/broadwellnk-4.4.180/i40e.ko and b/broadwellnk-4.4.180/i40e.ko differ diff --git a/broadwellnk-4.4.180/igb.ko b/broadwellnk-4.4.180/igb.ko index 2929932f3..2dc89335a 100644 Binary files a/broadwellnk-4.4.180/igb.ko and b/broadwellnk-4.4.180/igb.ko differ diff --git a/broadwellnk-4.4.180/igbvf.ko b/broadwellnk-4.4.180/igbvf.ko index 3b84c4f1f..5a0cab84f 100644 Binary files a/broadwellnk-4.4.180/igbvf.ko and b/broadwellnk-4.4.180/igbvf.ko differ diff --git a/broadwellnk-4.4.180/ixgb.ko b/broadwellnk-4.4.180/ixgb.ko index 449709c69..e65f743b8 100644 Binary files a/broadwellnk-4.4.180/ixgb.ko and b/broadwellnk-4.4.180/ixgb.ko differ diff --git a/broadwellnk-4.4.180/ixgbe.ko b/broadwellnk-4.4.180/ixgbe.ko index 49c2ce857..2c6fdcf82 100644 Binary files a/broadwellnk-4.4.180/ixgbe.ko and b/broadwellnk-4.4.180/ixgbe.ko differ diff --git a/broadwellnk-4.4.180/ixgbevf.ko b/broadwellnk-4.4.180/ixgbevf.ko index 0fa7c15fc..ac489807c 100644 Binary files a/broadwellnk-4.4.180/ixgbevf.ko and b/broadwellnk-4.4.180/ixgbevf.ko differ diff --git a/broadwellnk-4.4.180/mlx4_core.ko b/broadwellnk-4.4.180/mlx4_core.ko index 9b253ae06..ccdc1442a 100644 Binary files a/broadwellnk-4.4.180/mlx4_core.ko and b/broadwellnk-4.4.180/mlx4_core.ko differ diff --git a/broadwellnk-4.4.180/mlx4_en.ko b/broadwellnk-4.4.180/mlx4_en.ko index 5ab8eeef3..ac2f3869c 100644 Binary files a/broadwellnk-4.4.180/mlx4_en.ko and b/broadwellnk-4.4.180/mlx4_en.ko differ diff --git a/broadwellnk-4.4.180/mlx5_core.ko b/broadwellnk-4.4.180/mlx5_core.ko index 090fbdeac..90e0f2edb 100644 Binary files a/broadwellnk-4.4.180/mlx5_core.ko and b/broadwellnk-4.4.180/mlx5_core.ko differ diff --git a/broadwellnk-4.4.180/mlxsw_core.ko b/broadwellnk-4.4.180/mlxsw_core.ko index f66d60092..9aa4dcee1 100644 Binary files a/broadwellnk-4.4.180/mlxsw_core.ko and b/broadwellnk-4.4.180/mlxsw_core.ko differ diff --git a/broadwellnk-4.4.180/mlxsw_pci.ko b/broadwellnk-4.4.180/mlxsw_pci.ko index c1ff4b33d..431f2b2fd 100644 Binary files a/broadwellnk-4.4.180/mlxsw_pci.ko and b/broadwellnk-4.4.180/mlxsw_pci.ko differ diff --git a/broadwellnk-4.4.180/mpt3sas.ko b/broadwellnk-4.4.180/mpt3sas.ko index c1758cb59..0fb04308f 100644 Binary files a/broadwellnk-4.4.180/mpt3sas.ko and b/broadwellnk-4.4.180/mpt3sas.ko differ diff --git a/broadwellnk-4.4.180/r8152.ko b/broadwellnk-4.4.180/r8152.ko index 95f716c38..bad6db1b5 100644 Binary files a/broadwellnk-4.4.180/r8152.ko and b/broadwellnk-4.4.180/r8152.ko differ diff --git a/broadwellnk-4.4.180/r8169.ko b/broadwellnk-4.4.180/r8169.ko index f8508defa..0e2333be1 100644 Binary files a/broadwellnk-4.4.180/r8169.ko and b/broadwellnk-4.4.180/r8169.ko differ diff --git a/broadwellnk-4.4.180/tg3.ko b/broadwellnk-4.4.180/tg3.ko index 85154c802..333dd05ee 100644 Binary files a/broadwellnk-4.4.180/tg3.ko and b/broadwellnk-4.4.180/tg3.ko differ diff --git a/broadwellnk-4.4.180/virtio_pci.ko b/broadwellnk-4.4.180/virtio_pci.ko index f345398b1..b32b15961 100644 Binary files a/broadwellnk-4.4.180/virtio_pci.ko and b/broadwellnk-4.4.180/virtio_pci.ko differ diff --git a/bromolow-3.10.108/efifb.ko b/bromolow-3.10.108/efifb.ko new file mode 100644 index 000000000..ff8274d5a Binary files /dev/null and b/bromolow-3.10.108/efifb.ko differ diff --git a/denverton-4.4.180/b44.ko b/denverton-4.4.180/b44.ko index 03d8f0890..7ffef8bdf 100644 Binary files a/denverton-4.4.180/b44.ko and b/denverton-4.4.180/b44.ko differ diff --git a/denverton-4.4.180/bnx2.ko b/denverton-4.4.180/bnx2.ko index a2038a02d..ebcef2f1c 100644 Binary files a/denverton-4.4.180/bnx2.ko and b/denverton-4.4.180/bnx2.ko differ diff --git a/denverton-4.4.180/bnxt_en.ko b/denverton-4.4.180/bnxt_en.ko index 3ccfd1e84..258a8fa92 100644 Binary files a/denverton-4.4.180/bnxt_en.ko and b/denverton-4.4.180/bnxt_en.ko differ diff --git a/denverton-4.4.180/cxgb.ko b/denverton-4.4.180/cxgb.ko index 2e5cddcd0..53aca58c5 100644 Binary files a/denverton-4.4.180/cxgb.ko and b/denverton-4.4.180/cxgb.ko differ diff --git a/denverton-4.4.180/cxgb3.ko b/denverton-4.4.180/cxgb3.ko index 2768de902..0d3ef52c7 100644 Binary files a/denverton-4.4.180/cxgb3.ko and b/denverton-4.4.180/cxgb3.ko differ diff --git a/denverton-4.4.180/cxgb4.ko b/denverton-4.4.180/cxgb4.ko index e09225e0d..74cc28724 100644 Binary files a/denverton-4.4.180/cxgb4.ko and b/denverton-4.4.180/cxgb4.ko differ diff --git a/denverton-4.4.180/cxgb4vf.ko b/denverton-4.4.180/cxgb4vf.ko index 7328785b4..b7cd26230 100644 Binary files a/denverton-4.4.180/cxgb4vf.ko and b/denverton-4.4.180/cxgb4vf.ko differ diff --git a/denverton-4.4.180/e1000e.ko b/denverton-4.4.180/e1000e.ko index 44fdf3c3a..92b2d082c 100644 Binary files a/denverton-4.4.180/e1000e.ko and b/denverton-4.4.180/e1000e.ko differ diff --git a/denverton-4.4.180/i40e.ko b/denverton-4.4.180/i40e.ko index 1868bce86..f1275b5d8 100644 Binary files a/denverton-4.4.180/i40e.ko and b/denverton-4.4.180/i40e.ko differ diff --git a/denverton-4.4.180/igbvf.ko b/denverton-4.4.180/igbvf.ko index 731f1cb9c..39a8efcfa 100644 Binary files a/denverton-4.4.180/igbvf.ko and b/denverton-4.4.180/igbvf.ko differ diff --git a/denverton-4.4.180/ixgb.ko b/denverton-4.4.180/ixgb.ko index 7838c0e48..60f465ddd 100644 Binary files a/denverton-4.4.180/ixgb.ko and b/denverton-4.4.180/ixgb.ko differ diff --git a/denverton-4.4.180/ixgbe.ko b/denverton-4.4.180/ixgbe.ko index 773e8d949..0e12c1071 100644 Binary files a/denverton-4.4.180/ixgbe.ko and b/denverton-4.4.180/ixgbe.ko differ diff --git a/denverton-4.4.180/ixgbevf.ko b/denverton-4.4.180/ixgbevf.ko index 1e7487dc8..16623c357 100644 Binary files a/denverton-4.4.180/ixgbevf.ko and b/denverton-4.4.180/ixgbevf.ko differ diff --git a/denverton-4.4.180/mlx4_core.ko b/denverton-4.4.180/mlx4_core.ko index de0539512..c24409dec 100644 Binary files a/denverton-4.4.180/mlx4_core.ko and b/denverton-4.4.180/mlx4_core.ko differ diff --git a/denverton-4.4.180/mlx4_en.ko b/denverton-4.4.180/mlx4_en.ko index e2cf7f120..7325295f7 100644 Binary files a/denverton-4.4.180/mlx4_en.ko and b/denverton-4.4.180/mlx4_en.ko differ diff --git a/denverton-4.4.180/mlx5_core.ko b/denverton-4.4.180/mlx5_core.ko index cb3705c1d..a2a8c8912 100644 Binary files a/denverton-4.4.180/mlx5_core.ko and b/denverton-4.4.180/mlx5_core.ko differ diff --git a/denverton-4.4.180/mlxsw_core.ko b/denverton-4.4.180/mlxsw_core.ko index 34a5b5ef5..56d218eab 100644 Binary files a/denverton-4.4.180/mlxsw_core.ko and b/denverton-4.4.180/mlxsw_core.ko differ diff --git a/denverton-4.4.180/mlxsw_pci.ko b/denverton-4.4.180/mlxsw_pci.ko index 66c385a98..9947c7ba8 100644 Binary files a/denverton-4.4.180/mlxsw_pci.ko and b/denverton-4.4.180/mlxsw_pci.ko differ diff --git a/denverton-4.4.180/mpt3sas.ko b/denverton-4.4.180/mpt3sas.ko index 2f173465e..592a27d50 100644 Binary files a/denverton-4.4.180/mpt3sas.ko and b/denverton-4.4.180/mpt3sas.ko differ diff --git a/denverton-4.4.180/r8152.ko b/denverton-4.4.180/r8152.ko index 848a07ac8..76afc979c 100644 Binary files a/denverton-4.4.180/r8152.ko and b/denverton-4.4.180/r8152.ko differ diff --git a/denverton-4.4.180/r8169.ko b/denverton-4.4.180/r8169.ko index 3216473d5..f6f04313c 100644 Binary files a/denverton-4.4.180/r8169.ko and b/denverton-4.4.180/r8169.ko differ diff --git a/denverton-4.4.180/tg3.ko b/denverton-4.4.180/tg3.ko index 049e466bc..d1ec66dfe 100644 Binary files a/denverton-4.4.180/tg3.ko and b/denverton-4.4.180/tg3.ko differ diff --git a/denverton-4.4.180/virtio_pci.ko b/denverton-4.4.180/virtio_pci.ko index 10c986568..af6e71879 100644 Binary files a/denverton-4.4.180/virtio_pci.ko and b/denverton-4.4.180/virtio_pci.ko differ diff --git a/geminilake-4.4.180/b44.ko b/geminilake-4.4.180/b44.ko index 61c6dac84..b398e7c54 100644 Binary files a/geminilake-4.4.180/b44.ko and b/geminilake-4.4.180/b44.ko differ diff --git a/geminilake-4.4.180/be2net.ko b/geminilake-4.4.180/be2net.ko index 13c2916ec..3bcc7c24a 100644 Binary files a/geminilake-4.4.180/be2net.ko and b/geminilake-4.4.180/be2net.ko differ diff --git a/geminilake-4.4.180/bnx2.ko b/geminilake-4.4.180/bnx2.ko index fd2c5748b..537224521 100644 Binary files a/geminilake-4.4.180/bnx2.ko and b/geminilake-4.4.180/bnx2.ko differ diff --git a/geminilake-4.4.180/bnx2x.ko b/geminilake-4.4.180/bnx2x.ko index 487254a24..5038efae2 100644 Binary files a/geminilake-4.4.180/bnx2x.ko and b/geminilake-4.4.180/bnx2x.ko differ diff --git a/geminilake-4.4.180/bnxt_en.ko b/geminilake-4.4.180/bnxt_en.ko index 01dc93dfd..58e914d8f 100644 Binary files a/geminilake-4.4.180/bnxt_en.ko and b/geminilake-4.4.180/bnxt_en.ko differ diff --git a/geminilake-4.4.180/cxgb.ko b/geminilake-4.4.180/cxgb.ko index 5e46d195f..4cea944f3 100644 Binary files a/geminilake-4.4.180/cxgb.ko and b/geminilake-4.4.180/cxgb.ko differ diff --git a/geminilake-4.4.180/cxgb3.ko b/geminilake-4.4.180/cxgb3.ko index 4913f8a79..7014b524f 100644 Binary files a/geminilake-4.4.180/cxgb3.ko and b/geminilake-4.4.180/cxgb3.ko differ diff --git a/geminilake-4.4.180/cxgb4.ko b/geminilake-4.4.180/cxgb4.ko index aad892b96..67f77f24e 100644 Binary files a/geminilake-4.4.180/cxgb4.ko and b/geminilake-4.4.180/cxgb4.ko differ diff --git a/geminilake-4.4.180/cxgb4vf.ko b/geminilake-4.4.180/cxgb4vf.ko index 4eb0f8acc..e64c2056e 100644 Binary files a/geminilake-4.4.180/cxgb4vf.ko and b/geminilake-4.4.180/cxgb4vf.ko differ diff --git a/geminilake-4.4.180/e1000e.ko b/geminilake-4.4.180/e1000e.ko index 163ba9aec..1fabcd92e 100644 Binary files a/geminilake-4.4.180/e1000e.ko and b/geminilake-4.4.180/e1000e.ko differ diff --git a/geminilake-4.4.180/i40e.ko b/geminilake-4.4.180/i40e.ko index c89b14bda..3e0d2b91a 100644 Binary files a/geminilake-4.4.180/i40e.ko and b/geminilake-4.4.180/i40e.ko differ diff --git a/geminilake-4.4.180/igb.ko b/geminilake-4.4.180/igb.ko index 0cc7226f4..2e0bb32b9 100644 Binary files a/geminilake-4.4.180/igb.ko and b/geminilake-4.4.180/igb.ko differ diff --git a/geminilake-4.4.180/igbvf.ko b/geminilake-4.4.180/igbvf.ko index 42c148932..afb57415d 100644 Binary files a/geminilake-4.4.180/igbvf.ko and b/geminilake-4.4.180/igbvf.ko differ diff --git a/geminilake-4.4.180/ixgb.ko b/geminilake-4.4.180/ixgb.ko index 94162d89b..4f8d2e120 100644 Binary files a/geminilake-4.4.180/ixgb.ko and b/geminilake-4.4.180/ixgb.ko differ diff --git a/geminilake-4.4.180/ixgbe.ko b/geminilake-4.4.180/ixgbe.ko index d1bc728d3..f8e6c9285 100644 Binary files a/geminilake-4.4.180/ixgbe.ko and b/geminilake-4.4.180/ixgbe.ko differ diff --git a/geminilake-4.4.180/ixgbevf.ko b/geminilake-4.4.180/ixgbevf.ko index 53f4ecf14..4751fb04a 100644 Binary files a/geminilake-4.4.180/ixgbevf.ko and b/geminilake-4.4.180/ixgbevf.ko differ diff --git a/geminilake-4.4.180/mlx4_core.ko b/geminilake-4.4.180/mlx4_core.ko index 7893c352a..ad6e42d0c 100644 Binary files a/geminilake-4.4.180/mlx4_core.ko and b/geminilake-4.4.180/mlx4_core.ko differ diff --git a/geminilake-4.4.180/mlx4_en.ko b/geminilake-4.4.180/mlx4_en.ko index 2c53843d7..eaa6c2a0e 100644 Binary files a/geminilake-4.4.180/mlx4_en.ko and b/geminilake-4.4.180/mlx4_en.ko differ diff --git a/geminilake-4.4.180/mlx5_core.ko b/geminilake-4.4.180/mlx5_core.ko index d58d9d242..6d1514100 100644 Binary files a/geminilake-4.4.180/mlx5_core.ko and b/geminilake-4.4.180/mlx5_core.ko differ diff --git a/geminilake-4.4.180/mlxsw_core.ko b/geminilake-4.4.180/mlxsw_core.ko index 94fa9242f..9d30e75eb 100644 Binary files a/geminilake-4.4.180/mlxsw_core.ko and b/geminilake-4.4.180/mlxsw_core.ko differ diff --git a/geminilake-4.4.180/mlxsw_pci.ko b/geminilake-4.4.180/mlxsw_pci.ko index 7ffe6fede..b62e69caa 100644 Binary files a/geminilake-4.4.180/mlxsw_pci.ko and b/geminilake-4.4.180/mlxsw_pci.ko differ diff --git a/geminilake-4.4.180/mpt3sas.ko b/geminilake-4.4.180/mpt3sas.ko index 556be3d9d..6e51bd07e 100644 Binary files a/geminilake-4.4.180/mpt3sas.ko and b/geminilake-4.4.180/mpt3sas.ko differ diff --git a/geminilake-4.4.180/r8152.ko b/geminilake-4.4.180/r8152.ko index 0470de2df..f0e482162 100644 Binary files a/geminilake-4.4.180/r8152.ko and b/geminilake-4.4.180/r8152.ko differ diff --git a/geminilake-4.4.180/r8169.ko b/geminilake-4.4.180/r8169.ko index d14c34009..6ae21d006 100644 Binary files a/geminilake-4.4.180/r8169.ko and b/geminilake-4.4.180/r8169.ko differ diff --git a/geminilake-4.4.180/tg3.ko b/geminilake-4.4.180/tg3.ko index 6d024bbb5..aebc139f9 100644 Binary files a/geminilake-4.4.180/tg3.ko and b/geminilake-4.4.180/tg3.ko differ diff --git a/geminilake-4.4.180/virtio_pci.ko b/geminilake-4.4.180/virtio_pci.ko index 61ac1aed8..53c879f9b 100644 Binary files a/geminilake-4.4.180/virtio_pci.ko and b/geminilake-4.4.180/virtio_pci.ko differ diff --git a/r1000-4.4.180/mpt3sas.ko b/r1000-4.4.180/mpt3sas.ko index 826da7e3a..53c83d8c8 100644 Binary files a/r1000-4.4.180/mpt3sas.ko and b/r1000-4.4.180/mpt3sas.ko differ diff --git a/r1000-4.4.180/virtio_pci.ko b/r1000-4.4.180/virtio_pci.ko index 78791cd96..462de591a 100644 Binary files a/r1000-4.4.180/virtio_pci.ko and b/r1000-4.4.180/virtio_pci.ko differ diff --git a/src/4.x/Makefile b/src/4.x/Makefile index ec6b1312a..c5aea874c 100644 --- a/src/4.x/Makefile +++ b/src/4.x/Makefile @@ -1,4 +1,2 @@ obj-y += drivers/ -obj-y += lib/ -obj-y += video/ diff --git a/src/4.x/defines.apollolake b/src/4.x/defines.apollolake index a6520213c..39838eaa0 100644 --- a/src/4.x/defines.apollolake +++ b/src/4.x/defines.apollolake @@ -1,58 +1,13 @@ -CONFIG_ACPI_PROCESSOR=n -CONFIG_ACPI_BUTTON=n -nCONFIG_ACPI_THERMAL=m -CONFIG_CPU_FREQ_GOV_ONDEMAND=n -CONFIG_CPU_FREQ_GOV_CONSERVATIVE=n -CONFIG_CPU_FREQ_GOV_COMMON=n - -CONFIG_HWMON_VID=m -CONFIG_SENSORS_NCT6775=m -CONFIG_RTC_DRV_CMOS=m - -CONFIG_FB=n -CONFIG_FB_CFB_FILLRECT=n -CONFIG_FB_CFB_COPYAREA=n -CONFIG_FB_CFB_IMAGEBLIT=n -CONFIG_FB_SYS_FILLRECT=n -CONFIG_FB_SYS_COPYAREA=n -CONFIG_FB_SYS_IMAGEBLIT=n -CONFIG_FB_SYS_FOPS=n -CONFIG_DRM=n -CONFIG_DRM_KMS_HELPER=n -CONFIG_DRM_I915=n -CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=n -CONFIG_VGASTATE=m -CONFIG_FB_VESA=m -CONFIG_FB_EFI=m -CONFIG_FB_VGA16=m -CONFIG_FRAMEBUFFER_CONSOLE=m -CONFIG_INPUT_KEYBOARD=y -CONFIG_FONT_SUPPORT=m -CONFIG_FONT_8x8=y -CONFIG_FONT_8x16=y -CONFIG_KEYBOARD_ATKBD=m -CONFIG_SERIO_I8042=m - CONFIG_VIRTIO=m CONFIG_VIRTIO_MMIO=m CONFIG_VIRTIO_PCI=m -CONFIG_VIRTIO_PCI_LEGACY=m +CONFIG_VIRTIO_PCI_LEGACY=y CONFIG_VIRTIO_INPUT=m CONFIG_VIRTIO_NET=m CONFIG_SCSI_VIRTIO=m -nCONFIG_PHYLIB=m -CONFIG_MDIO=m -nCONFIG_MII=m -CONFIG_VMXNET3=m CONFIG_USB_RTL8152=m -nCONFIG_USB_NET_CDC_NCM=m - -CONFIG_NET_VENDOR_ATHEROS=y -CONFIG_ATL1E=m -CONFIG_ATL1C=m -CONFIG_ALX=m CONFIG_NET_VENDOR_BROADCOM=y CONFIG_B44=m @@ -70,7 +25,7 @@ CONFIG_CHELSIO_T4VF=m CONFIG_NET_VENDOR_EMULEX=y CONFIG_BE2NET=m -CONFIG_E1000=m +nCONFIG_NET_VENDOR_INTEL=y CONFIG_E1000E=n CONFIG_IGB=n CONFIG_IGBVF=m @@ -82,10 +37,6 @@ CONFIG_IAVF=m CONFIG_ICE=m CONFIG_IGC=m -CONFIG_NET_VENDOR_MARVELL=y -CONFIG_SKGE=m -CONFIG_SKY2=m - CONFIG_NET_VENDOR_MELLANOX=y CONFIG_MLX4_CORE=m CONFIG_MLX4_EN=m @@ -102,47 +53,4 @@ CONFIG_R8169=m CONFIG_R8125=m CONFIG_R8168=m -CONFIG_JME=m - -CONFIG_BLK_DEV_SR=m -CONFIG_RAID_ATTRS=m -CONFIG_SCSI_SPI_ATTRS=m -CONFIG_SCSI_SAS_ATTRS=m -CONFIG_SCSI_SAS_LIBSAS=m CONFIG_SCSI_MPT3SAS=m -CONFIG_FUSION=m -CONFIG_FUSION_SPI=m -CONFIG_FUSION_SAS=m -CONFIG_FUSION_CTL=m -CONFIG_MEGARAID_NEWGEN=m -CONFIG_MEGARAID_MM=m -CONFIG_MEGARAID_MAILBOX=m -CONFIG_MEGARAID_SAS=m - -CONFIG_VMWARE_PVSCSI=m - -CONFIG_VMWARE_VMCI=n - -nCONFIG_MEDIA_SUPPORT=m -nCONFIG_MEDIA_DIGITAL_TV_SUPPORT=y -nCONFIG_RC_CORE=m -nCONFIG_DVB_CORE=m -nCONFIG_DVB_MAX_ADAPTERS=8 -nCONFIG_MEDIA_USB_SUPPORT=y -nCONFIG_DVB_USB=m -nCONFIG_DVB_USB_V2=m -nCONFIG_DVB_USB_DVBSKY=m -nCONFIG_DVB_M88DS3103=m -nCONFIG_DVB_SI2168=m -nCONFIG_DVB_TS2020=m -nCONFIG_MEDIA_TUNER_SI2157=m -nCONFIG_DVB_SP2=m -nCONFIG_DVB_USB_DIB0700=m - -CONFIG_MMC=m -CONFIG_MMC_BLOCK=m -CONFIG_MMC_SDHCI=m -CONFIG_MMC_VIA_SDMMC=m -CONFIG_MMC_VUB300=m -CONFIG_MMC_USHC=m -CONFIG_MMC_MTK=m diff --git a/src/4.x/defines.broadwell b/src/4.x/defines.broadwell index 81c2f9a52..d647ea7ee 100644 --- a/src/4.x/defines.broadwell +++ b/src/4.x/defines.broadwell @@ -1,53 +1,13 @@ -CONFIG_ACPI_PROCESSOR=n -nCONFIG_ACPI_BUTTON=m -nCONFIG_ACPI_THERMAL=m -nCONFIG_CPU_FREQ_GOV_ONDEMAND=m -nCONFIG_CPU_FREQ_GOV_CONSERVATIVE=m - -nCONFIG_FB=m -CONFIG_FB_CFB_FILLRECT=m -CONFIG_FB_CFB_COPYAREA=m -CONFIG_FB_CFB_IMAGEBLIT=m -CONFIG_FB_SYS_FILLRECT=m -CONFIG_FB_SYS_COPYAREA=m -CONFIG_FB_SYS_IMAGEBLIT=m -CONFIG_FB_SYS_FOPS=m -CONFIG_VGASTATE=m -CONFIG_FB_VESA=m -CONFIG_FB_EFI=m -CONFIG_FB_VGA16=m -CONFIG_FRAMEBUFFER_CONSOLE=m -CONFIG_FONT_SUPPORT=m -CONFIG_FONT_8x8=y -CONFIG_FONT_8x16=y -CONFIG_INPUT_KEYBOARD=y -CONFIG_KEYBOARD_ATKBD=m -CONFIG_SERIO_I8042=m - -nCONFIG_HWMON_VID=y -CONFIG_SENSORS_NCT6775=m -nCONFIG_RTC_DRV_CMOS=y - CONFIG_VIRTIO=m CONFIG_VIRTIO_MMIO=m CONFIG_VIRTIO_PCI=m -CONFIG_VIRTIO_PCI_LEGACY=m +CONFIG_VIRTIO_PCI_LEGACY=y CONFIG_VIRTIO_INPUT=m CONFIG_VIRTIO_NET=m CONFIG_SCSI_VIRTIO=m -nCONFIG_PHYLIB=m -CONFIG_MDIO=n - -CONFIG_VMXNET3=m CONFIG_USB_RTL8152=m -nCONFIG_USB_NET_CDC_NCM=m - -CONFIG_NET_VENDOR_ATHEROS=y -CONFIG_ATL1E=m -CONFIG_ATL1C=m -CONFIG_ALX=m nCONFIG_NET_VENDOR_BROADCOM=y CONFIG_B44=m @@ -64,7 +24,7 @@ CONFIG_CHELSIO_T4VF=m CONFIG_NET_VENDOR_EMULEX=y CONFIG_BE2NET=m -CONFIG_E1000=m +nCONFIG_NET_VENDOR_INTEL=y CONFIG_E1000E=m CONFIG_IGB=n CONFIG_IGBVF=m @@ -76,15 +36,6 @@ CONFIG_IAVF=m CONFIG_ICE=m CONFIG_IGC=m -CONFIG_NET_VENDOR_MARVELL=y -CONFIG_SKGE=m -CONFIG_SKY2=m - -nCONFIG_NET_VENDOR_REALTEK=y -CONFIG_R8169=m -CONFIG_R8125=m -CONFIG_R8168=m - CONFIG_NET_VENDOR_MELLANOX=y CONFIG_MLX4_CORE=m CONFIG_MLX4_EN=m @@ -97,29 +48,9 @@ CONFIG_MLXSW_PCI=m CONFIG_MLXSW_SWITCHX2=n CONFIG_MLXSW_SPECTRUM=n -CONFIG_JME=m +nCONFIG_NET_VENDOR_REALTEK=y +CONFIG_R8169=m +CONFIG_R8125=m +CONFIG_R8168=m -CONFIG_BLK_DEV_SR=m -nCONFIG_RAID_ATTRS=y -CONFIG_SCSI_SPI_ATTRS=m -nCONFIG_SCSI_SAS_ATTRS=y -nCONFIG_SCSI_SAS_LIBSAS=y nCONFIG_SCSI_MPT3SAS=m -CONFIG_FUSION=m -CONFIG_FUSION_SPI=m -CONFIG_FUSION_SAS=m -CONFIG_FUSION_CTL=m -CONFIG_MEGARAID_NEWGEN=m -CONFIG_MEGARAID_MM=m -CONFIG_MEGARAID_MAILBOX=m -CONFIG_MEGARAID_SAS=m - -CONFIG_VMWARE_PVSCSI=m - -CONFIG_MMC=m -CONFIG_MMC_BLOCK=m -CONFIG_MMC_SDHCI=m -CONFIG_MMC_VIA_SDMMC=m -CONFIG_MMC_VUB300=m -CONFIG_MMC_USHC=m -CONFIG_MMC_MTK=m diff --git a/src/4.x/defines.broadwellnk b/src/4.x/defines.broadwellnk index b28a911f7..f564e89c5 100644 --- a/src/4.x/defines.broadwellnk +++ b/src/4.x/defines.broadwellnk @@ -1,53 +1,13 @@ -CONFIG_ACPI_PROCESSOR=n -nCONFIG_ACPI_BUTTON=m -nCONFIG_ACPI_THERMAL=m -nCONFIG_CPU_FREQ_GOV_ONDEMAND=m -nCONFIG_CPU_FREQ_GOV_CONSERVATIVE=m - -nCONFIG_FB=m -CONFIG_FB_CFB_FILLRECT=m -CONFIG_FB_CFB_COPYAREA=m -CONFIG_FB_CFB_IMAGEBLIT=m -CONFIG_FB_SYS_FILLRECT=m -CONFIG_FB_SYS_COPYAREA=m -CONFIG_FB_SYS_IMAGEBLIT=m -CONFIG_FB_SYS_FOPS=m -CONFIG_VGASTATE=m -CONFIG_FB_VESA=m -CONFIG_FB_EFI=m -CONFIG_FB_VGA16=m -CONFIG_FRAMEBUFFER_CONSOLE=m -CONFIG_FONT_SUPPORT=m -CONFIG_FONT_8x8=y -CONFIG_FONT_8x16=y -CONFIG_INPUT_KEYBOARD=y -CONFIG_KEYBOARD_ATKBD=m -CONFIG_SERIO_I8042=m - -nCONFIG_HWMON_VID=y -CONFIG_SENSORS_NCT6775=m -nCONFIG_RTC_DRV_CMOS=y - CONFIG_VIRTIO=m CONFIG_VIRTIO_MMIO=m CONFIG_VIRTIO_PCI=m -CONFIG_VIRTIO_PCI_LEGACY=m +CONFIG_VIRTIO_PCI_LEGACY=y CONFIG_VIRTIO_INPUT=m CONFIG_VIRTIO_NET=m CONFIG_SCSI_VIRTIO=m -CONFIG_MDIO=n -nCONFIG_PHYLIB=m - -CONFIG_VMXNET3=m CONFIG_USB_RTL8152=m -nCONFIG_USB_NET_CDC_NCM=m - -CONFIG_NET_VENDOR_ATHEROS=y -CONFIG_ATL1E=m -CONFIG_ATL1C=m -CONFIG_ALX=m nCONFIG_NET_VENDOR_BROADCOM=y CONFIG_B44=m @@ -65,7 +25,7 @@ CONFIG_CHELSIO_T4VF=m CONFIG_NET_VENDOR_EMULEX=y CONFIG_BE2NET=m -CONFIG_E1000=m +nCONFIG_NET_VENDOR_INTEL=y CONFIG_E1000E=m nCONFIG_IGB=m CONFIG_IGBVF=m @@ -77,15 +37,6 @@ CONFIG_IAVF=m CONFIG_ICE=m CONFIG_IGC=m -CONFIG_NET_VENDOR_MARVELL=y -CONFIG_SKGE=m -CONFIG_SKY2=m - -nCONFIG_NET_VENDOR_REALTEK=y -CONFIG_R8169=m -CONFIG_R8125=m -CONFIG_R8168=m - CONFIG_NET_VENDOR_MELLANOX=y CONFIG_MLX4_CORE=m CONFIG_MLX4_EN=m @@ -98,29 +49,9 @@ CONFIG_MLXSW_PCI=m CONFIG_MLXSW_SWITCHX2=n CONFIG_MLXSW_SPECTRUM=n -CONFIG_JME=m +nCONFIG_NET_VENDOR_REALTEK=y +CONFIG_R8169=m +CONFIG_R8125=m +CONFIG_R8168=m -CONFIG_BLK_DEV_SR=m -nCONFIG_RAID_ATTRS=y -CONFIG_SCSI_SPI_ATTRS=m -nCONFIG_SCSI_SAS_ATTRS=y -nCONFIG_SCSI_SAS_LIBSAS=y nCONFIG_SCSI_MPT3SAS=m -CONFIG_FUSION=m -CONFIG_FUSION_SPI=m -CONFIG_FUSION_SAS=m -CONFIG_FUSION_CTL=m -CONFIG_MEGARAID_NEWGEN=m -CONFIG_MEGARAID_MM=m -CONFIG_MEGARAID_MAILBOX=m -CONFIG_MEGARAID_SAS=m - -CONFIG_VMWARE_PVSCSI=m - -CONFIG_MMC=m -CONFIG_MMC_BLOCK=m -CONFIG_MMC_SDHCI=m -CONFIG_MMC_VIA_SDMMC=m -CONFIG_MMC_VUB300=m -CONFIG_MMC_USHC=m -CONFIG_MMC_MTK=m diff --git a/src/4.x/defines.denverton b/src/4.x/defines.denverton index 51b8dab4a..d9199e8bf 100644 --- a/src/4.x/defines.denverton +++ b/src/4.x/defines.denverton @@ -1,53 +1,13 @@ -CONFIG_ACPI_PROCESSOR=n -nCONFIG_ACPI_BUTTON=m -nCONFIG_ACPI_THERMAL=m -nCONFIG_ACPI_VIDEO=m - -CONFIG_HWMON_VID=n -CONFIG_SENSORS_NCT6775=m -nCONFIG_RTC_DRV_CMOS=y - -nCONFIG_FB=m -CONFIG_FB_CFB_FILLRECT=m -CONFIG_FB_CFB_COPYAREA=m -CONFIG_FB_CFB_IMAGEBLIT=m -CONFIG_FB_SYS_FILLRECT=m -CONFIG_FB_SYS_COPYAREA=m -CONFIG_FB_SYS_IMAGEBLIT=m -CONFIG_FB_SYS_FOPS=m -CONFIG_VGASTATE=m -CONFIG_FB_VESA=m -CONFIG_FB_EFI=m -CONFIG_FB_VGA16=m -CONFIG_FRAMEBUFFER_CONSOLE=m -CONFIG_FONT_SUPPORT=m -CONFIG_FONT_8x8=y -CONFIG_FONT_8x16=y -CONFIG_INPUT_KEYBOARD=y -CONFIG_KEYBOARD_ATKBD=m -CONFIG_SERIO_I8042=m - CONFIG_VIRTIO=m CONFIG_VIRTIO_MMIO=m CONFIG_VIRTIO_PCI=m -CONFIG_VIRTIO_PCI_LEGACY=m +CONFIG_VIRTIO_PCI_LEGACY=y CONFIG_VIRTIO_INPUT=m CONFIG_VIRTIO_NET=m CONFIG_SCSI_VIRTIO=m -CONFIG_MDIO=n -nCONFIG_MII=m -nCONFIG_PHYLIB=m - -CONFIG_VMXNET3=m CONFIG_USB_RTL8152=m -nCONFIG_USB_NET_CDC_NCM=m - -CONFIG_NET_VENDOR_ATHEROS=y -CONFIG_ATL1E=m -CONFIG_ATL1C=m -CONFIG_ALX=m nCONFIG_NET_VENDOR_BROADCOM=y CONFIG_B44=m @@ -65,7 +25,7 @@ CONFIG_CHELSIO_T4VF=m nCONFIG_NET_VENDOR_EMULEX=y CONFIG_BE2NET=n -CONFIG_E1000=m +nCONFIG_NET_VENDOR_INTEL=y CONFIG_E1000E=m CONFIG_IGB=n CONFIG_IGBVF=m @@ -77,15 +37,6 @@ CONFIG_IAVF=m CONFIG_ICE=m CONFIG_IGC=m -CONFIG_NET_VENDOR_MARVELL=y -CONFIG_SKGE=m -CONFIG_SKY2=m - -CONFIG_NET_VENDOR_REALTEK=y -CONFIG_R8169=m -CONFIG_R8125=m -CONFIG_R8168=m - CONFIG_NET_VENDOR_MELLANOX=y CONFIG_MLX4_CORE=m CONFIG_MLX4_EN=m @@ -98,29 +49,9 @@ CONFIG_MLXSW_PCI=m CONFIG_MLXSW_SWITCHX2=n CONFIG_MLXSW_SPECTRUM=n -CONFIG_JME=m +CONFIG_NET_VENDOR_REALTEK=y +CONFIG_R8169=m +CONFIG_R8125=m +CONFIG_R8168=m -CONFIG_BLK_DEV_SR=m -CONFIG_RAID_ATTRS=m -CONFIG_SCSI_SPI_ATTRS=m -CONFIG_SCSI_SAS_ATTRS=m -CONFIG_SCSI_SAS_LIBSAS=m CONFIG_SCSI_MPT3SAS=m -CONFIG_FUSION=m -CONFIG_FUSION_SPI=m -CONFIG_FUSION_SAS=m -CONFIG_FUSION_CTL=m -CONFIG_MEGARAID_NEWGEN=m -CONFIG_MEGARAID_MM=m -CONFIG_MEGARAID_MAILBOX=m -CONFIG_MEGARAID_SAS=m - -CONFIG_VMWARE_PVSCSI=m - -CONFIG_MMC=m -CONFIG_MMC_BLOCK=m -CONFIG_MMC_SDHCI=m -CONFIG_MMC_VIA_SDMMC=m -CONFIG_MMC_VUB300=m -CONFIG_MMC_USHC=m -CONFIG_MMC_MTK=m diff --git a/src/4.x/defines.geminilake b/src/4.x/defines.geminilake index a9c6b9211..3d78673ea 100644 --- a/src/4.x/defines.geminilake +++ b/src/4.x/defines.geminilake @@ -1,57 +1,13 @@ -CONFIG_ACPI_PROCESSOR=n -CONFIG_ACPI_BUTTON=n -nCONFIG_ACPI_THERMAL=m -CONFIG_ACPI_VIDEO=n - -CONFIG_HWMON_VID=m -CONFIG_SENSORS_NCT6775=m -CONFIG_RTC_DRV_CMOS=m - -CONFIG_FB=n -CONFIG_FB_CFB_FILLRECT=n -CONFIG_FB_CFB_COPYAREA=n -CONFIG_FB_CFB_IMAGEBLIT=n -CONFIG_FB_SYS_FILLRECT=n -CONFIG_FB_SYS_COPYAREA=n -CONFIG_FB_SYS_IMAGEBLIT=n -CONFIG_FB_SYS_FOPS=n -CONFIG_DRM=n -CONFIG_DRM_KMS_HELPER=n -CONFIG_DRM_I915=n -CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=n -CONFIG_VGASTATE=m -CONFIG_FB_VESA=m -CONFIG_FB_EFI=m -CONFIG_FB_VGA16=m -CONFIG_FRAMEBUFFER_CONSOLE=m -CONFIG_FONT_SUPPORT=m -CONFIG_FONT_8x8=y -CONFIG_FONT_8x16=y -nCONFIG_INPUT_KEYBOARD=y -nCONFIG_KEYBOARD_ATKBD=y -nCONFIG_SERIO_I8042=y - CONFIG_VIRTIO=m CONFIG_VIRTIO_MMIO=m CONFIG_VIRTIO_PCI=m -CONFIG_VIRTIO_PCI_LEGACY=m +CONFIG_VIRTIO_PCI_LEGACY=y CONFIG_VIRTIO_INPUT=m CONFIG_VIRTIO_NET=m CONFIG_SCSI_VIRTIO=m -CONFIG_MDIO=m -nCONFIG_MII=m -nCONFIG_PHYLIB=m - -CONFIG_VMXNET3=m CONFIG_USB_RTL8152=m -nCONFIG_USB_NET_CDC_NCM=m - -CONFIG_NET_VENDOR_ATHEROS=y -CONFIG_ATL1E=m -CONFIG_ATL1C=m -CONFIG_ALX=m CONFIG_NET_VENDOR_BROADCOM=y CONFIG_B44=m @@ -69,7 +25,7 @@ CONFIG_CHELSIO_T4VF=m CONFIG_NET_VENDOR_EMULEX=y CONFIG_BE2NET=m -CONFIG_E1000=m +nCONFIG_NET_VENDOR_INTEL=y CONFIG_E1000E=m nCONFIG_IGB=m CONFIG_IGBVF=m @@ -81,15 +37,6 @@ CONFIG_IAVF=m CONFIG_ICE=m CONFIG_IGC=m -CONFIG_NET_VENDOR_MARVELL=y -CONFIG_SKGE=m -CONFIG_SKY2=m - -nCONFIG_NET_VENDOR_REALTEK=y -CONFIG_R8169=m -CONFIG_R8125=m -CONFIG_R8168=m - CONFIG_NET_VENDOR_MELLANOX=y CONFIG_MLX4_CORE=m CONFIG_MLX4_EN=m @@ -102,29 +49,9 @@ CONFIG_MLXSW_PCI=m CONFIG_MLXSW_SWITCHX2=n CONFIG_MLXSW_SPECTRUM=n -CONFIG_JME=m +nCONFIG_NET_VENDOR_REALTEK=y +CONFIG_R8169=m +CONFIG_R8125=m +CONFIG_R8168=m -CONFIG_BLK_DEV_SR=m -CONFIG_RAID_ATTRS=m -CONFIG_SCSI_SPI_ATTRS=m -CONFIG_SCSI_SAS_ATTRS=m -CONFIG_SCSI_SAS_LIBSAS=m CONFIG_SCSI_MPT3SAS=m -CONFIG_FUSION=m -CONFIG_FUSION_SPI=m -CONFIG_FUSION_SAS=m -CONFIG_FUSION_CTL=m -CONFIG_MEGARAID_NEWGEN=m -CONFIG_MEGARAID_MM=m -CONFIG_MEGARAID_MAILBOX=m -CONFIG_MEGARAID_SAS=m - -CONFIG_VMWARE_PVSCSI=m - -CONFIG_MMC=m -CONFIG_MMC_BLOCK=m -CONFIG_MMC_SDHCI=m -CONFIG_MMC_VIA_SDMMC=m -CONFIG_MMC_VUB300=m -CONFIG_MMC_USHC=m -CONFIG_MMC_MTK=m diff --git a/src/4.x/defines.r1000 b/src/4.x/defines.r1000 index 209cd5eb6..ec2053c17 100644 --- a/src/4.x/defines.r1000 +++ b/src/4.x/defines.r1000 @@ -1,10 +1,5 @@ CONFIG_ACPI_PROCESSOR=n -nCONFIG_ACPI_BUTTON=m -nCONFIG_ACPI_THERMAL=m -nCONFIG_ACPI_VIDEO=m -nCONFIG_CPU_FREQ_GOV_ONDEMAND=m -nCONFIG_CPU_FREQ_GOV_CONSERVATIVE=m nCONFIG_FB=m CONFIG_FB_CFB_FILLRECT=m @@ -33,7 +28,7 @@ CONFIG_RTC_DRV_CMOS=m CONFIG_VIRTIO=m CONFIG_VIRTIO_MMIO=m CONFIG_VIRTIO_PCI=m -CONFIG_VIRTIO_PCI_LEGACY=m +CONFIG_VIRTIO_PCI_LEGACY=y CONFIG_VIRTIO_INPUT=m CONFIG_VIRTIO_NET=m CONFIG_SCSI_VIRTIO=m @@ -67,6 +62,7 @@ CONFIG_CHELSIO_T4VF=m CONFIG_NET_VENDOR_EMULEX=y CONFIG_BE2NET=m +nCONFIG_NET_VENDOR_INTEL=y CONFIG_E1000=m CONFIG_E1000E=m CONFIG_IGB=m @@ -79,15 +75,6 @@ CONFIG_IAVF=m CONFIG_ICE=m CONFIG_IGC=m -CONFIG_NET_VENDOR_MARVELL=y -CONFIG_SKGE=m -CONFIG_SKY2=m - -nCONFIG_NET_VENDOR_REALTEK=y -CONFIG_R8169=m -CONFIG_R8125=m -CONFIG_R8168=m - CONFIG_NET_VENDOR_MELLANOX=y CONFIG_MLX4_CORE=m CONFIG_MLX4_EN=m @@ -100,6 +87,11 @@ CONFIG_MLXSW_PCI=m CONFIG_MLXSW_SWITCHX2=n CONFIG_MLXSW_SPECTRUM=n +nCONFIG_NET_VENDOR_REALTEK=y +CONFIG_R8169=m +CONFIG_R8125=m +CONFIG_R8168=m + CONFIG_JME=m CONFIG_BLK_DEV_SR=m diff --git a/src/4.x/defines.v1000 b/src/4.x/defines.v1000 index ad02383d9..5ca9d5f94 100644 --- a/src/4.x/defines.v1000 +++ b/src/4.x/defines.v1000 @@ -1,55 +1,13 @@ -CONFIG_ACPI_PROCESSOR=n -nCONFIG_ACPI_BUTTON=m -nCONFIG_ACPI_THERMAL=m -nCONFIG_ACPI_VIDEO=m -nCONFIG_CPU_FREQ_GOV_ONDEMAND=m -nCONFIG_CPU_FREQ_GOV_CONSERVATIVE=m - -nCONFIG_FB=m -CONFIG_FB_CFB_FILLRECT=m -CONFIG_FB_CFB_COPYAREA=m -CONFIG_FB_CFB_IMAGEBLIT=m -CONFIG_FB_SYS_FILLRECT=m -CONFIG_FB_SYS_COPYAREA=m -CONFIG_FB_SYS_IMAGEBLIT=m -CONFIG_FB_SYS_FOPS=m -CONFIG_VGASTATE=m -CONFIG_FB_VESA=m -CONFIG_FB_EFI=m -CONFIG_FB_VGA16=m -CONFIG_FRAMEBUFFER_CONSOLE=m -CONFIG_FONT_SUPPORT=m -CONFIG_FONT_8x8=y -CONFIG_FONT_8x16=y -CONFIG_INPUT_KEYBOARD=y -CONFIG_KEYBOARD_ATKBD=m -CONFIG_SERIO_I8042=m - -CONFIG_HWMON_VID=n -CONFIG_SENSORS_NCT6775=m -CONFIG_RTC_DRV_CMOS=m - CONFIG_VIRTIO=m CONFIG_VIRTIO_MMIO=m CONFIG_VIRTIO_PCI=m -CONFIG_VIRTIO_PCI_LEGACY=m +CONFIG_VIRTIO_PCI_LEGACY=y CONFIG_VIRTIO_INPUT=m CONFIG_VIRTIO_NET=m CONFIG_SCSI_VIRTIO=m -CONFIG_MDIO=n -nCONFIG_MII=m -nCONFIG_PHYLIB=y - -CONFIG_VMXNET3=m CONFIG_USB_RTL8152=m -nCONFIG_USB_NET_CDC_NCM=m - -CONFIG_NET_VENDOR_ATHEROS=y -CONFIG_ATL1E=m -CONFIG_ATL1C=m -CONFIG_ALX=m nCONFIG_NET_VENDOR_BROADCOM=y CONFIG_B44=n @@ -67,7 +25,7 @@ CONFIG_CHELSIO_T4VF=m CONFIG_NET_VENDOR_EMULEX=y CONFIG_BE2NET=m -CONFIG_E1000=m +nCONFIG_NET_VENDOR_INTEL=y CONFIG_E1000E=m CONFIG_IGBVF=m CONFIG_IXGBE=m @@ -79,15 +37,6 @@ CONFIG_ICE=m CONFIG_IGB=m CONFIG_IGC=m -CONFIG_NET_VENDOR_MARVELL=y -CONFIG_SKGE=m -CONFIG_SKY2=m - -nCONFIG_NET_VENDOR_REALTEK=y -CONFIG_R8169=m -CONFIG_R8125=m -CONFIG_R8168=m - CONFIG_NET_VENDOR_MELLANOX=y CONFIG_MLX4_CORE=m CONFIG_MLX4_EN=m @@ -100,28 +49,9 @@ CONFIG_MLXSW_PCI=m CONFIG_MLXSW_SWITCHX2=n CONFIG_MLXSW_SPECTRUM=n -CONFIG_JME=m +nCONFIG_NET_VENDOR_REALTEK=y +CONFIG_R8169=m +CONFIG_R8125=m +CONFIG_R8168=m -CONFIG_BLK_DEV_SR=m -CONFIG_RAID_ATTRS=m -CONFIG_SCSI_SPI_ATTRS=m -CONFIG_SCSI_SAS_ATTRS=m -CONFIG_SCSI_SAS_LIBSAS=m CONFIG_SCSI_MPT3SAS=m -CONFIG_FUSION=m -CONFIG_FUSION_SPI=m -CONFIG_FUSION_SAS=m -CONFIG_FUSION_CTL=m -CONFIG_VMWARE_PVSCSI=m -CONFIG_MEGARAID_NEWGEN=m -CONFIG_MEGARAID_MM=m -CONFIG_MEGARAID_MAILBOX=m -CONFIG_MEGARAID_SAS=m - -CONFIG_MMC=m -CONFIG_MMC_BLOCK=m -CONFIG_MMC_SDHCI=m -CONFIG_MMC_VIA_SDMMC=m -CONFIG_MMC_VUB300=m -CONFIG_MMC_USHC=m -CONFIG_MMC_MTK=m diff --git a/src/4.x/drivers/Makefile b/src/4.x/drivers/Makefile index 932bc52f3..6f62256ba 100644 --- a/src/4.x/drivers/Makefile +++ b/src/4.x/drivers/Makefile @@ -17,13 +17,13 @@ #obj-$(CONFIG_PCI) += pci/ #obj-$(CONFIG_PARISC) += parisc/ #obj-$(CONFIG_RAPIDIO) += rapidio/ -obj-y += video/ +#obj-y += video/ #obj-y += idle/ # IPMI must come before ACPI in order to provide IPMI opregion support #obj-$(CONFIG_IPMI_HANDLER) += char/ipmi/ -obj-$(CONFIG_ACPI) += acpi/ +#obj-$(CONFIG_ACPI) += acpi/ #obj-$(CONFIG_SFI) += sfi/ # PnP must come after ACPI since it will eventually need to check if acpi # was used and do nothing if so @@ -54,7 +54,7 @@ obj-$(CONFIG_VIRTIO) += virtio/ #obj-$(CONFIG_IOMMU_SUPPORT) += iommu/ # gpu/ comes after char for AGP vs DRM startup and after iommu -obj-y += gpu/ +#obj-y += gpu/ #obj-$(CONFIG_CONNECTOR) += connector/ @@ -80,11 +80,11 @@ obj-$(CONFIG_SCSI) += scsi/ #obj-y += hsi/ obj-y += net/ #obj-$(CONFIG_ATM) += atm/ -obj-$(CONFIG_FUSION) += message/ +#obj-$(CONFIG_FUSION) += message/ #obj-y += firewire/ #obj-$(CONFIG_UIO) += uio/ #obj-$(CONFIG_VFIO) += vfio/ -obj-y += cdrom/ +#obj-y += cdrom/ #obj-y += auxdisplay/ #obj-$(CONFIG_PCCARD) += pcmcia/ #obj-$(CONFIG_DIO) += dio/ @@ -100,17 +100,17 @@ obj-y += cdrom/ #obj-$(CONFIG_PCI) += usb/ #obj-$(CONFIG_USB_GADGET) += usb/ #obj-$(CONFIG_OF) += usb/ -obj-$(CONFIG_SERIO) += input/serio/ +#obj-$(CONFIG_SERIO) += input/serio/ #obj-$(CONFIG_GAMEPORT) += input/gameport/ -obj-$(CONFIG_INPUT) += input/ -obj-$(CONFIG_RTC_LIB) += rtc/ +#obj-$(CONFIG_INPUT) += input/ +#obj-$(CONFIG_RTC_LIB) += rtc/ #obj-y += i2c/ media/ -obj-y += media/ +#obj-y += media/ #obj-$(CONFIG_PPS) += pps/ #obj-$(CONFIG_PTP_1588_CLOCK) += ptp/ #obj-$(CONFIG_W1) += w1/ #obj-$(CONFIG_POWER_SUPPLY) += power/ -obj-$(CONFIG_HWMON) += hwmon/ +#obj-$(CONFIG_HWMON) += hwmon/ #obj-$(CONFIG_THERMAL) += thermal/ #obj-$(CONFIG_WATCHDOG) += watchdog/ #obj-$(CONFIG_MD) += md/ @@ -120,9 +120,9 @@ obj-$(CONFIG_HWMON) += hwmon/ #obj-$(CONFIG_EDAC) += edac/ #obj-$(CONFIG_EISA) += eisa/ #obj-y += lguest/ -obj-$(CONFIG_CPU_FREQ) += cpufreq/ +#obj-$(CONFIG_CPU_FREQ) += cpufreq/ #obj-$(CONFIG_CPU_IDLE) += cpuidle/ -obj-y += mmc/ +#obj-y += mmc/ #obj-$(CONFIG_MEMSTICK) += memstick/ #obj-y += leds/ #obj-$(CONFIG_INFINIBAND) += infiniband/ diff --git a/src/4.x/drivers/acpi/Makefile b/src/4.x/drivers/acpi/Makefile deleted file mode 100644 index a6744533e..000000000 --- a/src/4.x/drivers/acpi/Makefile +++ /dev/null @@ -1,99 +0,0 @@ -# -# Makefile for the Linux ACPI interpreter -# - -ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT - -# -# ACPI Boot-Time Table Parsing -# -#obj-y += tables.o -#obj-$(CONFIG_X86) += blacklist.o - -# -# ACPI Core Subsystem (Interpreter) -# -#obj-y += acpi.o \ - acpica/ - -# All the builtin files are in the "acpi." module_param namespace. -#acpi-y += osl.o utils.o reboot.o -#acpi-y += nvs.o - -# Power management related files -#acpi-y += wakeup.o -#acpi-$(CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT) += sleep.o -#acpi-y += device_sysfs.o device_pm.o -#acpi-$(CONFIG_ACPI_SLEEP) += proc.o - - -# -# ACPI Bus and Device Drivers -# -#acpi-y += bus.o glue.o -#acpi-y += scan.o -#acpi-y += resource.o -#acpi-y += acpi_processor.o -#acpi-y += processor_core.o -#acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o -#acpi-y += ec.o -#acpi-$(CONFIG_ACPI_DOCK) += dock.o -#acpi-y += pci_root.o pci_link.o pci_irq.o -#acpi-y += acpi_lpss.o acpi_apd.o -#acpi-y += acpi_platform.o -#acpi-y += acpi_pnp.o -#acpi-y += int340x_thermal.o -#acpi-y += power.o -#acpi-y += event.o -#acpi-y += sysfs.o -#acpi-y += property.o -#acpi-$(CONFIG_X86) += acpi_cmos_rtc.o -#acpi-$(CONFIG_DEBUG_FS) += debugfs.o -#acpi-$(CONFIG_ACPI_NUMA) += numa.o -#acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o -#acpi-y += acpi_lpat.o -#acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o - -# These are (potentially) separate modules - -# IPMI may be used by other drivers, so it has to initialise before them -#obj-$(CONFIG_ACPI_IPMI) += acpi_ipmi.o - -#obj-$(CONFIG_ACPI_AC) += ac.o -obj-$(CONFIG_ACPI_BUTTON) += button.o -#obj-$(CONFIG_ACPI_FAN) += fan.o -#obj-$(CONFIG_ACPI_VIDEO) += video.o -#obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o -obj-$(CONFIG_ACPI_PROCESSOR) += processor.o -#obj-y += container.o -obj-$(CONFIG_ACPI_THERMAL) += thermal.o -#obj-$(CONFIG_ACPI_NFIT) += nfit.o -#obj-y += acpi_memhotplug.o -#obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o -#obj-$(CONFIG_ACPI_BATTERY) += battery.o -#obj-$(CONFIG_ACPI_SBS) += sbshc.o -#obj-$(CONFIG_ACPI_SBS) += sbs.o -#obj-$(CONFIG_ACPI_HED) += hed.o -#obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o -#obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o -#obj-$(CONFIG_ACPI_BGRT) += bgrt.o -#obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o - -# processor has its own "processor." module_param namespace -processor-y := processor_driver.o -processor-$(CONFIG_ACPI_PROCESSOR_IDLE) += processor_idle.o -processor-$(CONFIG_ACPI_CPU_FREQ_PSS) += processor_throttling.o \ - processor_thermal.o -processor-$(CONFIG_CPU_FREQ) += processor_perflib.o - -#obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o - -#obj-$(CONFIG_ACPI_APEI) += apei/ - -#obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o - -#obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o -#obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o -#obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o - -#video-objs += acpi_video.o video_detect.o diff --git a/src/4.x/drivers/acpi/button.c b/src/4.x/drivers/acpi/button.c deleted file mode 100644 index 5c3b0918d..000000000 --- a/src/4.x/drivers/acpi/button.c +++ /dev/null @@ -1,449 +0,0 @@ -/* - * button.c - ACPI Button Driver - * - * Copyright (C) 2001, 2002 Andy Grover - * Copyright (C) 2001, 2002 Paul Diefenbaugh - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PREFIX "ACPI: " - -#define ACPI_BUTTON_CLASS "button" -#define ACPI_BUTTON_FILE_INFO "info" -#define ACPI_BUTTON_FILE_STATE "state" -#define ACPI_BUTTON_TYPE_UNKNOWN 0x00 -#define ACPI_BUTTON_NOTIFY_STATUS 0x80 - -#define ACPI_BUTTON_SUBCLASS_POWER "power" -#define ACPI_BUTTON_HID_POWER "PNP0C0C" -#define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button" -#define ACPI_BUTTON_TYPE_POWER 0x01 - -#define ACPI_BUTTON_SUBCLASS_SLEEP "sleep" -#define ACPI_BUTTON_HID_SLEEP "PNP0C0E" -#define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button" -#define ACPI_BUTTON_TYPE_SLEEP 0x03 - -#define ACPI_BUTTON_SUBCLASS_LID "lid" -#define ACPI_BUTTON_HID_LID "PNP0C0D" -#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" -#define ACPI_BUTTON_TYPE_LID 0x05 - -#define _COMPONENT ACPI_BUTTON_COMPONENT -ACPI_MODULE_NAME("button"); - -MODULE_AUTHOR("Paul Diefenbaugh"); -MODULE_DESCRIPTION("ACPI Button Driver"); -MODULE_LICENSE("GPL"); - -static const struct acpi_device_id button_device_ids[] = { - {ACPI_BUTTON_HID_LID, 0}, - {ACPI_BUTTON_HID_SLEEP, 0}, - {ACPI_BUTTON_HID_SLEEPF, 0}, - {ACPI_BUTTON_HID_POWER, 0}, - {ACPI_BUTTON_HID_POWERF, 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, button_device_ids); - -static int acpi_button_add(struct acpi_device *device); -static int acpi_button_remove(struct acpi_device *device); -static void acpi_button_notify(struct acpi_device *device, u32 event); - -#ifdef CONFIG_PM_SLEEP -static int acpi_button_suspend(struct device *dev); -static int acpi_button_resume(struct device *dev); -#else -#define acpi_button_suspend NULL -#define acpi_button_resume NULL -#endif -static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume); - -static struct acpi_driver acpi_button_driver = { - .name = "button", - .class = ACPI_BUTTON_CLASS, - .ids = button_device_ids, - .ops = { - .add = acpi_button_add, - .remove = acpi_button_remove, - .notify = acpi_button_notify, - }, - .drv.pm = &acpi_button_pm, -}; - -struct acpi_button { - unsigned int type; - struct input_dev *input; - char phys[32]; /* for input device */ - unsigned long pushed; - bool suspended; -}; - -static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier); -static struct acpi_device *lid_device; - -/* -------------------------------------------------------------------------- - FS Interface (/proc) - -------------------------------------------------------------------------- */ - -static struct proc_dir_entry *acpi_button_dir; -static struct proc_dir_entry *acpi_lid_dir; - -static int acpi_button_state_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_device *device = seq->private; - acpi_status status; - unsigned long long state; - - status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state); - seq_printf(seq, "state: %s\n", - ACPI_FAILURE(status) ? "unsupported" : - (state ? "open" : "closed")); - return 0; -} - -static int acpi_button_state_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_button_state_seq_show, PDE_DATA(inode)); -} - -static const struct file_operations acpi_button_state_fops = { - .owner = THIS_MODULE, - .open = acpi_button_state_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int acpi_button_add_fs(struct acpi_device *device) -{ - struct acpi_button *button = acpi_driver_data(device); - struct proc_dir_entry *entry = NULL; - int ret = 0; - - /* procfs I/F for ACPI lid device only */ - if (button->type != ACPI_BUTTON_TYPE_LID) - return 0; - - if (acpi_button_dir || acpi_lid_dir) { - printk(KERN_ERR PREFIX "More than one Lid device found!\n"); - return -EEXIST; - } - - /* create /proc/acpi/button */ - acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir); - if (!acpi_button_dir) - return -ENODEV; - - /* create /proc/acpi/button/lid */ - acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); - if (!acpi_lid_dir) { - ret = -ENODEV; - goto remove_button_dir; - } - - /* create /proc/acpi/button/lid/LID/ */ - acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir); - if (!acpi_device_dir(device)) { - ret = -ENODEV; - goto remove_lid_dir; - } - - /* create /proc/acpi/button/lid/LID/state */ - entry = proc_create_data(ACPI_BUTTON_FILE_STATE, - S_IRUGO, acpi_device_dir(device), - &acpi_button_state_fops, device); - if (!entry) { - ret = -ENODEV; - goto remove_dev_dir; - } - -done: - return ret; - -remove_dev_dir: - remove_proc_entry(acpi_device_bid(device), - acpi_lid_dir); - acpi_device_dir(device) = NULL; -remove_lid_dir: - remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); -remove_button_dir: - remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); - goto done; -} - -static int acpi_button_remove_fs(struct acpi_device *device) -{ - struct acpi_button *button = acpi_driver_data(device); - - if (button->type != ACPI_BUTTON_TYPE_LID) - return 0; - - remove_proc_entry(ACPI_BUTTON_FILE_STATE, - acpi_device_dir(device)); - remove_proc_entry(acpi_device_bid(device), - acpi_lid_dir); - acpi_device_dir(device) = NULL; - remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); - remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); - - return 0; -} - -/* -------------------------------------------------------------------------- - Driver Interface - -------------------------------------------------------------------------- */ -int acpi_lid_notifier_register(struct notifier_block *nb) -{ - return blocking_notifier_chain_register(&acpi_lid_notifier, nb); -} -EXPORT_SYMBOL(acpi_lid_notifier_register); - -int acpi_lid_notifier_unregister(struct notifier_block *nb) -{ - return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb); -} -EXPORT_SYMBOL(acpi_lid_notifier_unregister); - -int acpi_lid_open(void) -{ - acpi_status status; - unsigned long long state; - - if (!lid_device) - return -ENODEV; - - status = acpi_evaluate_integer(lid_device->handle, "_LID", NULL, - &state); - if (ACPI_FAILURE(status)) - return -ENODEV; - - return !!state; -} -EXPORT_SYMBOL(acpi_lid_open); - -static int acpi_lid_send_state(struct acpi_device *device) -{ - struct acpi_button *button = acpi_driver_data(device); - unsigned long long state; - acpi_status status; - int ret; - - status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state); - if (ACPI_FAILURE(status)) - return -ENODEV; - - /* input layer checks if event is redundant */ - input_report_switch(button->input, SW_LID, !state); - input_sync(button->input); - - if (state) - pm_wakeup_event(&device->dev, 0); - - ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); - if (ret == NOTIFY_DONE) - ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, - device); - if (ret == NOTIFY_DONE || ret == NOTIFY_OK) { - /* - * It is also regarded as success if the notifier_chain - * returns NOTIFY_OK or NOTIFY_DONE. - */ - ret = 0; - } - return ret; -} - -static void acpi_button_notify(struct acpi_device *device, u32 event) -{ - struct acpi_button *button = acpi_driver_data(device); - struct input_dev *input; - - switch (event) { - case ACPI_FIXED_HARDWARE_EVENT: - event = ACPI_BUTTON_NOTIFY_STATUS; - /* fall through */ - case ACPI_BUTTON_NOTIFY_STATUS: - input = button->input; - if (button->type == ACPI_BUTTON_TYPE_LID) { - acpi_lid_send_state(device); - } else { - int keycode; - - pm_wakeup_event(&device->dev, 0); - if (button->suspended) - break; - - keycode = test_bit(KEY_SLEEP, input->keybit) ? - KEY_SLEEP : KEY_POWER; - input_report_key(input, keycode, 1); - input_sync(input); - input_report_key(input, keycode, 0); - input_sync(input); - - acpi_bus_generate_netlink_event( - device->pnp.device_class, - dev_name(&device->dev), - event, ++button->pushed); - } - break; - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); - break; - } -} - -#ifdef CONFIG_PM_SLEEP -static int acpi_button_suspend(struct device *dev) -{ - struct acpi_device *device = to_acpi_device(dev); - struct acpi_button *button = acpi_driver_data(device); - - button->suspended = true; - return 0; -} - -static int acpi_button_resume(struct device *dev) -{ - struct acpi_device *device = to_acpi_device(dev); - struct acpi_button *button = acpi_driver_data(device); - - button->suspended = false; - if (button->type == ACPI_BUTTON_TYPE_LID) - return acpi_lid_send_state(device); - return 0; -} -#endif - -static int acpi_button_add(struct acpi_device *device) -{ - struct acpi_button *button; - struct input_dev *input; - const char *hid = acpi_device_hid(device); - char *name, *class; - int error; - - button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL); - if (!button) - return -ENOMEM; - - device->driver_data = button; - - button->input = input = input_allocate_device(); - if (!input) { - error = -ENOMEM; - goto err_free_button; - } - - name = acpi_device_name(device); - class = acpi_device_class(device); - - if (!strcmp(hid, ACPI_BUTTON_HID_POWER) || - !strcmp(hid, ACPI_BUTTON_HID_POWERF)) { - button->type = ACPI_BUTTON_TYPE_POWER; - strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER); - sprintf(class, "%s/%s", - ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); - } else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) || - !strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) { - button->type = ACPI_BUTTON_TYPE_SLEEP; - strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP); - sprintf(class, "%s/%s", - ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); - } else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) { - button->type = ACPI_BUTTON_TYPE_LID; - strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID); - sprintf(class, "%s/%s", - ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); - } else { - printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid); - error = -ENODEV; - goto err_free_input; - } - - error = acpi_button_add_fs(device); - if (error) - goto err_free_input; - - snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid); - - input->name = name; - input->phys = button->phys; - input->id.bustype = BUS_HOST; - input->id.product = button->type; - input->dev.parent = &device->dev; - - switch (button->type) { - case ACPI_BUTTON_TYPE_POWER: - input_set_capability(input, EV_KEY, KEY_POWER); - break; - - case ACPI_BUTTON_TYPE_SLEEP: - input_set_capability(input, EV_KEY, KEY_SLEEP); - break; - - case ACPI_BUTTON_TYPE_LID: - input_set_capability(input, EV_SW, SW_LID); - break; - } - - error = input_register_device(input); - if (error) - goto err_remove_fs; - if (button->type == ACPI_BUTTON_TYPE_LID) { - acpi_lid_send_state(device); - /* - * This assumes there's only one lid device, or if there are - * more we only care about the last one... - */ - lid_device = device; - } - - printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device)); - return 0; - - err_remove_fs: - acpi_button_remove_fs(device); - err_free_input: - input_free_device(input); - err_free_button: - kfree(button); - return error; -} - -static int acpi_button_remove(struct acpi_device *device) -{ - struct acpi_button *button = acpi_driver_data(device); - - acpi_button_remove_fs(device); - input_unregister_device(button->input); - kfree(button); - return 0; -} - -module_acpi_driver(acpi_button_driver); diff --git a/src/4.x/drivers/acpi/internal.h b/src/4.x/drivers/acpi/internal.h deleted file mode 100644 index 0f3f41c13..000000000 --- a/src/4.x/drivers/acpi/internal.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - * acpi/internal.h - * For use by Linux/ACPI infrastructure, not drivers - * - * Copyright (c) 2009, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - */ - -#ifndef _ACPI_INTERNAL_H_ -#define _ACPI_INTERNAL_H_ - -#define PREFIX "ACPI: " - -acpi_status acpi_os_initialize1(void); -void init_acpi_device_notify(void); -int acpi_scan_init(void); -void acpi_pci_root_init(void); -void acpi_pci_link_init(void); -void acpi_processor_init(void); -void acpi_platform_init(void); -void acpi_pnp_init(void); -void acpi_int340x_thermal_init(void); -int acpi_sysfs_init(void); -void acpi_container_init(void); -void acpi_memory_hotplug_init(void); -#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC -int acpi_ioapic_add(struct acpi_pci_root *root); -int acpi_ioapic_remove(struct acpi_pci_root *root); -#else -static inline int acpi_ioapic_add(struct acpi_pci_root *root) { return 0; } -static inline int acpi_ioapic_remove(struct acpi_pci_root *root) { return 0; } -#endif -#ifdef CONFIG_ACPI_DOCK -void register_dock_dependent_device(struct acpi_device *adev, - acpi_handle dshandle); -int dock_notify(struct acpi_device *adev, u32 event); -void acpi_dock_add(struct acpi_device *adev); -#else -static inline void register_dock_dependent_device(struct acpi_device *adev, - acpi_handle dshandle) {} -static inline int dock_notify(struct acpi_device *adev, u32 event) { return -ENODEV; } -static inline void acpi_dock_add(struct acpi_device *adev) {} -#endif -#ifdef CONFIG_X86 -void acpi_cmos_rtc_init(void); -#else -static inline void acpi_cmos_rtc_init(void) {} -#endif -int acpi_rev_override_setup(char *str); - -extern bool acpi_force_hot_remove; - -void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, - const char *name); -int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, - const char *hotplug_profile_name); -void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val); - -#ifdef CONFIG_DEBUG_FS -extern struct dentry *acpi_debugfs_dir; -void acpi_debugfs_init(void); -#else -static inline void acpi_debugfs_init(void) { return; } -#endif -void acpi_lpss_init(void); - -void acpi_apd_init(void); - -acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src); -bool acpi_queue_hotplug_work(struct work_struct *work); -void acpi_device_hotplug(struct acpi_device *adev, u32 src); -bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); - -/* -------------------------------------------------------------------------- - Device Node Initialization / Removal - -------------------------------------------------------------------------- */ -#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ - ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING) - -int acpi_device_add(struct acpi_device *device, - void (*release)(struct device *)); -void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, - int type, unsigned long long sta); -int acpi_device_setup_files(struct acpi_device *dev); -void acpi_device_remove_files(struct acpi_device *dev); -void acpi_device_add_finalize(struct acpi_device *device); -void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); -bool acpi_device_is_present(struct acpi_device *adev); -bool acpi_device_is_battery(struct acpi_device *adev); -bool acpi_device_is_first_physical_node(struct acpi_device *adev, - const struct device *dev); - -/* -------------------------------------------------------------------------- - Device Matching and Notification - -------------------------------------------------------------------------- */ -struct acpi_device *acpi_companion_match(const struct device *dev); -int __acpi_device_uevent_modalias(struct acpi_device *adev, - struct kobj_uevent_env *env); - -/* -------------------------------------------------------------------------- - Power Resource - -------------------------------------------------------------------------- */ -int acpi_power_init(void); -void acpi_power_resources_list_free(struct list_head *list); -int acpi_extract_power_resources(union acpi_object *package, unsigned int start, - struct list_head *list); -int acpi_add_power_resource(acpi_handle handle); -void acpi_power_add_remove_device(struct acpi_device *adev, bool add); -int acpi_power_wakeup_list_init(struct list_head *list, int *system_level); -int acpi_device_sleep_wake(struct acpi_device *dev, - int enable, int sleep_state, int dev_state); -int acpi_power_get_inferred_state(struct acpi_device *device, int *state); -int acpi_power_on_resources(struct acpi_device *device, int state); -int acpi_power_transition(struct acpi_device *device, int state); - -int acpi_wakeup_device_init(void); - -#ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC -void acpi_early_processor_set_pdc(void); -#else -static inline void acpi_early_processor_set_pdc(void) {} -#endif - -#ifdef CONFIG_X86 -void acpi_early_processor_osc(void); -#else -static inline void acpi_early_processor_osc(void) {} -#endif - -/* -------------------------------------------------------------------------- - Embedded Controller - -------------------------------------------------------------------------- */ -struct acpi_ec { - acpi_handle handle; - unsigned long gpe; - unsigned long command_addr; - unsigned long data_addr; - bool global_lock; - unsigned long flags; - unsigned long reference_count; - struct mutex mutex; - wait_queue_head_t wait; - struct list_head list; - struct transaction *curr; - spinlock_t lock; - struct work_struct work; - unsigned long timestamp; - unsigned long nr_pending_queries; -}; - -extern struct acpi_ec *first_ec; - -/* If we find an EC via the ECDT, we need to keep a ptr to its context */ -/* External interfaces use first EC only, so remember */ -typedef int (*acpi_ec_query_func) (void *data); - -int acpi_ec_init(void); -int acpi_ec_ecdt_probe(void); -int acpi_boot_ec_enable(void); -void acpi_ec_block_transactions(void); -void acpi_ec_unblock_transactions(void); -void acpi_ec_unblock_transactions_early(void); -int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, - acpi_handle handle, acpi_ec_query_func func, - void *data); -void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit); - - -/*-------------------------------------------------------------------------- - Suspend/Resume - -------------------------------------------------------------------------- */ -#ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT -extern int acpi_sleep_init(void); -#else -static inline int acpi_sleep_init(void) { return -ENXIO; } -#endif - -#ifdef CONFIG_ACPI_SLEEP -void acpi_sleep_proc_init(void); -int suspend_nvs_alloc(void); -void suspend_nvs_free(void); -int suspend_nvs_save(void); -void suspend_nvs_restore(void); -#else -static inline void acpi_sleep_proc_init(void) {} -static inline int suspend_nvs_alloc(void) { return 0; } -static inline void suspend_nvs_free(void) {} -static inline int suspend_nvs_save(void) { return 0; } -static inline void suspend_nvs_restore(void) {} -#endif - -/*-------------------------------------------------------------------------- - Device properties - -------------------------------------------------------------------------- */ -#define ACPI_DT_NAMESPACE_HID "PRP0001" - -void acpi_init_properties(struct acpi_device *adev); -void acpi_free_properties(struct acpi_device *adev); - -#endif /* _ACPI_INTERNAL_H_ */ diff --git a/src/4.x/drivers/acpi/processor_driver.c b/src/4.x/drivers/acpi/processor_driver.c deleted file mode 100644 index c9bf74982..000000000 --- a/src/4.x/drivers/acpi/processor_driver.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * processor_driver.c - ACPI Processor Driver - * - * Copyright (C) 2001, 2002 Andy Grover - * Copyright (C) 2001, 2002 Paul Diefenbaugh - * Copyright (C) 2004 Dominik Brodowski - * Copyright (C) 2004 Anil S Keshavamurthy - * - Added processor hotplug support - * Copyright (C) 2013, Intel Corporation - * Rafael J. Wysocki - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "internal.h" - -#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 -#define ACPI_PROCESSOR_NOTIFY_POWER 0x81 -#define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82 - -#define _COMPONENT ACPI_PROCESSOR_COMPONENT -ACPI_MODULE_NAME("processor_driver"); - -MODULE_AUTHOR("Paul Diefenbaugh"); -MODULE_DESCRIPTION("ACPI Processor Driver"); -MODULE_LICENSE("GPL"); - -static int acpi_processor_start(struct device *dev); -static int acpi_processor_stop(struct device *dev); - -static const struct acpi_device_id processor_device_ids[] = { - {ACPI_PROCESSOR_OBJECT_HID, 0}, - {ACPI_PROCESSOR_DEVICE_HID, 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, processor_device_ids); - -static struct device_driver acpi_processor_driver = { - .name = "processor", - .bus = &cpu_subsys, - .acpi_match_table = processor_device_ids, - .probe = acpi_processor_start, - .remove = acpi_processor_stop, -}; - -static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) -{ - struct acpi_device *device = data; - struct acpi_processor *pr; - int saved; - - if (device->handle != handle) - return; - - pr = acpi_driver_data(device); - if (!pr) - return; - - switch (event) { - case ACPI_PROCESSOR_NOTIFY_PERFORMANCE: - saved = pr->performance_platform_limit; - acpi_processor_ppc_has_changed(pr, 1); - if (saved == pr->performance_platform_limit) - break; - acpi_bus_generate_netlink_event(device->pnp.device_class, - dev_name(&device->dev), event, - pr->performance_platform_limit); - break; - case ACPI_PROCESSOR_NOTIFY_POWER: - acpi_processor_cst_has_changed(pr); - acpi_bus_generate_netlink_event(device->pnp.device_class, - dev_name(&device->dev), event, 0); - break; - case ACPI_PROCESSOR_NOTIFY_THROTTLING: - acpi_processor_tstate_has_changed(pr); - acpi_bus_generate_netlink_event(device->pnp.device_class, - dev_name(&device->dev), event, 0); - break; - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); - break; - } - - return; -} - -static int __acpi_processor_start(struct acpi_device *device); - -static int acpi_cpu_soft_notify(struct notifier_block *nfb, - unsigned long action, void *hcpu) -{ - unsigned int cpu = (unsigned long)hcpu; - struct acpi_processor *pr = per_cpu(processors, cpu); - struct acpi_device *device; - action &= ~CPU_TASKS_FROZEN; - - /* - * CPU_STARTING and CPU_DYING must not sleep. Return here since - * acpi_bus_get_device() may sleep. - */ - if (action == CPU_STARTING || action == CPU_DYING) - return NOTIFY_DONE; - - if (!pr || acpi_bus_get_device(pr->handle, &device)) - return NOTIFY_DONE; - - if (action == CPU_ONLINE) { - /* - * CPU got physically hotplugged and onlined for the first time: - * Initialize missing things. - */ - if (pr->flags.need_hotplug_init) { - int ret; - - pr_info("Will online and init hotplugged CPU: %d\n", - pr->id); - pr->flags.need_hotplug_init = 0; - ret = __acpi_processor_start(device); - WARN(ret, "Failed to start CPU: %d\n", pr->id); - } else { - /* Normal CPU soft online event. */ - acpi_processor_ppc_has_changed(pr, 0); - acpi_processor_hotplug(pr); - acpi_processor_reevaluate_tstate(pr, action); - acpi_processor_tstate_has_changed(pr); - } - } else if (action == CPU_DEAD) { - /* Invalidate flag.throttling after the CPU is offline. */ - acpi_processor_reevaluate_tstate(pr, action); - } - return NOTIFY_OK; -} - -static struct notifier_block acpi_cpu_notifier = { - .notifier_call = acpi_cpu_soft_notify, -}; - -#ifdef CONFIG_ACPI_CPU_FREQ_PSS -static int acpi_pss_perf_init(struct acpi_processor *pr, - struct acpi_device *device) -{ - int result = 0; - - acpi_processor_ppc_has_changed(pr, 0); - - acpi_processor_get_throttling_info(pr); - - if (pr->flags.throttling) - pr->flags.limit = 1; - - pr->cdev = thermal_cooling_device_register("Processor", device, - &processor_cooling_ops); - if (IS_ERR(pr->cdev)) { - result = PTR_ERR(pr->cdev); - return result; - } - - dev_dbg(&device->dev, "registered as cooling_device%d\n", - pr->cdev->id); - - result = sysfs_create_link(&device->dev.kobj, - &pr->cdev->device.kobj, - "thermal_cooling"); - if (result) { - dev_err(&device->dev, - "Failed to create sysfs link 'thermal_cooling'\n"); - goto err_thermal_unregister; - } - - result = sysfs_create_link(&pr->cdev->device.kobj, - &device->dev.kobj, - "device"); - if (result) { - dev_err(&pr->cdev->device, - "Failed to create sysfs link 'device'\n"); - goto err_remove_sysfs_thermal; - } - - return 0; - - err_remove_sysfs_thermal: - sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); - err_thermal_unregister: - thermal_cooling_device_unregister(pr->cdev); - - return result; -} - -static void acpi_pss_perf_exit(struct acpi_processor *pr, - struct acpi_device *device) -{ - if (pr->cdev) { - sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); - sysfs_remove_link(&pr->cdev->device.kobj, "device"); - thermal_cooling_device_unregister(pr->cdev); - pr->cdev = NULL; - } -} -#else -static inline int acpi_pss_perf_init(struct acpi_processor *pr, - struct acpi_device *device) -{ - return 0; -} - -static inline void acpi_pss_perf_exit(struct acpi_processor *pr, - struct acpi_device *device) {} -#endif /* CONFIG_ACPI_CPU_FREQ_PSS */ - -static int __acpi_processor_start(struct acpi_device *device) -{ - struct acpi_processor *pr = acpi_driver_data(device); - acpi_status status; - int result = 0; - - if (!pr) - return -ENODEV; - - if (pr->flags.need_hotplug_init) - return 0; - - result = acpi_cppc_processor_probe(pr); - if (result) - return -ENODEV; - - if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) - acpi_processor_power_init(pr); - - result = acpi_pss_perf_init(pr, device); - if (result) - goto err_power_exit; - - status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, - acpi_processor_notify, device); - if (ACPI_SUCCESS(status)) - return 0; - - result = -ENODEV; - acpi_pss_perf_exit(pr, device); - -err_power_exit: - acpi_processor_power_exit(pr); - return result; -} - -static int acpi_processor_start(struct device *dev) -{ - struct acpi_device *device = ACPI_COMPANION(dev); - int ret; - - if (!device) - return -ENODEV; - - /* Protect against concurrent CPU hotplug operations */ - get_online_cpus(); - ret = __acpi_processor_start(device); - put_online_cpus(); - return ret; -} - -static int acpi_processor_stop(struct device *dev) -{ - struct acpi_device *device = ACPI_COMPANION(dev); - struct acpi_processor *pr; - - if (!device) - return 0; - - acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, - acpi_processor_notify); - - pr = acpi_driver_data(device); - if (!pr) - return 0; - acpi_processor_power_exit(pr); - - acpi_pss_perf_exit(pr, device); - - acpi_cppc_processor_exit(pr); - - return 0; -} - -/* - * We keep the driver loaded even when ACPI is not running. - * This is needed for the powernow-k8 driver, that works even without - * ACPI, but needs symbols from this driver - */ - -static int __init acpi_processor_driver_init(void) -{ - int result = 0; - - if (acpi_disabled) - return 0; - - result = driver_register(&acpi_processor_driver); - if (result < 0) - return result; - - acpi_processor_syscore_init(); - register_hotcpu_notifier(&acpi_cpu_notifier); - acpi_thermal_cpufreq_init(); - acpi_processor_ppc_init(); - acpi_processor_throttling_init(); - return 0; -} - -static void __exit acpi_processor_driver_exit(void) -{ - if (acpi_disabled) - return; - - acpi_processor_ppc_exit(); - acpi_thermal_cpufreq_exit(); - unregister_hotcpu_notifier(&acpi_cpu_notifier); - acpi_processor_syscore_exit(); - driver_unregister(&acpi_processor_driver); -} - -module_init(acpi_processor_driver_init); -module_exit(acpi_processor_driver_exit); - -MODULE_ALIAS("processor"); diff --git a/src/4.x/drivers/acpi/processor_idle.c b/src/4.x/drivers/acpi/processor_idle.c deleted file mode 100644 index 175c86bee..000000000 --- a/src/4.x/drivers/acpi/processor_idle.c +++ /dev/null @@ -1,1141 +0,0 @@ -/* - * processor_idle - idle state submodule to the ACPI processor driver - * - * Copyright (C) 2001, 2002 Andy Grover - * Copyright (C) 2001, 2002 Paul Diefenbaugh - * Copyright (C) 2004, 2005 Dominik Brodowski - * Copyright (C) 2004 Anil S Keshavamurthy - * - Added processor hotplug support - * Copyright (C) 2005 Venkatesh Pallipadi - * - Added support for C3 on SMP - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#include -#include -#include -#include /* need_resched() */ -#include -#include -#include -#include - -/* - * Include the apic definitions for x86 to have the APIC timer related defines - * available also for UP (on SMP it gets magically included via linux/smp.h). - * asm/acpi.h is not an option, as it would require more include magic. Also - * creating an empty asm-ia64/apic.h would just trade pest vs. cholera. - */ -#ifdef CONFIG_X86 -#include -#endif - -#define PREFIX "ACPI: " - -#define ACPI_PROCESSOR_CLASS "processor" -#define _COMPONENT ACPI_PROCESSOR_COMPONENT -ACPI_MODULE_NAME("processor_idle"); - -static unsigned int max_cstate __read_mostly = ACPI_PROCESSOR_MAX_POWER; -module_param(max_cstate, uint, 0000); -static unsigned int nocst __read_mostly; -module_param(nocst, uint, 0000); -static int bm_check_disable __read_mostly; -module_param(bm_check_disable, uint, 0000); - -static unsigned int latency_factor __read_mostly = 2; -module_param(latency_factor, uint, 0644); - -static DEFINE_PER_CPU(struct cpuidle_device *, acpi_cpuidle_device); - -static DEFINE_PER_CPU(struct acpi_processor_cx * [CPUIDLE_STATE_MAX], - acpi_cstate); - -static int disabled_by_idle_boot_param(void) -{ - return boot_option_idle_override == IDLE_POLL || - boot_option_idle_override == IDLE_HALT; -} - -/* - * IBM ThinkPad R40e crashes mysteriously when going into C2 or C3. - * For now disable this. Probably a bug somewhere else. - * - * To skip this limit, boot/load with a large max_cstate limit. - */ -static int set_max_cstate(const struct dmi_system_id *id) -{ - if (max_cstate > ACPI_PROCESSOR_MAX_POWER) - return 0; - - printk(KERN_NOTICE PREFIX "%s detected - limiting to C%ld max_cstate." - " Override with \"processor.max_cstate=%d\"\n", id->ident, - (long)id->driver_data, ACPI_PROCESSOR_MAX_POWER + 1); - - max_cstate = (long)id->driver_data; - - return 0; -} - -static const struct dmi_system_id processor_power_dmi_table[] = { - { set_max_cstate, "Clevo 5600D", { - DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"), - DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307")}, - (void *)2}, - { set_max_cstate, "Pavilion zv5000", { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME,"Pavilion zv5000 (DS502A#ABA)")}, - (void *)1}, - { set_max_cstate, "Asus L8400B", { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME,"L8400B series Notebook PC")}, - (void *)1}, - {}, -}; - - -/* - * Callers should disable interrupts before the call and enable - * interrupts after return. - */ -static void acpi_safe_halt(void) -{ - if (!tif_need_resched()) { - safe_halt(); - local_irq_disable(); - } -} - -#ifdef ARCH_APICTIMER_STOPS_ON_C3 - -/* - * Some BIOS implementations switch to C3 in the published C2 state. - * This seems to be a common problem on AMD boxen, but other vendors - * are affected too. We pick the most conservative approach: we assume - * that the local APIC stops in both C2 and C3. - */ -static void lapic_timer_check_state(int state, struct acpi_processor *pr, - struct acpi_processor_cx *cx) -{ - struct acpi_processor_power *pwr = &pr->power; - u8 type = local_apic_timer_c2_ok ? ACPI_STATE_C3 : ACPI_STATE_C2; - - if (cpu_has(&cpu_data(pr->id), X86_FEATURE_ARAT)) - return; - - if (amd_e400_c1e_detected) - type = ACPI_STATE_C1; - - /* - * Check, if one of the previous states already marked the lapic - * unstable - */ - if (pwr->timer_broadcast_on_state < state) - return; - - if (cx->type >= type) - pr->power.timer_broadcast_on_state = state; -} - -static void __lapic_timer_propagate_broadcast(void *arg) -{ - struct acpi_processor *pr = (struct acpi_processor *) arg; - - if (pr->power.timer_broadcast_on_state < INT_MAX) - tick_broadcast_enable(); - else - tick_broadcast_disable(); -} - -static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) -{ - smp_call_function_single(pr->id, __lapic_timer_propagate_broadcast, - (void *)pr, 1); -} - -/* Power(C) State timer broadcast control */ -static void lapic_timer_state_broadcast(struct acpi_processor *pr, - struct acpi_processor_cx *cx, - int broadcast) -{ - int state = cx - pr->power.states; - - if (state >= pr->power.timer_broadcast_on_state) { - if (broadcast) - tick_broadcast_enter(); - else - tick_broadcast_exit(); - } -} - -#else - -static void lapic_timer_check_state(int state, struct acpi_processor *pr, - struct acpi_processor_cx *cstate) { } -static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) { } -static void lapic_timer_state_broadcast(struct acpi_processor *pr, - struct acpi_processor_cx *cx, - int broadcast) -{ -} - -#endif - -#ifdef CONFIG_PM_SLEEP -static u32 saved_bm_rld; - -static int acpi_processor_suspend(void) -{ - acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_RLD, &saved_bm_rld); - return 0; -} - -static void acpi_processor_resume(void) -{ - u32 resumed_bm_rld = 0; - - acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_RLD, &resumed_bm_rld); - if (resumed_bm_rld == saved_bm_rld) - return; - - acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, saved_bm_rld); -} - -static struct syscore_ops acpi_processor_syscore_ops = { - .suspend = acpi_processor_suspend, - .resume = acpi_processor_resume, -}; - -void acpi_processor_syscore_init(void) -{ - register_syscore_ops(&acpi_processor_syscore_ops); -} - -void acpi_processor_syscore_exit(void) -{ - unregister_syscore_ops(&acpi_processor_syscore_ops); -} -#endif /* CONFIG_PM_SLEEP */ - -#if defined(CONFIG_X86) -static void tsc_check_state(int state) -{ - switch (boot_cpu_data.x86_vendor) { - case X86_VENDOR_AMD: - case X86_VENDOR_INTEL: - /* - * AMD Fam10h TSC will tick in all - * C/P/S0/S1 states when this bit is set. - */ - if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) - return; - - /*FALL THROUGH*/ - default: - /* TSC could halt in idle, so notify users */ - if (state > ACPI_STATE_C1) - mark_tsc_unstable("TSC halts in idle"); - } -} -#else -static void tsc_check_state(int state) { return; } -#endif - -static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr) -{ - - if (!pr->pblk) - return -ENODEV; - - /* if info is obtained from pblk/fadt, type equals state */ - pr->power.states[ACPI_STATE_C2].type = ACPI_STATE_C2; - pr->power.states[ACPI_STATE_C3].type = ACPI_STATE_C3; - -#ifndef CONFIG_HOTPLUG_CPU - /* - * Check for P_LVL2_UP flag before entering C2 and above on - * an SMP system. - */ - if ((num_online_cpus() > 1) && - !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) - return -ENODEV; -#endif - - /* determine C2 and C3 address from pblk */ - pr->power.states[ACPI_STATE_C2].address = pr->pblk + 4; - pr->power.states[ACPI_STATE_C3].address = pr->pblk + 5; - - /* determine latencies from FADT */ - pr->power.states[ACPI_STATE_C2].latency = acpi_gbl_FADT.c2_latency; - pr->power.states[ACPI_STATE_C3].latency = acpi_gbl_FADT.c3_latency; - - /* - * FADT specified C2 latency must be less than or equal to - * 100 microseconds. - */ - if (acpi_gbl_FADT.c2_latency > ACPI_PROCESSOR_MAX_C2_LATENCY) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "C2 latency too large [%d]\n", acpi_gbl_FADT.c2_latency)); - /* invalidate C2 */ - pr->power.states[ACPI_STATE_C2].address = 0; - } - - /* - * FADT supplied C3 latency must be less than or equal to - * 1000 microseconds. - */ - if (acpi_gbl_FADT.c3_latency > ACPI_PROCESSOR_MAX_C3_LATENCY) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "C3 latency too large [%d]\n", acpi_gbl_FADT.c3_latency)); - /* invalidate C3 */ - pr->power.states[ACPI_STATE_C3].address = 0; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "lvl2[0x%08x] lvl3[0x%08x]\n", - pr->power.states[ACPI_STATE_C2].address, - pr->power.states[ACPI_STATE_C3].address)); - - return 0; -} - -static int acpi_processor_get_power_info_default(struct acpi_processor *pr) -{ - if (!pr->power.states[ACPI_STATE_C1].valid) { - /* set the first C-State to C1 */ - /* all processors need to support C1 */ - pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1; - pr->power.states[ACPI_STATE_C1].valid = 1; - pr->power.states[ACPI_STATE_C1].entry_method = ACPI_CSTATE_HALT; - } - /* the C0 state only exists as a filler in our array */ - pr->power.states[ACPI_STATE_C0].valid = 1; - return 0; -} - -static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) -{ - acpi_status status; - u64 count; - int current_count; - int i, ret = 0; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *cst; - - - if (nocst) - return -ENODEV; - - current_count = 0; - - status = acpi_evaluate_object(pr->handle, "_CST", NULL, &buffer); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No _CST, giving up\n")); - return -ENODEV; - } - - cst = buffer.pointer; - - /* There must be at least 2 elements */ - if (!cst || (cst->type != ACPI_TYPE_PACKAGE) || cst->package.count < 2) { - printk(KERN_ERR PREFIX "not enough elements in _CST\n"); - ret = -EFAULT; - goto end; - } - - count = cst->package.elements[0].integer.value; - - /* Validate number of power states. */ - if (count < 1 || count != cst->package.count - 1) { - printk(KERN_ERR PREFIX "count given by _CST is not valid\n"); - ret = -EFAULT; - goto end; - } - - /* Tell driver that at least _CST is supported. */ - pr->flags.has_cst = 1; - - for (i = 1; i <= count; i++) { - union acpi_object *element; - union acpi_object *obj; - struct acpi_power_register *reg; - struct acpi_processor_cx cx; - - memset(&cx, 0, sizeof(cx)); - - element = &(cst->package.elements[i]); - if (element->type != ACPI_TYPE_PACKAGE) - continue; - - if (element->package.count != 4) - continue; - - obj = &(element->package.elements[0]); - - if (obj->type != ACPI_TYPE_BUFFER) - continue; - - reg = (struct acpi_power_register *)obj->buffer.pointer; - - if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO && - (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) - continue; - - /* There should be an easy way to extract an integer... */ - obj = &(element->package.elements[1]); - if (obj->type != ACPI_TYPE_INTEGER) - continue; - - cx.type = obj->integer.value; - /* - * Some buggy BIOSes won't list C1 in _CST - - * Let acpi_processor_get_power_info_default() handle them later - */ - if (i == 1 && cx.type != ACPI_STATE_C1) - current_count++; - - cx.address = reg->address; - cx.index = current_count + 1; - - cx.entry_method = ACPI_CSTATE_SYSTEMIO; - if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { - if (acpi_processor_ffh_cstate_probe - (pr->id, &cx, reg) == 0) { - cx.entry_method = ACPI_CSTATE_FFH; - } else if (cx.type == ACPI_STATE_C1) { - /* - * C1 is a special case where FIXED_HARDWARE - * can be handled in non-MWAIT way as well. - * In that case, save this _CST entry info. - * Otherwise, ignore this info and continue. - */ - cx.entry_method = ACPI_CSTATE_HALT; - snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI HLT"); - } else { - continue; - } - if (cx.type == ACPI_STATE_C1 && - (boot_option_idle_override == IDLE_NOMWAIT)) { - /* - * In most cases the C1 space_id obtained from - * _CST object is FIXED_HARDWARE access mode. - * But when the option of idle=halt is added, - * the entry_method type should be changed from - * CSTATE_FFH to CSTATE_HALT. - * When the option of idle=nomwait is added, - * the C1 entry_method type should be - * CSTATE_HALT. - */ - cx.entry_method = ACPI_CSTATE_HALT; - snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI HLT"); - } - } else { - snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI IOPORT 0x%x", - cx.address); - } - - if (cx.type == ACPI_STATE_C1) { - cx.valid = 1; - } - - obj = &(element->package.elements[2]); - if (obj->type != ACPI_TYPE_INTEGER) - continue; - - cx.latency = obj->integer.value; - - obj = &(element->package.elements[3]); - if (obj->type != ACPI_TYPE_INTEGER) - continue; - - current_count++; - memcpy(&(pr->power.states[current_count]), &cx, sizeof(cx)); - - /* - * We support total ACPI_PROCESSOR_MAX_POWER - 1 - * (From 1 through ACPI_PROCESSOR_MAX_POWER - 1) - */ - if (current_count >= (ACPI_PROCESSOR_MAX_POWER - 1)) { - printk(KERN_WARNING - "Limiting number of power states to max (%d)\n", - ACPI_PROCESSOR_MAX_POWER); - printk(KERN_WARNING - "Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n"); - break; - } - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d power states\n", - current_count)); - - /* Validate number of power states discovered */ - if (current_count < 2) - ret = -EFAULT; - - end: - kfree(buffer.pointer); - - return ret; -} - -static void acpi_processor_power_verify_c3(struct acpi_processor *pr, - struct acpi_processor_cx *cx) -{ - static int bm_check_flag = -1; - static int bm_control_flag = -1; - - - if (!cx->address) - return; - - /* - * PIIX4 Erratum #18: We don't support C3 when Type-F (fast) - * DMA transfers are used by any ISA device to avoid livelock. - * Note that we could disable Type-F DMA (as recommended by - * the erratum), but this is known to disrupt certain ISA - * devices thus we take the conservative approach. - */ - else if (errata.piix4.fdma) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "C3 not supported on PIIX4 with Type-F DMA\n")); - return; - } - - /* All the logic here assumes flags.bm_check is same across all CPUs */ - if (bm_check_flag == -1) { - /* Determine whether bm_check is needed based on CPU */ - acpi_processor_power_init_bm_check(&(pr->flags), pr->id); - bm_check_flag = pr->flags.bm_check; - bm_control_flag = pr->flags.bm_control; - } else { - pr->flags.bm_check = bm_check_flag; - pr->flags.bm_control = bm_control_flag; - } - - if (pr->flags.bm_check) { - if (!pr->flags.bm_control) { - if (pr->flags.has_cst != 1) { - /* bus mastering control is necessary */ - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "C3 support requires BM control\n")); - return; - } else { - /* Here we enter C3 without bus mastering */ - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "C3 support without BM control\n")); - } - } - } else { - /* - * WBINVD should be set in fadt, for C3 state to be - * supported on when bm_check is not required. - */ - if (!(acpi_gbl_FADT.flags & ACPI_FADT_WBINVD)) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Cache invalidation should work properly" - " for C3 to be enabled on SMP systems\n")); - return; - } - } - - /* - * Otherwise we've met all of our C3 requirements. - * Normalize the C3 latency to expidite policy. Enable - * checking of bus mastering status (bm_check) so we can - * use this in our C3 policy - */ - cx->valid = 1; - - /* - * On older chipsets, BM_RLD needs to be set - * in order for Bus Master activity to wake the - * system from C3. Newer chipsets handle DMA - * during C3 automatically and BM_RLD is a NOP. - * In either case, the proper way to - * handle BM_RLD is to set it and leave it set. - */ - acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 1); - - return; -} - -static int acpi_processor_power_verify(struct acpi_processor *pr) -{ - unsigned int i; - unsigned int working = 0; - - pr->power.timer_broadcast_on_state = INT_MAX; - - for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { - struct acpi_processor_cx *cx = &pr->power.states[i]; - - switch (cx->type) { - case ACPI_STATE_C1: - cx->valid = 1; - break; - - case ACPI_STATE_C2: - if (!cx->address) - break; - cx->valid = 1; - break; - - case ACPI_STATE_C3: - acpi_processor_power_verify_c3(pr, cx); - break; - } - if (!cx->valid) - continue; - - lapic_timer_check_state(i, pr, cx); - tsc_check_state(cx->type); - working++; - } - - lapic_timer_propagate_broadcast(pr); - - return (working); -} - -static int acpi_processor_get_power_info(struct acpi_processor *pr) -{ - unsigned int i; - int result; - - - /* NOTE: the idle thread may not be running while calling - * this function */ - - /* Zero initialize all the C-states info. */ - memset(pr->power.states, 0, sizeof(pr->power.states)); - - result = acpi_processor_get_power_info_cst(pr); - if (result == -ENODEV) - result = acpi_processor_get_power_info_fadt(pr); - - if (result) - return result; - - acpi_processor_get_power_info_default(pr); - - pr->power.count = acpi_processor_power_verify(pr); - - /* - * if one state of type C2 or C3 is available, mark this - * CPU as being "idle manageable" - */ - for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) { - if (pr->power.states[i].valid) { - pr->power.count = i; - if (pr->power.states[i].type >= ACPI_STATE_C2) - pr->flags.power = 1; - } - } - - return 0; -} - -/** - * acpi_idle_bm_check - checks if bus master activity was detected - */ -static int acpi_idle_bm_check(void) -{ - u32 bm_status = 0; - - if (bm_check_disable) - return 0; - - acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, &bm_status); - if (bm_status) - acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, 1); - /* - * PIIX4 Erratum #18: Note that BM_STS doesn't always reflect - * the true state of bus mastering activity; forcing us to - * manually check the BMIDEA bit of each IDE channel. - */ - else if (errata.piix4.bmisx) { - if ((inb_p(errata.piix4.bmisx + 0x02) & 0x01) - || (inb_p(errata.piix4.bmisx + 0x0A) & 0x01)) - bm_status = 1; - } - return bm_status; -} - -/** - * acpi_idle_do_entry - enter idle state using the appropriate method - * @cx: cstate data - * - * Caller disables interrupt before call and enables interrupt after return. - */ -static void acpi_idle_do_entry(struct acpi_processor_cx *cx) -{ - if (cx->entry_method == ACPI_CSTATE_FFH) { - /* Call into architectural FFH based C-state */ - acpi_processor_ffh_cstate_enter(cx); - } else if (cx->entry_method == ACPI_CSTATE_HALT) { - acpi_safe_halt(); - } else { - /* IO port based C-state */ - inb(cx->address); - /* Dummy wait op - must do something useless after P_LVL2 read - because chipsets cannot guarantee that STPCLK# signal - gets asserted in time to freeze execution properly. */ - inl(acpi_gbl_FADT.xpm_timer_block.address); - } -} - -/** - * acpi_idle_play_dead - enters an ACPI state for long-term idle (i.e. off-lining) - * @dev: the target CPU - * @index: the index of suggested state - */ -static int acpi_idle_play_dead(struct cpuidle_device *dev, int index) -{ - struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); - - ACPI_FLUSH_CPU_CACHE(); - - while (1) { - - if (cx->entry_method == ACPI_CSTATE_HALT) - safe_halt(); - else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) { - inb(cx->address); - /* See comment in acpi_idle_do_entry() */ - inl(acpi_gbl_FADT.xpm_timer_block.address); - } else - return -ENODEV; - } - - /* Never reached */ - return 0; -} - -static bool acpi_idle_fallback_to_c1(struct acpi_processor *pr) -{ - return IS_ENABLED(CONFIG_HOTPLUG_CPU) && !pr->flags.has_cst && - !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED); -} - -static int c3_cpu_count; -static DEFINE_RAW_SPINLOCK(c3_lock); - -/** - * acpi_idle_enter_bm - enters C3 with proper BM handling - * @pr: Target processor - * @cx: Target state context - * @timer_bc: Whether or not to change timer mode to broadcast - */ -static void acpi_idle_enter_bm(struct acpi_processor *pr, - struct acpi_processor_cx *cx, bool timer_bc) -{ - acpi_unlazy_tlb(smp_processor_id()); - - /* - * Must be done before busmaster disable as we might need to - * access HPET ! - */ - if (timer_bc) - lapic_timer_state_broadcast(pr, cx, 1); - - /* - * disable bus master - * bm_check implies we need ARB_DIS - * bm_control implies whether we can do ARB_DIS - * - * That leaves a case where bm_check is set and bm_control is - * not set. In that case we cannot do much, we enter C3 - * without doing anything. - */ - if (pr->flags.bm_control) { - raw_spin_lock(&c3_lock); - c3_cpu_count++; - /* Disable bus master arbitration when all CPUs are in C3 */ - if (c3_cpu_count == num_online_cpus()) - acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1); - raw_spin_unlock(&c3_lock); - } - - acpi_idle_do_entry(cx); - - /* Re-enable bus master arbitration */ - if (pr->flags.bm_control) { - raw_spin_lock(&c3_lock); - acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0); - c3_cpu_count--; - raw_spin_unlock(&c3_lock); - } - - if (timer_bc) - lapic_timer_state_broadcast(pr, cx, 0); -} - -static int acpi_idle_enter(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index) -{ - struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); - struct acpi_processor *pr; - - pr = __this_cpu_read(processors); - if (unlikely(!pr)) - return -EINVAL; - - if (cx->type != ACPI_STATE_C1) { - if (acpi_idle_fallback_to_c1(pr) && num_online_cpus() > 1) { - index = CPUIDLE_DRIVER_STATE_START; - cx = per_cpu(acpi_cstate[index], dev->cpu); - } else if (cx->type == ACPI_STATE_C3 && pr->flags.bm_check) { - if (cx->bm_sts_skip || !acpi_idle_bm_check()) { - acpi_idle_enter_bm(pr, cx, true); - return index; - } else if (drv->safe_state_index >= 0) { - index = drv->safe_state_index; - cx = per_cpu(acpi_cstate[index], dev->cpu); - } else { - acpi_safe_halt(); - return -EBUSY; - } - } - } - - lapic_timer_state_broadcast(pr, cx, 1); - - if (cx->type == ACPI_STATE_C3) - ACPI_FLUSH_CPU_CACHE(); - - acpi_idle_do_entry(cx); - - lapic_timer_state_broadcast(pr, cx, 0); - - return index; -} - -static void acpi_idle_enter_freeze(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index) -{ - struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); - - if (cx->type == ACPI_STATE_C3) { - struct acpi_processor *pr = __this_cpu_read(processors); - - if (unlikely(!pr)) - return; - - if (pr->flags.bm_check) { - acpi_idle_enter_bm(pr, cx, false); - return; - } else { - ACPI_FLUSH_CPU_CACHE(); - } - } - acpi_idle_do_entry(cx); -} - -struct cpuidle_driver acpi_idle_driver = { - .name = "acpi_idle", - .owner = THIS_MODULE, -}; - -/** - * acpi_processor_setup_cpuidle_cx - prepares and configures CPUIDLE - * device i.e. per-cpu data - * - * @pr: the ACPI processor - * @dev : the cpuidle device - */ -static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, - struct cpuidle_device *dev) -{ - int i, count = CPUIDLE_DRIVER_STATE_START; - struct acpi_processor_cx *cx; - - if (!pr->flags.power_setup_done) - return -EINVAL; - - if (pr->flags.power == 0) { - return -EINVAL; - } - - if (!dev) - return -EINVAL; - - dev->cpu = pr->id; - - if (max_cstate == 0) - max_cstate = 1; - - for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { - cx = &pr->power.states[i]; - - if (!cx->valid) - continue; - - per_cpu(acpi_cstate[count], dev->cpu) = cx; - - count++; - if (count == CPUIDLE_STATE_MAX) - break; - } - - if (!count) - return -EINVAL; - - return 0; -} - -/** - * acpi_processor_setup_cpuidle states- prepares and configures cpuidle - * global state data i.e. idle routines - * - * @pr: the ACPI processor - */ -static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) -{ - int i, count = CPUIDLE_DRIVER_STATE_START; - struct acpi_processor_cx *cx; - struct cpuidle_state *state; - struct cpuidle_driver *drv = &acpi_idle_driver; - - if (!pr->flags.power_setup_done) - return -EINVAL; - - if (pr->flags.power == 0) - return -EINVAL; - - drv->safe_state_index = -1; - for (i = CPUIDLE_DRIVER_STATE_START; i < CPUIDLE_STATE_MAX; i++) { - drv->states[i].name[0] = '\0'; - drv->states[i].desc[0] = '\0'; - } - - if (max_cstate == 0) - max_cstate = 1; - - for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { - cx = &pr->power.states[i]; - - if (!cx->valid) - continue; - - state = &drv->states[count]; - snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i); - strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); - state->exit_latency = cx->latency; - state->target_residency = cx->latency * latency_factor; - state->enter = acpi_idle_enter; - - state->flags = 0; - if (cx->type == ACPI_STATE_C1 || cx->type == ACPI_STATE_C2) { - state->enter_dead = acpi_idle_play_dead; - drv->safe_state_index = count; - } - /* - * Halt-induced C1 is not good for ->enter_freeze, because it - * re-enables interrupts on exit. Moreover, C1 is generally not - * particularly interesting from the suspend-to-idle angle, so - * avoid C1 and the situations in which we may need to fall back - * to it altogether. - */ - if (cx->type != ACPI_STATE_C1 && !acpi_idle_fallback_to_c1(pr)) - state->enter_freeze = acpi_idle_enter_freeze; - - count++; - if (count == CPUIDLE_STATE_MAX) - break; - } - - drv->state_count = count; - - if (!count) - return -EINVAL; - - return 0; -} - -int acpi_processor_hotplug(struct acpi_processor *pr) -{ - int ret = 0; - struct cpuidle_device *dev; - - if (disabled_by_idle_boot_param()) - return 0; - - if (nocst) - return -ENODEV; - - if (!pr->flags.power_setup_done) - return -ENODEV; - - dev = per_cpu(acpi_cpuidle_device, pr->id); - cpuidle_pause_and_lock(); - cpuidle_disable_device(dev); - acpi_processor_get_power_info(pr); - if (pr->flags.power) { - acpi_processor_setup_cpuidle_cx(pr, dev); - ret = cpuidle_enable_device(dev); - } - cpuidle_resume_and_unlock(); - - return ret; -} - -int acpi_processor_cst_has_changed(struct acpi_processor *pr) -{ - int cpu; - struct acpi_processor *_pr; - struct cpuidle_device *dev; - - if (disabled_by_idle_boot_param()) - return 0; - - if (nocst) - return -ENODEV; - - if (!pr->flags.power_setup_done) - return -ENODEV; - - /* - * FIXME: Design the ACPI notification to make it once per - * system instead of once per-cpu. This condition is a hack - * to make the code that updates C-States be called once. - */ - - if (pr->id == 0 && cpuidle_get_driver() == &acpi_idle_driver) { - - /* Protect against cpu-hotplug */ - get_online_cpus(); - cpuidle_pause_and_lock(); - - /* Disable all cpuidle devices */ - for_each_online_cpu(cpu) { - _pr = per_cpu(processors, cpu); - if (!_pr || !_pr->flags.power_setup_done) - continue; - dev = per_cpu(acpi_cpuidle_device, cpu); - cpuidle_disable_device(dev); - } - - /* Populate Updated C-state information */ - acpi_processor_get_power_info(pr); - acpi_processor_setup_cpuidle_states(pr); - - /* Enable all cpuidle devices */ - for_each_online_cpu(cpu) { - _pr = per_cpu(processors, cpu); - if (!_pr || !_pr->flags.power_setup_done) - continue; - acpi_processor_get_power_info(_pr); - if (_pr->flags.power) { - dev = per_cpu(acpi_cpuidle_device, cpu); - acpi_processor_setup_cpuidle_cx(_pr, dev); - cpuidle_enable_device(dev); - } - } - cpuidle_resume_and_unlock(); - put_online_cpus(); - } - - return 0; -} - -static int acpi_processor_registered; - -int acpi_processor_power_init(struct acpi_processor *pr) -{ - acpi_status status; - int retval; - struct cpuidle_device *dev; - static int first_run; - - if (disabled_by_idle_boot_param()) - return 0; - - if (!first_run) { - dmi_check_system(processor_power_dmi_table); - max_cstate = acpi_processor_cstate_check(max_cstate); - if (max_cstate < ACPI_C_STATES_MAX) - printk(KERN_NOTICE - "ACPI: processor limited to max C-state %d\n", - max_cstate); - first_run++; - } - - if (acpi_gbl_FADT.cst_control && !nocst) { - status = - acpi_os_write_port(acpi_gbl_FADT.smi_command, acpi_gbl_FADT.cst_control, 8); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Notifying BIOS of _CST ability failed")); - } - } - - acpi_processor_get_power_info(pr); - pr->flags.power_setup_done = 1; - - /* - * Install the idle handler if processor power management is supported. - * Note that we use previously set idle handler will be used on - * platforms that only support C1. - */ - if (pr->flags.power) { - /* Register acpi_idle_driver if not already registered */ - if (!acpi_processor_registered) { - acpi_processor_setup_cpuidle_states(pr); - retval = cpuidle_register_driver(&acpi_idle_driver); - if (retval) - return retval; - printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n", - acpi_idle_driver.name); - } - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - per_cpu(acpi_cpuidle_device, pr->id) = dev; - - acpi_processor_setup_cpuidle_cx(pr, dev); - - /* Register per-cpu cpuidle_device. Cpuidle driver - * must already be registered before registering device - */ - retval = cpuidle_register_device(dev); - if (retval) { - if (acpi_processor_registered == 0) - cpuidle_unregister_driver(&acpi_idle_driver); - return retval; - } - acpi_processor_registered++; - } - return 0; -} - -int acpi_processor_power_exit(struct acpi_processor *pr) -{ - struct cpuidle_device *dev = per_cpu(acpi_cpuidle_device, pr->id); - - if (disabled_by_idle_boot_param()) - return 0; - - if (pr->flags.power) { - cpuidle_unregister_device(dev); - acpi_processor_registered--; - if (acpi_processor_registered == 0) - cpuidle_unregister_driver(&acpi_idle_driver); - } - - pr->flags.power_setup_done = 0; - return 0; -} diff --git a/src/4.x/drivers/acpi/processor_perflib.c b/src/4.x/drivers/acpi/processor_perflib.c deleted file mode 100644 index 9825780a1..000000000 --- a/src/4.x/drivers/acpi/processor_perflib.c +++ /dev/null @@ -1,804 +0,0 @@ -/* - * processor_perflib.c - ACPI Processor P-States Library ($Revision: 71 $) - * - * Copyright (C) 2001, 2002 Andy Grover - * Copyright (C) 2001, 2002 Paul Diefenbaugh - * Copyright (C) 2004 Dominik Brodowski - * Copyright (C) 2004 Anil S Keshavamurthy - * - Added processor hotplug support - * - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_X86 -#include -#endif - -#define PREFIX "ACPI: " - -#define ACPI_PROCESSOR_CLASS "processor" -#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance" -#define _COMPONENT ACPI_PROCESSOR_COMPONENT -ACPI_MODULE_NAME("processor_perflib"); - -static DEFINE_MUTEX(performance_mutex); - -/* - * _PPC support is implemented as a CPUfreq policy notifier: - * This means each time a CPUfreq driver registered also with - * the ACPI core is asked to change the speed policy, the maximum - * value is adjusted so that it is within the platform limit. - * - * Also, when a new platform limit value is detected, the CPUfreq - * policy is adjusted accordingly. - */ - -/* ignore_ppc: - * -1 -> cpufreq low level drivers not initialized -> _PSS, etc. not called yet - * ignore _PPC - * 0 -> cpufreq low level drivers initialized -> consider _PPC values - * 1 -> ignore _PPC totally -> forced by user through boot param - */ -static int ignore_ppc = -1; -module_param(ignore_ppc, int, 0644); -MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \ - "limited by BIOS, this should help"); - -#define PPC_REGISTERED 1 -#define PPC_IN_USE 2 - -static int acpi_processor_ppc_status; - -static int acpi_processor_ppc_notifier(struct notifier_block *nb, - unsigned long event, void *data) -{ - struct cpufreq_policy *policy = data; - struct acpi_processor *pr; - unsigned int ppc = 0; - - if (event == CPUFREQ_START && ignore_ppc <= 0) { - ignore_ppc = 0; - return 0; - } - - if (ignore_ppc) - return 0; - - if (event != CPUFREQ_ADJUST) - return 0; - - mutex_lock(&performance_mutex); - - pr = per_cpu(processors, policy->cpu); - if (!pr || !pr->performance) - goto out; - - ppc = (unsigned int)pr->performance_platform_limit; - - if (ppc >= pr->performance->state_count) - goto out; - - cpufreq_verify_within_limits(policy, 0, - pr->performance->states[ppc]. - core_frequency * 1000); - - out: - mutex_unlock(&performance_mutex); - - return 0; -} - -static struct notifier_block acpi_ppc_notifier_block = { - .notifier_call = acpi_processor_ppc_notifier, -}; - -static int acpi_processor_get_platform_limit(struct acpi_processor *pr) -{ - acpi_status status = 0; - unsigned long long ppc = 0; - - - if (!pr) - return -EINVAL; - - /* - * _PPC indicates the maximum state currently supported by the platform - * (e.g. 0 = states 0..n; 1 = states 1..n; etc. - */ - status = acpi_evaluate_integer(pr->handle, "_PPC", NULL, &ppc); - - if (status != AE_NOT_FOUND) - acpi_processor_ppc_status |= PPC_IN_USE; - - if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PPC")); - return -ENODEV; - } - - pr_debug("CPU %d: _PPC is %d - frequency %s limited\n", pr->id, - (int)ppc, ppc ? "" : "not"); - - pr->performance_platform_limit = (int)ppc; - - return 0; -} - -#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 -/* - * acpi_processor_ppc_ost: Notify firmware the _PPC evaluation status - * @handle: ACPI processor handle - * @status: the status code of _PPC evaluation - * 0: success. OSPM is now using the performance state specificed. - * 1: failure. OSPM has not changed the number of P-states in use - */ -static void acpi_processor_ppc_ost(acpi_handle handle, int status) -{ - if (acpi_has_method(handle, "_OST")) - acpi_evaluate_ost(handle, ACPI_PROCESSOR_NOTIFY_PERFORMANCE, - status, NULL); -} - -int acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag) -{ - int ret; - - if (ignore_ppc || !pr->performance) { - /* - * Only when it is notification event, the _OST object - * will be evaluated. Otherwise it is skipped. - */ - if (event_flag) - acpi_processor_ppc_ost(pr->handle, 1); - return 0; - } - - ret = acpi_processor_get_platform_limit(pr); - /* - * Only when it is notification event, the _OST object - * will be evaluated. Otherwise it is skipped. - */ - if (event_flag) { - if (ret < 0) - acpi_processor_ppc_ost(pr->handle, 1); - else - acpi_processor_ppc_ost(pr->handle, 0); - } - if (ret < 0) - return (ret); - else - return cpufreq_update_policy(pr->id); -} - -int acpi_processor_get_bios_limit(int cpu, unsigned int *limit) -{ - struct acpi_processor *pr; - - pr = per_cpu(processors, cpu); - if (!pr || !pr->performance || !pr->performance->state_count) - return -ENODEV; - *limit = pr->performance->states[pr->performance_platform_limit]. - core_frequency * 1000; - return 0; -} -EXPORT_SYMBOL(acpi_processor_get_bios_limit); - -void acpi_processor_ppc_init(void) -{ - if (!cpufreq_register_notifier - (&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER)) - acpi_processor_ppc_status |= PPC_REGISTERED; - else - printk(KERN_DEBUG - "Warning: Processor Platform Limit not supported.\n"); -} - -void acpi_processor_ppc_exit(void) -{ - if (acpi_processor_ppc_status & PPC_REGISTERED) - cpufreq_unregister_notifier(&acpi_ppc_notifier_block, - CPUFREQ_POLICY_NOTIFIER); - - acpi_processor_ppc_status &= ~PPC_REGISTERED; -} - -static int acpi_processor_get_performance_control(struct acpi_processor *pr) -{ - int result = 0; - acpi_status status = 0; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *pct = NULL; - union acpi_object obj = { 0 }; - - - status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PCT")); - return -ENODEV; - } - - pct = (union acpi_object *)buffer.pointer; - if (!pct || (pct->type != ACPI_TYPE_PACKAGE) - || (pct->package.count != 2)) { - printk(KERN_ERR PREFIX "Invalid _PCT data\n"); - result = -EFAULT; - goto end; - } - - /* - * control_register - */ - - obj = pct->package.elements[0]; - - if ((obj.type != ACPI_TYPE_BUFFER) - || (obj.buffer.length < sizeof(struct acpi_pct_register)) - || (obj.buffer.pointer == NULL)) { - printk(KERN_ERR PREFIX "Invalid _PCT data (control_register)\n"); - result = -EFAULT; - goto end; - } - memcpy(&pr->performance->control_register, obj.buffer.pointer, - sizeof(struct acpi_pct_register)); - - /* - * status_register - */ - - obj = pct->package.elements[1]; - - if ((obj.type != ACPI_TYPE_BUFFER) - || (obj.buffer.length < sizeof(struct acpi_pct_register)) - || (obj.buffer.pointer == NULL)) { - printk(KERN_ERR PREFIX "Invalid _PCT data (status_register)\n"); - result = -EFAULT; - goto end; - } - - memcpy(&pr->performance->status_register, obj.buffer.pointer, - sizeof(struct acpi_pct_register)); - - end: - kfree(buffer.pointer); - - return result; -} - -#ifdef CONFIG_X86 -/* - * Some AMDs have 50MHz frequency multiples, but only provide 100MHz rounding - * in their ACPI data. Calculate the real values and fix up the _PSS data. - */ -static void amd_fixup_frequency(struct acpi_processor_px *px, int i) -{ - u32 hi, lo, fid, did; - int index = px->control & 0x00000007; - - if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) - return; - - if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10) - || boot_cpu_data.x86 == 0x11) { - rdmsr(MSR_AMD_PSTATE_DEF_BASE + index, lo, hi); - /* - * MSR C001_0064+: - * Bit 63: PstateEn. Read-write. If set, the P-state is valid. - */ - if (!(hi & BIT(31))) - return; - - fid = lo & 0x3f; - did = (lo >> 6) & 7; - if (boot_cpu_data.x86 == 0x10) - px->core_frequency = (100 * (fid + 0x10)) >> did; - else - px->core_frequency = (100 * (fid + 8)) >> did; - } -} -#else -static void amd_fixup_frequency(struct acpi_processor_px *px, int i) {}; -#endif - -static int acpi_processor_get_performance_states(struct acpi_processor *pr) -{ - int result = 0; - acpi_status status = AE_OK; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_buffer format = { sizeof("NNNNNN"), "NNNNNN" }; - struct acpi_buffer state = { 0, NULL }; - union acpi_object *pss = NULL; - int i; - int last_invalid = -1; - - - status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PSS")); - return -ENODEV; - } - - pss = buffer.pointer; - if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) { - printk(KERN_ERR PREFIX "Invalid _PSS data\n"); - result = -EFAULT; - goto end; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d performance states\n", - pss->package.count)); - - pr->performance->state_count = pss->package.count; - pr->performance->states = - kmalloc(sizeof(struct acpi_processor_px) * pss->package.count, - GFP_KERNEL); - if (!pr->performance->states) { - result = -ENOMEM; - goto end; - } - - for (i = 0; i < pr->performance->state_count; i++) { - - struct acpi_processor_px *px = &(pr->performance->states[i]); - - state.length = sizeof(struct acpi_processor_px); - state.pointer = px; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i)); - - status = acpi_extract_package(&(pss->package.elements[i]), - &format, &state); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Invalid _PSS data")); - result = -EFAULT; - kfree(pr->performance->states); - goto end; - } - - amd_fixup_frequency(px, i); - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n", - i, - (u32) px->core_frequency, - (u32) px->power, - (u32) px->transition_latency, - (u32) px->bus_master_latency, - (u32) px->control, (u32) px->status)); - - /* - * Check that ACPI's u64 MHz will be valid as u32 KHz in cpufreq - */ - if (!px->core_frequency || - ((u32)(px->core_frequency * 1000) != - (px->core_frequency * 1000))) { - printk(KERN_ERR FW_BUG PREFIX - "Invalid BIOS _PSS frequency found for processor %d: 0x%llx MHz\n", - pr->id, px->core_frequency); - if (last_invalid == -1) - last_invalid = i; - } else { - if (last_invalid != -1) { - /* - * Copy this valid entry over last_invalid entry - */ - memcpy(&(pr->performance->states[last_invalid]), - px, sizeof(struct acpi_processor_px)); - ++last_invalid; - } - } - } - - if (last_invalid == 0) { - printk(KERN_ERR FW_BUG PREFIX - "No valid BIOS _PSS frequency found for processor %d\n", pr->id); - result = -EFAULT; - kfree(pr->performance->states); - pr->performance->states = NULL; - } - - if (last_invalid > 0) - pr->performance->state_count = last_invalid; - - end: - kfree(buffer.pointer); - - return result; -} - -int acpi_processor_get_performance_info(struct acpi_processor *pr) -{ - int result = 0; - - if (!pr || !pr->performance || !pr->handle) - return -EINVAL; - - if (!acpi_has_method(pr->handle, "_PCT")) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "ACPI-based processor performance control unavailable\n")); - return -ENODEV; - } - - result = acpi_processor_get_performance_control(pr); - if (result) - goto update_bios; - - result = acpi_processor_get_performance_states(pr); - if (result) - goto update_bios; - - /* We need to call _PPC once when cpufreq starts */ - if (ignore_ppc != 1) - result = acpi_processor_get_platform_limit(pr); - - return result; - - /* - * Having _PPC but missing frequencies (_PSS, _PCT) is a very good hint that - * the BIOS is older than the CPU and does not know its frequencies - */ - update_bios: -#ifdef CONFIG_X86 - if (acpi_has_method(pr->handle, "_PPC")) { - if(boot_cpu_has(X86_FEATURE_EST)) - printk(KERN_WARNING FW_BUG "BIOS needs update for CPU " - "frequency support\n"); - } -#endif - return result; -} -EXPORT_SYMBOL_GPL(acpi_processor_get_performance_info); -int acpi_processor_notify_smm(struct module *calling_module) -{ - acpi_status status; - static int is_done = 0; - - - if (!(acpi_processor_ppc_status & PPC_REGISTERED)) - return -EBUSY; - - if (!try_module_get(calling_module)) - return -EINVAL; - - /* is_done is set to negative if an error occurred, - * and to postitive if _no_ error occurred, but SMM - * was already notified. This avoids double notification - * which might lead to unexpected results... - */ - if (is_done > 0) { - module_put(calling_module); - return 0; - } else if (is_done < 0) { - module_put(calling_module); - return is_done; - } - - is_done = -EIO; - - /* Can't write pstate_control to smi_command if either value is zero */ - if ((!acpi_gbl_FADT.smi_command) || (!acpi_gbl_FADT.pstate_control)) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No SMI port or pstate_control\n")); - module_put(calling_module); - return 0; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Writing pstate_control [0x%x] to smi_command [0x%x]\n", - acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command)); - - status = acpi_os_write_port(acpi_gbl_FADT.smi_command, - (u32) acpi_gbl_FADT.pstate_control, 8); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Failed to write pstate_control [0x%x] to " - "smi_command [0x%x]", acpi_gbl_FADT.pstate_control, - acpi_gbl_FADT.smi_command)); - module_put(calling_module); - return status; - } - - /* Success. If there's no _PPC, we need to fear nothing, so - * we can allow the cpufreq driver to be rmmod'ed. */ - is_done = 1; - - if (!(acpi_processor_ppc_status & PPC_IN_USE)) - module_put(calling_module); - - return 0; -} - -EXPORT_SYMBOL(acpi_processor_notify_smm); - -static int acpi_processor_get_psd(struct acpi_processor *pr) -{ - int result = 0; - acpi_status status = AE_OK; - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; - struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"}; - struct acpi_buffer state = {0, NULL}; - union acpi_object *psd = NULL; - struct acpi_psd_package *pdomain; - - status = acpi_evaluate_object(pr->handle, "_PSD", NULL, &buffer); - if (ACPI_FAILURE(status)) { - return -ENODEV; - } - - psd = buffer.pointer; - if (!psd || (psd->type != ACPI_TYPE_PACKAGE)) { - printk(KERN_ERR PREFIX "Invalid _PSD data\n"); - result = -EFAULT; - goto end; - } - - if (psd->package.count != 1) { - printk(KERN_ERR PREFIX "Invalid _PSD data\n"); - result = -EFAULT; - goto end; - } - - pdomain = &(pr->performance->domain_info); - - state.length = sizeof(struct acpi_psd_package); - state.pointer = pdomain; - - status = acpi_extract_package(&(psd->package.elements[0]), - &format, &state); - if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Invalid _PSD data\n"); - result = -EFAULT; - goto end; - } - - if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) { - printk(KERN_ERR PREFIX "Unknown _PSD:num_entries\n"); - result = -EFAULT; - goto end; - } - - if (pdomain->revision != ACPI_PSD_REV0_REVISION) { - printk(KERN_ERR PREFIX "Unknown _PSD:revision\n"); - result = -EFAULT; - goto end; - } - - if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL && - pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY && - pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) { - printk(KERN_ERR PREFIX "Invalid _PSD:coord_type\n"); - result = -EFAULT; - goto end; - } -end: - kfree(buffer.pointer); - return result; -} - -int acpi_processor_preregister_performance( - struct acpi_processor_performance __percpu *performance) -{ - int count_target; - int retval = 0; - unsigned int i, j; - cpumask_var_t covered_cpus; - struct acpi_processor *pr; - struct acpi_psd_package *pdomain; - struct acpi_processor *match_pr; - struct acpi_psd_package *match_pdomain; - - if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL)) - return -ENOMEM; - - mutex_lock(&performance_mutex); - - /* - * Check if another driver has already registered, and abort before - * changing pr->performance if it has. Check input data as well. - */ - for_each_possible_cpu(i) { - pr = per_cpu(processors, i); - if (!pr) { - /* Look only at processors in ACPI namespace */ - continue; - } - - if (pr->performance) { - retval = -EBUSY; - goto err_out; - } - - if (!performance || !per_cpu_ptr(performance, i)) { - retval = -EINVAL; - goto err_out; - } - } - - /* Call _PSD for all CPUs */ - for_each_possible_cpu(i) { - pr = per_cpu(processors, i); - if (!pr) - continue; - - pr->performance = per_cpu_ptr(performance, i); - cpumask_set_cpu(i, pr->performance->shared_cpu_map); - if (acpi_processor_get_psd(pr)) { - retval = -EINVAL; - continue; - } - } - if (retval) - goto err_ret; - - /* - * Now that we have _PSD data from all CPUs, lets setup P-state - * domain info. - */ - for_each_possible_cpu(i) { - pr = per_cpu(processors, i); - if (!pr) - continue; - - if (cpumask_test_cpu(i, covered_cpus)) - continue; - - pdomain = &(pr->performance->domain_info); - cpumask_set_cpu(i, pr->performance->shared_cpu_map); - cpumask_set_cpu(i, covered_cpus); - if (pdomain->num_processors <= 1) - continue; - - /* Validate the Domain info */ - count_target = pdomain->num_processors; - if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL) - pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL; - else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL) - pr->performance->shared_type = CPUFREQ_SHARED_TYPE_HW; - else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY) - pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY; - - for_each_possible_cpu(j) { - if (i == j) - continue; - - match_pr = per_cpu(processors, j); - if (!match_pr) - continue; - - match_pdomain = &(match_pr->performance->domain_info); - if (match_pdomain->domain != pdomain->domain) - continue; - - /* Here i and j are in the same domain */ - - if (match_pdomain->num_processors != count_target) { - retval = -EINVAL; - goto err_ret; - } - - if (pdomain->coord_type != match_pdomain->coord_type) { - retval = -EINVAL; - goto err_ret; - } - - cpumask_set_cpu(j, covered_cpus); - cpumask_set_cpu(j, pr->performance->shared_cpu_map); - } - - for_each_possible_cpu(j) { - if (i == j) - continue; - - match_pr = per_cpu(processors, j); - if (!match_pr) - continue; - - match_pdomain = &(match_pr->performance->domain_info); - if (match_pdomain->domain != pdomain->domain) - continue; - - match_pr->performance->shared_type = - pr->performance->shared_type; - cpumask_copy(match_pr->performance->shared_cpu_map, - pr->performance->shared_cpu_map); - } - } - -err_ret: - for_each_possible_cpu(i) { - pr = per_cpu(processors, i); - if (!pr || !pr->performance) - continue; - - /* Assume no coordination on any error parsing domain info */ - if (retval) { - cpumask_clear(pr->performance->shared_cpu_map); - cpumask_set_cpu(i, pr->performance->shared_cpu_map); - pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL; - } - pr->performance = NULL; /* Will be set for real in register */ - } - -err_out: - mutex_unlock(&performance_mutex); - free_cpumask_var(covered_cpus); - return retval; -} -EXPORT_SYMBOL(acpi_processor_preregister_performance); - -int -acpi_processor_register_performance(struct acpi_processor_performance - *performance, unsigned int cpu) -{ - struct acpi_processor *pr; - - if (!(acpi_processor_ppc_status & PPC_REGISTERED)) - return -EINVAL; - - mutex_lock(&performance_mutex); - - pr = per_cpu(processors, cpu); - if (!pr) { - mutex_unlock(&performance_mutex); - return -ENODEV; - } - - if (pr->performance) { - mutex_unlock(&performance_mutex); - return -EBUSY; - } - - WARN_ON(!performance); - - pr->performance = performance; - - if (acpi_processor_get_performance_info(pr)) { - pr->performance = NULL; - mutex_unlock(&performance_mutex); - return -EIO; - } - - mutex_unlock(&performance_mutex); - return 0; -} - -EXPORT_SYMBOL(acpi_processor_register_performance); - -void acpi_processor_unregister_performance(unsigned int cpu) -{ - struct acpi_processor *pr; - - mutex_lock(&performance_mutex); - - pr = per_cpu(processors, cpu); - if (!pr) { - mutex_unlock(&performance_mutex); - return; - } - - if (pr->performance) - kfree(pr->performance->states); - pr->performance = NULL; - - mutex_unlock(&performance_mutex); - - return; -} - -EXPORT_SYMBOL(acpi_processor_unregister_performance); diff --git a/src/4.x/drivers/acpi/processor_thermal.c b/src/4.x/drivers/acpi/processor_thermal.c deleted file mode 100644 index 1fed84a09..000000000 --- a/src/4.x/drivers/acpi/processor_thermal.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * processor_thermal.c - Passive cooling submodule of the ACPI processor driver - * - * Copyright (C) 2001, 2002 Andy Grover - * Copyright (C) 2001, 2002 Paul Diefenbaugh - * Copyright (C) 2004 Dominik Brodowski - * Copyright (C) 2004 Anil S Keshavamurthy - * - Added processor hotplug support - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#include -#include -#include -#include -#include -#include -#include - -#define PREFIX "ACPI: " - -#define ACPI_PROCESSOR_CLASS "processor" -#define _COMPONENT ACPI_PROCESSOR_COMPONENT -ACPI_MODULE_NAME("processor_thermal"); - -#ifdef CONFIG_CPU_FREQ - -/* If a passive cooling situation is detected, primarily CPUfreq is used, as it - * offers (in most cases) voltage scaling in addition to frequency scaling, and - * thus a cubic (instead of linear) reduction of energy. Also, we allow for - * _any_ cpufreq driver and not only the acpi-cpufreq driver. - */ - -#define CPUFREQ_THERMAL_MIN_STEP 0 -#define CPUFREQ_THERMAL_MAX_STEP 3 - -static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_pctg); -static unsigned int acpi_thermal_cpufreq_is_init = 0; - -#define reduction_pctg(cpu) \ - per_cpu(cpufreq_thermal_reduction_pctg, phys_package_first_cpu(cpu)) - -/* - * Emulate "per package data" using per cpu data (which should really be - * provided elsewhere) - * - * Note we can lose a CPU on cpu hotunplug, in this case we forget the state - * temporarily. Fortunately that's not a big issue here (I hope) - */ -static int phys_package_first_cpu(int cpu) -{ - int i; - int id = topology_physical_package_id(cpu); - - for_each_online_cpu(i) - if (topology_physical_package_id(i) == id) - return i; - return 0; -} - -static int cpu_has_cpufreq(unsigned int cpu) -{ - struct cpufreq_policy policy; - if (!acpi_thermal_cpufreq_is_init || cpufreq_get_policy(&policy, cpu)) - return 0; - return 1; -} - -static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb, - unsigned long event, void *data) -{ - struct cpufreq_policy *policy = data; - unsigned long max_freq = 0; - - if (event != CPUFREQ_ADJUST) - goto out; - - max_freq = ( - policy->cpuinfo.max_freq * - (100 - reduction_pctg(policy->cpu) * 20) - ) / 100; - - cpufreq_verify_within_limits(policy, 0, max_freq); - - out: - return 0; -} - -static struct notifier_block acpi_thermal_cpufreq_notifier_block = { - .notifier_call = acpi_thermal_cpufreq_notifier, -}; - -static int cpufreq_get_max_state(unsigned int cpu) -{ - if (!cpu_has_cpufreq(cpu)) - return 0; - - return CPUFREQ_THERMAL_MAX_STEP; -} - -static int cpufreq_get_cur_state(unsigned int cpu) -{ - if (!cpu_has_cpufreq(cpu)) - return 0; - - return reduction_pctg(cpu); -} - -static int cpufreq_set_cur_state(unsigned int cpu, int state) -{ - int i; - - if (!cpu_has_cpufreq(cpu)) - return 0; - - reduction_pctg(cpu) = state; - - /* - * Update all the CPUs in the same package because they all - * contribute to the temperature and often share the same - * frequency. - */ - for_each_online_cpu(i) { - if (topology_physical_package_id(i) == - topology_physical_package_id(cpu)) - cpufreq_update_policy(i); - } - return 0; -} - -void acpi_thermal_cpufreq_init(void) -{ - int i; - - i = cpufreq_register_notifier(&acpi_thermal_cpufreq_notifier_block, - CPUFREQ_POLICY_NOTIFIER); - if (!i) - acpi_thermal_cpufreq_is_init = 1; -} - -void acpi_thermal_cpufreq_exit(void) -{ - if (acpi_thermal_cpufreq_is_init) - cpufreq_unregister_notifier - (&acpi_thermal_cpufreq_notifier_block, - CPUFREQ_POLICY_NOTIFIER); - - acpi_thermal_cpufreq_is_init = 0; -} - -#else /* ! CONFIG_CPU_FREQ */ -static int cpufreq_get_max_state(unsigned int cpu) -{ - return 0; -} - -static int cpufreq_get_cur_state(unsigned int cpu) -{ - return 0; -} - -static int cpufreq_set_cur_state(unsigned int cpu, int state) -{ - return 0; -} - -#endif - -/* thermal cooling device callbacks */ -static int acpi_processor_max_state(struct acpi_processor *pr) -{ - int max_state = 0; - - /* - * There exists four states according to - * cpufreq_thermal_reduction_pctg. 0, 1, 2, 3 - */ - max_state += cpufreq_get_max_state(pr->id); - if (pr->flags.throttling) - max_state += (pr->throttling.state_count -1); - - return max_state; -} -static int -processor_get_max_state(struct thermal_cooling_device *cdev, - unsigned long *state) -{ - struct acpi_device *device = cdev->devdata; - struct acpi_processor *pr; - - if (!device) - return -EINVAL; - - pr = acpi_driver_data(device); - if (!pr) - return -EINVAL; - - *state = acpi_processor_max_state(pr); - return 0; -} - -static int -processor_get_cur_state(struct thermal_cooling_device *cdev, - unsigned long *cur_state) -{ - struct acpi_device *device = cdev->devdata; - struct acpi_processor *pr; - - if (!device) - return -EINVAL; - - pr = acpi_driver_data(device); - if (!pr) - return -EINVAL; - - *cur_state = cpufreq_get_cur_state(pr->id); - if (pr->flags.throttling) - *cur_state += pr->throttling.state; - return 0; -} - -static int -processor_set_cur_state(struct thermal_cooling_device *cdev, - unsigned long state) -{ - struct acpi_device *device = cdev->devdata; - struct acpi_processor *pr; - int result = 0; - int max_pstate; - - if (!device) - return -EINVAL; - - pr = acpi_driver_data(device); - if (!pr) - return -EINVAL; - - max_pstate = cpufreq_get_max_state(pr->id); - - if (state > acpi_processor_max_state(pr)) - return -EINVAL; - - if (state <= max_pstate) { - if (pr->flags.throttling && pr->throttling.state) - result = acpi_processor_set_throttling(pr, 0, false); - cpufreq_set_cur_state(pr->id, state); - } else { - cpufreq_set_cur_state(pr->id, max_pstate); - result = acpi_processor_set_throttling(pr, - state - max_pstate, false); - } - return result; -} - -const struct thermal_cooling_device_ops processor_cooling_ops = { - .get_max_state = processor_get_max_state, - .get_cur_state = processor_get_cur_state, - .set_cur_state = processor_set_cur_state, -}; diff --git a/src/4.x/drivers/acpi/processor_throttling.c b/src/4.x/drivers/acpi/processor_throttling.c deleted file mode 100644 index 93d72413d..000000000 --- a/src/4.x/drivers/acpi/processor_throttling.c +++ /dev/null @@ -1,1279 +0,0 @@ -/* - * processor_throttling.c - Throttling submodule of the ACPI processor driver - * - * Copyright (C) 2001, 2002 Andy Grover - * Copyright (C) 2001, 2002 Paul Diefenbaugh - * Copyright (C) 2004 Dominik Brodowski - * Copyright (C) 2004 Anil S Keshavamurthy - * - Added processor hotplug support - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PREFIX "ACPI: " - -#define ACPI_PROCESSOR_CLASS "processor" -#define _COMPONENT ACPI_PROCESSOR_COMPONENT -ACPI_MODULE_NAME("processor_throttling"); - -/* ignore_tpc: - * 0 -> acpi processor driver doesn't ignore _TPC values - * 1 -> acpi processor driver ignores _TPC values - */ -static int ignore_tpc; -module_param(ignore_tpc, int, 0644); -MODULE_PARM_DESC(ignore_tpc, "Disable broken BIOS _TPC throttling support"); - -struct throttling_tstate { - unsigned int cpu; /* cpu nr */ - int target_state; /* target T-state */ -}; - -struct acpi_processor_throttling_arg { - struct acpi_processor *pr; - int target_state; - bool force; -}; - -#define THROTTLING_PRECHANGE (1) -#define THROTTLING_POSTCHANGE (2) - -static int acpi_processor_get_throttling(struct acpi_processor *pr); -static int __acpi_processor_set_throttling(struct acpi_processor *pr, - int state, bool force, bool direct); - -static int acpi_processor_update_tsd_coord(void) -{ - int count, count_target; - int retval = 0; - unsigned int i, j; - cpumask_var_t covered_cpus; - struct acpi_processor *pr, *match_pr; - struct acpi_tsd_package *pdomain, *match_pdomain; - struct acpi_processor_throttling *pthrottling, *match_pthrottling; - - if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL)) - return -ENOMEM; - - /* - * Now that we have _TSD data from all CPUs, lets setup T-state - * coordination between all CPUs. - */ - for_each_possible_cpu(i) { - pr = per_cpu(processors, i); - if (!pr) - continue; - - /* Basic validity check for domain info */ - pthrottling = &(pr->throttling); - - /* - * If tsd package for one cpu is invalid, the coordination - * among all CPUs is thought as invalid. - * Maybe it is ugly. - */ - if (!pthrottling->tsd_valid_flag) { - retval = -EINVAL; - break; - } - } - if (retval) - goto err_ret; - - for_each_possible_cpu(i) { - pr = per_cpu(processors, i); - if (!pr) - continue; - - if (cpumask_test_cpu(i, covered_cpus)) - continue; - pthrottling = &pr->throttling; - - pdomain = &(pthrottling->domain_info); - cpumask_set_cpu(i, pthrottling->shared_cpu_map); - cpumask_set_cpu(i, covered_cpus); - /* - * If the number of processor in the TSD domain is 1, it is - * unnecessary to parse the coordination for this CPU. - */ - if (pdomain->num_processors <= 1) - continue; - - /* Validate the Domain info */ - count_target = pdomain->num_processors; - count = 1; - - for_each_possible_cpu(j) { - if (i == j) - continue; - - match_pr = per_cpu(processors, j); - if (!match_pr) - continue; - - match_pthrottling = &(match_pr->throttling); - match_pdomain = &(match_pthrottling->domain_info); - if (match_pdomain->domain != pdomain->domain) - continue; - - /* Here i and j are in the same domain. - * If two TSD packages have the same domain, they - * should have the same num_porcessors and - * coordination type. Otherwise it will be regarded - * as illegal. - */ - if (match_pdomain->num_processors != count_target) { - retval = -EINVAL; - goto err_ret; - } - - if (pdomain->coord_type != match_pdomain->coord_type) { - retval = -EINVAL; - goto err_ret; - } - - cpumask_set_cpu(j, covered_cpus); - cpumask_set_cpu(j, pthrottling->shared_cpu_map); - count++; - } - for_each_possible_cpu(j) { - if (i == j) - continue; - - match_pr = per_cpu(processors, j); - if (!match_pr) - continue; - - match_pthrottling = &(match_pr->throttling); - match_pdomain = &(match_pthrottling->domain_info); - if (match_pdomain->domain != pdomain->domain) - continue; - - /* - * If some CPUS have the same domain, they - * will have the same shared_cpu_map. - */ - cpumask_copy(match_pthrottling->shared_cpu_map, - pthrottling->shared_cpu_map); - } - } - -err_ret: - free_cpumask_var(covered_cpus); - - for_each_possible_cpu(i) { - pr = per_cpu(processors, i); - if (!pr) - continue; - - /* - * Assume no coordination on any error parsing domain info. - * The coordination type will be forced as SW_ALL. - */ - if (retval) { - pthrottling = &(pr->throttling); - cpumask_clear(pthrottling->shared_cpu_map); - cpumask_set_cpu(i, pthrottling->shared_cpu_map); - pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL; - } - } - - return retval; -} - -/* - * Update the T-state coordination after the _TSD - * data for all cpus is obtained. - */ -void acpi_processor_throttling_init(void) -{ - if (acpi_processor_update_tsd_coord()) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Assume no T-state coordination\n")); - } - - return; -} - -static int acpi_processor_throttling_notifier(unsigned long event, void *data) -{ - struct throttling_tstate *p_tstate = data; - struct acpi_processor *pr; - unsigned int cpu ; - int target_state; - struct acpi_processor_limit *p_limit; - struct acpi_processor_throttling *p_throttling; - - cpu = p_tstate->cpu; - pr = per_cpu(processors, cpu); - if (!pr) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid pr pointer\n")); - return 0; - } - if (!pr->flags.throttling) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Throttling control is " - "unsupported on CPU %d\n", cpu)); - return 0; - } - target_state = p_tstate->target_state; - p_throttling = &(pr->throttling); - switch (event) { - case THROTTLING_PRECHANGE: - /* - * Prechange event is used to choose one proper t-state, - * which meets the limits of thermal, user and _TPC. - */ - p_limit = &pr->limit; - if (p_limit->thermal.tx > target_state) - target_state = p_limit->thermal.tx; - if (p_limit->user.tx > target_state) - target_state = p_limit->user.tx; - if (pr->throttling_platform_limit > target_state) - target_state = pr->throttling_platform_limit; - if (target_state >= p_throttling->state_count) { - printk(KERN_WARNING - "Exceed the limit of T-state \n"); - target_state = p_throttling->state_count - 1; - } - p_tstate->target_state = target_state; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PreChange Event:" - "target T-state of CPU %d is T%d\n", - cpu, target_state)); - break; - case THROTTLING_POSTCHANGE: - /* - * Postchange event is only used to update the - * T-state flag of acpi_processor_throttling. - */ - p_throttling->state = target_state; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PostChange Event:" - "CPU %d is switched to T%d\n", - cpu, target_state)); - break; - default: - printk(KERN_WARNING - "Unsupported Throttling notifier event\n"); - break; - } - - return 0; -} - -/* - * _TPC - Throttling Present Capabilities - */ -static int acpi_processor_get_platform_limit(struct acpi_processor *pr) -{ - acpi_status status = 0; - unsigned long long tpc = 0; - - if (!pr) - return -EINVAL; - - if (ignore_tpc) - goto end; - - status = acpi_evaluate_integer(pr->handle, "_TPC", NULL, &tpc); - if (ACPI_FAILURE(status)) { - if (status != AE_NOT_FOUND) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _TPC")); - } - return -ENODEV; - } - -end: - pr->throttling_platform_limit = (int)tpc; - return 0; -} - -int acpi_processor_tstate_has_changed(struct acpi_processor *pr) -{ - int result = 0; - int throttling_limit; - int current_state; - struct acpi_processor_limit *limit; - int target_state; - - if (ignore_tpc) - return 0; - - result = acpi_processor_get_platform_limit(pr); - if (result) { - /* Throttling Limit is unsupported */ - return result; - } - - throttling_limit = pr->throttling_platform_limit; - if (throttling_limit >= pr->throttling.state_count) { - /* Uncorrect Throttling Limit */ - return -EINVAL; - } - - current_state = pr->throttling.state; - if (current_state > throttling_limit) { - /* - * The current state can meet the requirement of - * _TPC limit. But it is reasonable that OSPM changes - * t-states from high to low for better performance. - * Of course the limit condition of thermal - * and user should be considered. - */ - limit = &pr->limit; - target_state = throttling_limit; - if (limit->thermal.tx > target_state) - target_state = limit->thermal.tx; - if (limit->user.tx > target_state) - target_state = limit->user.tx; - } else if (current_state == throttling_limit) { - /* - * Unnecessary to change the throttling state - */ - return 0; - } else { - /* - * If the current state is lower than the limit of _TPC, it - * will be forced to switch to the throttling state defined - * by throttling_platfor_limit. - * Because the previous state meets with the limit condition - * of thermal and user, it is unnecessary to check it again. - */ - target_state = throttling_limit; - } - return acpi_processor_set_throttling(pr, target_state, false); -} - -/* - * This function is used to reevaluate whether the T-state is valid - * after one CPU is onlined/offlined. - * It is noted that it won't reevaluate the following properties for - * the T-state. - * 1. Control method. - * 2. the number of supported T-state - * 3. TSD domain - */ -void acpi_processor_reevaluate_tstate(struct acpi_processor *pr, - unsigned long action) -{ - int result = 0; - - if (action == CPU_DEAD) { - /* When one CPU is offline, the T-state throttling - * will be invalidated. - */ - pr->flags.throttling = 0; - return; - } - /* the following is to recheck whether the T-state is valid for - * the online CPU - */ - if (!pr->throttling.state_count) { - /* If the number of T-state is invalid, it is - * invalidated. - */ - pr->flags.throttling = 0; - return; - } - pr->flags.throttling = 1; - - /* Disable throttling (if enabled). We'll let subsequent - * policy (e.g.thermal) decide to lower performance if it - * so chooses, but for now we'll crank up the speed. - */ - - result = acpi_processor_get_throttling(pr); - if (result) - goto end; - - if (pr->throttling.state) { - result = acpi_processor_set_throttling(pr, 0, false); - if (result) - goto end; - } - -end: - if (result) - pr->flags.throttling = 0; -} -/* - * _PTC - Processor Throttling Control (and status) register location - */ -static int acpi_processor_get_throttling_control(struct acpi_processor *pr) -{ - int result = 0; - acpi_status status = 0; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *ptc = NULL; - union acpi_object obj = { 0 }; - struct acpi_processor_throttling *throttling; - - status = acpi_evaluate_object(pr->handle, "_PTC", NULL, &buffer); - if (ACPI_FAILURE(status)) { - if (status != AE_NOT_FOUND) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PTC")); - } - return -ENODEV; - } - - ptc = (union acpi_object *)buffer.pointer; - if (!ptc || (ptc->type != ACPI_TYPE_PACKAGE) - || (ptc->package.count != 2)) { - printk(KERN_ERR PREFIX "Invalid _PTC data\n"); - result = -EFAULT; - goto end; - } - - /* - * control_register - */ - - obj = ptc->package.elements[0]; - - if ((obj.type != ACPI_TYPE_BUFFER) - || (obj.buffer.length < sizeof(struct acpi_ptc_register)) - || (obj.buffer.pointer == NULL)) { - printk(KERN_ERR PREFIX - "Invalid _PTC data (control_register)\n"); - result = -EFAULT; - goto end; - } - memcpy(&pr->throttling.control_register, obj.buffer.pointer, - sizeof(struct acpi_ptc_register)); - - /* - * status_register - */ - - obj = ptc->package.elements[1]; - - if ((obj.type != ACPI_TYPE_BUFFER) - || (obj.buffer.length < sizeof(struct acpi_ptc_register)) - || (obj.buffer.pointer == NULL)) { - printk(KERN_ERR PREFIX "Invalid _PTC data (status_register)\n"); - result = -EFAULT; - goto end; - } - - memcpy(&pr->throttling.status_register, obj.buffer.pointer, - sizeof(struct acpi_ptc_register)); - - throttling = &pr->throttling; - - if ((throttling->control_register.bit_width + - throttling->control_register.bit_offset) > 32) { - printk(KERN_ERR PREFIX "Invalid _PTC control register\n"); - result = -EFAULT; - goto end; - } - - if ((throttling->status_register.bit_width + - throttling->status_register.bit_offset) > 32) { - printk(KERN_ERR PREFIX "Invalid _PTC status register\n"); - result = -EFAULT; - goto end; - } - - end: - kfree(buffer.pointer); - - return result; -} - -/* - * _TSS - Throttling Supported States - */ -static int acpi_processor_get_throttling_states(struct acpi_processor *pr) -{ - int result = 0; - acpi_status status = AE_OK; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" }; - struct acpi_buffer state = { 0, NULL }; - union acpi_object *tss = NULL; - int i; - - status = acpi_evaluate_object(pr->handle, "_TSS", NULL, &buffer); - if (ACPI_FAILURE(status)) { - if (status != AE_NOT_FOUND) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _TSS")); - } - return -ENODEV; - } - - tss = buffer.pointer; - if (!tss || (tss->type != ACPI_TYPE_PACKAGE)) { - printk(KERN_ERR PREFIX "Invalid _TSS data\n"); - result = -EFAULT; - goto end; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d throttling states\n", - tss->package.count)); - - pr->throttling.state_count = tss->package.count; - pr->throttling.states_tss = - kmalloc(sizeof(struct acpi_processor_tx_tss) * tss->package.count, - GFP_KERNEL); - if (!pr->throttling.states_tss) { - result = -ENOMEM; - goto end; - } - - for (i = 0; i < pr->throttling.state_count; i++) { - - struct acpi_processor_tx_tss *tx = - (struct acpi_processor_tx_tss *)&(pr->throttling. - states_tss[i]); - - state.length = sizeof(struct acpi_processor_tx_tss); - state.pointer = tx; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i)); - - status = acpi_extract_package(&(tss->package.elements[i]), - &format, &state); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Invalid _TSS data")); - result = -EFAULT; - kfree(pr->throttling.states_tss); - goto end; - } - - if (!tx->freqpercentage) { - printk(KERN_ERR PREFIX - "Invalid _TSS data: freq is zero\n"); - result = -EFAULT; - kfree(pr->throttling.states_tss); - goto end; - } - } - - end: - kfree(buffer.pointer); - - return result; -} - -/* - * _TSD - T-State Dependencies - */ -static int acpi_processor_get_tsd(struct acpi_processor *pr) -{ - int result = 0; - acpi_status status = AE_OK; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" }; - struct acpi_buffer state = { 0, NULL }; - union acpi_object *tsd = NULL; - struct acpi_tsd_package *pdomain; - struct acpi_processor_throttling *pthrottling; - - pthrottling = &pr->throttling; - pthrottling->tsd_valid_flag = 0; - - status = acpi_evaluate_object(pr->handle, "_TSD", NULL, &buffer); - if (ACPI_FAILURE(status)) { - if (status != AE_NOT_FOUND) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _TSD")); - } - return -ENODEV; - } - - tsd = buffer.pointer; - if (!tsd || (tsd->type != ACPI_TYPE_PACKAGE)) { - printk(KERN_ERR PREFIX "Invalid _TSD data\n"); - result = -EFAULT; - goto end; - } - - if (tsd->package.count != 1) { - printk(KERN_ERR PREFIX "Invalid _TSD data\n"); - result = -EFAULT; - goto end; - } - - pdomain = &(pr->throttling.domain_info); - - state.length = sizeof(struct acpi_tsd_package); - state.pointer = pdomain; - - status = acpi_extract_package(&(tsd->package.elements[0]), - &format, &state); - if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Invalid _TSD data\n"); - result = -EFAULT; - goto end; - } - - if (pdomain->num_entries != ACPI_TSD_REV0_ENTRIES) { - printk(KERN_ERR PREFIX "Unknown _TSD:num_entries\n"); - result = -EFAULT; - goto end; - } - - if (pdomain->revision != ACPI_TSD_REV0_REVISION) { - printk(KERN_ERR PREFIX "Unknown _TSD:revision\n"); - result = -EFAULT; - goto end; - } - - pthrottling = &pr->throttling; - pthrottling->tsd_valid_flag = 1; - pthrottling->shared_type = pdomain->coord_type; - cpumask_set_cpu(pr->id, pthrottling->shared_cpu_map); - /* - * If the coordination type is not defined in ACPI spec, - * the tsd_valid_flag will be clear and coordination type - * will be forecd as DOMAIN_COORD_TYPE_SW_ALL. - */ - if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL && - pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY && - pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) { - pthrottling->tsd_valid_flag = 0; - pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL; - } - - end: - kfree(buffer.pointer); - return result; -} - -/* -------------------------------------------------------------------------- - Throttling Control - -------------------------------------------------------------------------- */ -static int acpi_processor_get_throttling_fadt(struct acpi_processor *pr) -{ - int state = 0; - u32 value = 0; - u32 duty_mask = 0; - u32 duty_value = 0; - - if (!pr) - return -EINVAL; - - if (!pr->flags.throttling) - return -ENODEV; - - /* - * We don't care about error returns - we just try to mark - * these reserved so that nobody else is confused into thinking - * that this region might be unused.. - * - * (In particular, allocating the IO range for Cardbus) - */ - request_region(pr->throttling.address, 6, "ACPI CPU throttle"); - - pr->throttling.state = 0; - - duty_mask = pr->throttling.state_count - 1; - - duty_mask <<= pr->throttling.duty_offset; - - local_irq_disable(); - - value = inl(pr->throttling.address); - - /* - * Compute the current throttling state when throttling is enabled - * (bit 4 is on). - */ - if (value & 0x10) { - duty_value = value & duty_mask; - duty_value >>= pr->throttling.duty_offset; - - if (duty_value) - state = pr->throttling.state_count - duty_value; - } - - pr->throttling.state = state; - - local_irq_enable(); - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Throttling state is T%d (%d%% throttling applied)\n", - state, pr->throttling.states[state].performance)); - - return 0; -} - -#ifdef CONFIG_X86 -static int acpi_throttling_rdmsr(u64 *value) -{ - u64 msr_high, msr_low; - u64 msr = 0; - int ret = -1; - - if ((this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_INTEL) || - !this_cpu_has(X86_FEATURE_ACPI)) { - printk(KERN_ERR PREFIX - "HARDWARE addr space,NOT supported yet\n"); - } else { - msr_low = 0; - msr_high = 0; - rdmsr_safe(MSR_IA32_THERM_CONTROL, - (u32 *)&msr_low , (u32 *) &msr_high); - msr = (msr_high << 32) | msr_low; - *value = (u64) msr; - ret = 0; - } - return ret; -} - -static int acpi_throttling_wrmsr(u64 value) -{ - int ret = -1; - u64 msr; - - if ((this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_INTEL) || - !this_cpu_has(X86_FEATURE_ACPI)) { - printk(KERN_ERR PREFIX - "HARDWARE addr space,NOT supported yet\n"); - } else { - msr = value; - wrmsr_safe(MSR_IA32_THERM_CONTROL, - msr & 0xffffffff, msr >> 32); - ret = 0; - } - return ret; -} -#else -static int acpi_throttling_rdmsr(u64 *value) -{ - printk(KERN_ERR PREFIX - "HARDWARE addr space,NOT supported yet\n"); - return -1; -} - -static int acpi_throttling_wrmsr(u64 value) -{ - printk(KERN_ERR PREFIX - "HARDWARE addr space,NOT supported yet\n"); - return -1; -} -#endif - -static int acpi_read_throttling_status(struct acpi_processor *pr, - u64 *value) -{ - u32 bit_width, bit_offset; - u32 ptc_value; - u64 ptc_mask; - struct acpi_processor_throttling *throttling; - int ret = -1; - - throttling = &pr->throttling; - switch (throttling->status_register.space_id) { - case ACPI_ADR_SPACE_SYSTEM_IO: - bit_width = throttling->status_register.bit_width; - bit_offset = throttling->status_register.bit_offset; - - acpi_os_read_port((acpi_io_address) throttling->status_register. - address, &ptc_value, - (u32) (bit_width + bit_offset)); - ptc_mask = (1 << bit_width) - 1; - *value = (u64) ((ptc_value >> bit_offset) & ptc_mask); - ret = 0; - break; - case ACPI_ADR_SPACE_FIXED_HARDWARE: - ret = acpi_throttling_rdmsr(value); - break; - default: - printk(KERN_ERR PREFIX "Unknown addr space %d\n", - (u32) (throttling->status_register.space_id)); - } - return ret; -} - -static int acpi_write_throttling_state(struct acpi_processor *pr, - u64 value) -{ - u32 bit_width, bit_offset; - u64 ptc_value; - u64 ptc_mask; - struct acpi_processor_throttling *throttling; - int ret = -1; - - throttling = &pr->throttling; - switch (throttling->control_register.space_id) { - case ACPI_ADR_SPACE_SYSTEM_IO: - bit_width = throttling->control_register.bit_width; - bit_offset = throttling->control_register.bit_offset; - ptc_mask = (1 << bit_width) - 1; - ptc_value = value & ptc_mask; - - acpi_os_write_port((acpi_io_address) throttling-> - control_register.address, - (u32) (ptc_value << bit_offset), - (u32) (bit_width + bit_offset)); - ret = 0; - break; - case ACPI_ADR_SPACE_FIXED_HARDWARE: - ret = acpi_throttling_wrmsr(value); - break; - default: - printk(KERN_ERR PREFIX "Unknown addr space %d\n", - (u32) (throttling->control_register.space_id)); - } - return ret; -} - -static int acpi_get_throttling_state(struct acpi_processor *pr, - u64 value) -{ - int i; - - for (i = 0; i < pr->throttling.state_count; i++) { - struct acpi_processor_tx_tss *tx = - (struct acpi_processor_tx_tss *)&(pr->throttling. - states_tss[i]); - if (tx->control == value) - return i; - } - return -1; -} - -static int acpi_get_throttling_value(struct acpi_processor *pr, - int state, u64 *value) -{ - int ret = -1; - - if (state >= 0 && state <= pr->throttling.state_count) { - struct acpi_processor_tx_tss *tx = - (struct acpi_processor_tx_tss *)&(pr->throttling. - states_tss[state]); - *value = tx->control; - ret = 0; - } - return ret; -} - -static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr) -{ - int state = 0; - int ret; - u64 value; - - if (!pr) - return -EINVAL; - - if (!pr->flags.throttling) - return -ENODEV; - - pr->throttling.state = 0; - - value = 0; - ret = acpi_read_throttling_status(pr, &value); - if (ret >= 0) { - state = acpi_get_throttling_state(pr, value); - if (state == -1) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Invalid throttling state, reset\n")); - state = 0; - ret = __acpi_processor_set_throttling(pr, state, true, - true); - if (ret) - return ret; - } - pr->throttling.state = state; - } - - return 0; -} - -static long __acpi_processor_get_throttling(void *data) -{ - struct acpi_processor *pr = data; - - return pr->throttling.acpi_processor_get_throttling(pr); -} - -static int acpi_processor_get_throttling(struct acpi_processor *pr) -{ - if (!pr) - return -EINVAL; - - if (!pr->flags.throttling) - return -ENODEV; - - /* - * This is either called from the CPU hotplug callback of - * processor_driver or via the ACPI probe function. In the latter - * case the CPU is not guaranteed to be online. Both call sites are - * protected against CPU hotplug. - */ - if (!cpu_online(pr->id)) - return -ENODEV; - - return work_on_cpu(pr->id, __acpi_processor_get_throttling, pr); -} - -static int acpi_processor_get_fadt_info(struct acpi_processor *pr) -{ - int i, step; - - if (!pr->throttling.address) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling register\n")); - return -EINVAL; - } else if (!pr->throttling.duty_width) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling states\n")); - return -EINVAL; - } - /* TBD: Support duty_cycle values that span bit 4. */ - else if ((pr->throttling.duty_offset + pr->throttling.duty_width) > 4) { - printk(KERN_WARNING PREFIX "duty_cycle spans bit 4\n"); - return -EINVAL; - } - - pr->throttling.state_count = 1 << acpi_gbl_FADT.duty_width; - - /* - * Compute state values. Note that throttling displays a linear power - * performance relationship (at 50% performance the CPU will consume - * 50% power). Values are in 1/10th of a percent to preserve accuracy. - */ - - step = (1000 / pr->throttling.state_count); - - for (i = 0; i < pr->throttling.state_count; i++) { - pr->throttling.states[i].performance = 1000 - step * i; - pr->throttling.states[i].power = 1000 - step * i; - } - return 0; -} - -static int acpi_processor_set_throttling_fadt(struct acpi_processor *pr, - int state, bool force) -{ - u32 value = 0; - u32 duty_mask = 0; - u32 duty_value = 0; - - if (!pr) - return -EINVAL; - - if ((state < 0) || (state > (pr->throttling.state_count - 1))) - return -EINVAL; - - if (!pr->flags.throttling) - return -ENODEV; - - if (!force && (state == pr->throttling.state)) - return 0; - - if (state < pr->throttling_platform_limit) - return -EPERM; - /* - * Calculate the duty_value and duty_mask. - */ - if (state) { - duty_value = pr->throttling.state_count - state; - - duty_value <<= pr->throttling.duty_offset; - - /* Used to clear all duty_value bits */ - duty_mask = pr->throttling.state_count - 1; - - duty_mask <<= acpi_gbl_FADT.duty_offset; - duty_mask = ~duty_mask; - } - - local_irq_disable(); - - /* - * Disable throttling by writing a 0 to bit 4. Note that we must - * turn it off before you can change the duty_value. - */ - value = inl(pr->throttling.address); - if (value & 0x10) { - value &= 0xFFFFFFEF; - outl(value, pr->throttling.address); - } - - /* - * Write the new duty_value and then enable throttling. Note - * that a state value of 0 leaves throttling disabled. - */ - if (state) { - value &= duty_mask; - value |= duty_value; - outl(value, pr->throttling.address); - - value |= 0x00000010; - outl(value, pr->throttling.address); - } - - pr->throttling.state = state; - - local_irq_enable(); - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Throttling state set to T%d (%d%%)\n", state, - (pr->throttling.states[state].performance ? pr-> - throttling.states[state].performance / 10 : 0))); - - return 0; -} - -static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr, - int state, bool force) -{ - int ret; - u64 value; - - if (!pr) - return -EINVAL; - - if ((state < 0) || (state > (pr->throttling.state_count - 1))) - return -EINVAL; - - if (!pr->flags.throttling) - return -ENODEV; - - if (!force && (state == pr->throttling.state)) - return 0; - - if (state < pr->throttling_platform_limit) - return -EPERM; - - value = 0; - ret = acpi_get_throttling_value(pr, state, &value); - if (ret >= 0) { - acpi_write_throttling_state(pr, value); - pr->throttling.state = state; - } - - return 0; -} - -static long acpi_processor_throttling_fn(void *data) -{ - struct acpi_processor_throttling_arg *arg = data; - struct acpi_processor *pr = arg->pr; - - return pr->throttling.acpi_processor_set_throttling(pr, - arg->target_state, arg->force); -} - -static int call_on_cpu(int cpu, long (*fn)(void *), void *arg, bool direct) -{ - if (direct) - return fn(arg); - return work_on_cpu(cpu, fn, arg); -} - -static int __acpi_processor_set_throttling(struct acpi_processor *pr, - int state, bool force, bool direct) -{ - int ret = 0; - unsigned int i; - struct acpi_processor *match_pr; - struct acpi_processor_throttling *p_throttling; - struct acpi_processor_throttling_arg arg; - struct throttling_tstate t_state; - - if (!pr) - return -EINVAL; - - if (!pr->flags.throttling) - return -ENODEV; - - if ((state < 0) || (state > (pr->throttling.state_count - 1))) - return -EINVAL; - - if (cpu_is_offline(pr->id)) { - /* - * the cpu pointed by pr->id is offline. Unnecessary to change - * the throttling state any more. - */ - return -ENODEV; - } - - t_state.target_state = state; - p_throttling = &(pr->throttling); - - /* - * The throttling notifier will be called for every - * affected cpu in order to get one proper T-state. - * The notifier event is THROTTLING_PRECHANGE. - */ - for_each_cpu_and(i, cpu_online_mask, p_throttling->shared_cpu_map) { - t_state.cpu = i; - acpi_processor_throttling_notifier(THROTTLING_PRECHANGE, - &t_state); - } - /* - * The function of acpi_processor_set_throttling will be called - * to switch T-state. If the coordination type is SW_ALL or HW_ALL, - * it is necessary to call it for every affected cpu. Otherwise - * it can be called only for the cpu pointed by pr. - */ - if (p_throttling->shared_type == DOMAIN_COORD_TYPE_SW_ANY) { - arg.pr = pr; - arg.target_state = state; - arg.force = force; - ret = call_on_cpu(pr->id, acpi_processor_throttling_fn, &arg, - direct); - } else { - /* - * When the T-state coordination is SW_ALL or HW_ALL, - * it is necessary to set T-state for every affected - * cpus. - */ - for_each_cpu_and(i, cpu_online_mask, - p_throttling->shared_cpu_map) { - match_pr = per_cpu(processors, i); - /* - * If the pointer is invalid, we will report the - * error message and continue. - */ - if (!match_pr) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Invalid Pointer for CPU %d\n", i)); - continue; - } - /* - * If the throttling control is unsupported on CPU i, - * we will report the error message and continue. - */ - if (!match_pr->flags.throttling) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Throttling Control is unsupported " - "on CPU %d\n", i)); - continue; - } - - arg.pr = match_pr; - arg.target_state = state; - arg.force = force; - ret = call_on_cpu(pr->id, acpi_processor_throttling_fn, - &arg, direct); - } - } - /* - * After the set_throttling is called, the - * throttling notifier is called for every - * affected cpu to update the T-states. - * The notifier event is THROTTLING_POSTCHANGE - */ - for_each_cpu_and(i, cpu_online_mask, p_throttling->shared_cpu_map) { - t_state.cpu = i; - acpi_processor_throttling_notifier(THROTTLING_POSTCHANGE, - &t_state); - } - - return ret; -} - -int acpi_processor_set_throttling(struct acpi_processor *pr, int state, - bool force) -{ - return __acpi_processor_set_throttling(pr, state, force, false); -} - -int acpi_processor_get_throttling_info(struct acpi_processor *pr) -{ - int result = 0; - struct acpi_processor_throttling *pthrottling; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n", - pr->throttling.address, - pr->throttling.duty_offset, - pr->throttling.duty_width)); - - /* - * Evaluate _PTC, _TSS and _TPC - * They must all be present or none of them can be used. - */ - if (acpi_processor_get_throttling_control(pr) || - acpi_processor_get_throttling_states(pr) || - acpi_processor_get_platform_limit(pr)) - { - pr->throttling.acpi_processor_get_throttling = - &acpi_processor_get_throttling_fadt; - pr->throttling.acpi_processor_set_throttling = - &acpi_processor_set_throttling_fadt; - if (acpi_processor_get_fadt_info(pr)) - return 0; - } else { - pr->throttling.acpi_processor_get_throttling = - &acpi_processor_get_throttling_ptc; - pr->throttling.acpi_processor_set_throttling = - &acpi_processor_set_throttling_ptc; - } - - /* - * If TSD package for one CPU can't be parsed successfully, it means - * that this CPU will have no coordination with other CPUs. - */ - if (acpi_processor_get_tsd(pr)) { - pthrottling = &pr->throttling; - pthrottling->tsd_valid_flag = 0; - cpumask_set_cpu(pr->id, pthrottling->shared_cpu_map); - pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL; - } - - /* - * PIIX4 Errata: We don't support throttling on the original PIIX4. - * This shouldn't be an issue as few (if any) mobile systems ever - * used this part. - */ - if (errata.piix4.throttle) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Throttling not supported on PIIX4 A- or B-step\n")); - return 0; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d throttling states\n", - pr->throttling.state_count)); - - pr->flags.throttling = 1; - - /* - * Disable throttling (if enabled). We'll let subsequent policy (e.g. - * thermal) decide to lower performance if it so chooses, but for now - * we'll crank up the speed. - */ - - result = acpi_processor_get_throttling(pr); - if (result) - goto end; - - if (pr->throttling.state) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Disabling throttling (was T%d)\n", - pr->throttling.state)); - result = acpi_processor_set_throttling(pr, 0, false); - if (result) - goto end; - } - - end: - if (result) - pr->flags.throttling = 0; - - return result; -} - diff --git a/src/4.x/drivers/acpi/thermal.c b/src/4.x/drivers/acpi/thermal.c deleted file mode 100644 index 82707f982..000000000 --- a/src/4.x/drivers/acpi/thermal.c +++ /dev/null @@ -1,1284 +0,0 @@ -/* - * acpi_thermal.c - ACPI Thermal Zone Driver ($Revision: 41 $) - * - * Copyright (C) 2001, 2002 Andy Grover - * Copyright (C) 2001, 2002 Paul Diefenbaugh - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This driver fully implements the ACPI thermal policy as described in the - * ACPI 2.0 Specification. - * - * TBD: 1. Implement passive cooling hysteresis. - * 2. Enhance passive cooling (CPU) states/limit interface to support - * concepts of 'multiple limiters', upper/lower limits, etc. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PREFIX "ACPI: " - -#define ACPI_THERMAL_CLASS "thermal_zone" -#define ACPI_THERMAL_DEVICE_NAME "Thermal Zone" -#define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80 -#define ACPI_THERMAL_NOTIFY_THRESHOLDS 0x81 -#define ACPI_THERMAL_NOTIFY_DEVICES 0x82 -#define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0 -#define ACPI_THERMAL_NOTIFY_HOT 0xF1 -#define ACPI_THERMAL_MODE_ACTIVE 0x00 - -#define ACPI_THERMAL_MAX_ACTIVE 10 -#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65 - -#define _COMPONENT ACPI_THERMAL_COMPONENT -ACPI_MODULE_NAME("thermal"); - -MODULE_AUTHOR("Paul Diefenbaugh"); -MODULE_DESCRIPTION("ACPI Thermal Zone Driver"); -MODULE_LICENSE("GPL"); - -static int act; -module_param(act, int, 0644); -MODULE_PARM_DESC(act, "Disable or override all lowest active trip points."); - -static int crt; -module_param(crt, int, 0644); -MODULE_PARM_DESC(crt, "Disable or lower all critical trip points."); - -static int tzp; -module_param(tzp, int, 0444); -MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds."); - -static int nocrt; -module_param(nocrt, int, 0); -MODULE_PARM_DESC(nocrt, "Set to take no action upon ACPI thermal zone critical trips points."); - -static int off; -module_param(off, int, 0); -MODULE_PARM_DESC(off, "Set to disable ACPI thermal support."); - -static int psv; -module_param(psv, int, 0644); -MODULE_PARM_DESC(psv, "Disable or override all passive trip points."); - -static struct workqueue_struct *acpi_thermal_pm_queue; - -static int acpi_thermal_add(struct acpi_device *device); -static int acpi_thermal_remove(struct acpi_device *device); -static void acpi_thermal_notify(struct acpi_device *device, u32 event); - -static const struct acpi_device_id thermal_device_ids[] = { - {ACPI_THERMAL_HID, 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, thermal_device_ids); - -#ifdef CONFIG_PM_SLEEP -static int acpi_thermal_suspend(struct device *dev); -static int acpi_thermal_resume(struct device *dev); -#else -#define acpi_thermal_suspend NULL -#define acpi_thermal_resume NULL -#endif -static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume); - -static struct acpi_driver acpi_thermal_driver = { - .name = "thermal", - .class = ACPI_THERMAL_CLASS, - .ids = thermal_device_ids, - .ops = { - .add = acpi_thermal_add, - .remove = acpi_thermal_remove, - .notify = acpi_thermal_notify, - }, - .drv.pm = &acpi_thermal_pm, -}; - -struct acpi_thermal_state { - u8 critical:1; - u8 hot:1; - u8 passive:1; - u8 active:1; - u8 reserved:4; - int active_index; -}; - -struct acpi_thermal_state_flags { - u8 valid:1; - u8 enabled:1; - u8 reserved:6; -}; - -struct acpi_thermal_critical { - struct acpi_thermal_state_flags flags; - unsigned long temperature; -}; - -struct acpi_thermal_hot { - struct acpi_thermal_state_flags flags; - unsigned long temperature; -}; - -struct acpi_thermal_passive { - struct acpi_thermal_state_flags flags; - unsigned long temperature; - unsigned long tc1; - unsigned long tc2; - unsigned long tsp; - struct acpi_handle_list devices; -}; - -struct acpi_thermal_active { - struct acpi_thermal_state_flags flags; - unsigned long temperature; - struct acpi_handle_list devices; -}; - -struct acpi_thermal_trips { - struct acpi_thermal_critical critical; - struct acpi_thermal_hot hot; - struct acpi_thermal_passive passive; - struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE]; -}; - -struct acpi_thermal_flags { - u8 cooling_mode:1; /* _SCP */ - u8 devices:1; /* _TZD */ - u8 reserved:6; -}; - -struct acpi_thermal { - struct acpi_device * device; - acpi_bus_id name; - unsigned long temperature; - unsigned long last_temperature; - unsigned long polling_frequency; - volatile u8 zombie; - struct acpi_thermal_flags flags; - struct acpi_thermal_state state; - struct acpi_thermal_trips trips; - struct acpi_handle_list devices; - struct thermal_zone_device *thermal_zone; - int tz_enabled; - int kelvin_offset; - struct work_struct thermal_check_work; -}; - -/* -------------------------------------------------------------------------- - Thermal Zone Management - -------------------------------------------------------------------------- */ - -static int acpi_thermal_get_temperature(struct acpi_thermal *tz) -{ - acpi_status status = AE_OK; - unsigned long long tmp; - - if (!tz) - return -EINVAL; - - tz->last_temperature = tz->temperature; - - status = acpi_evaluate_integer(tz->device->handle, "_TMP", NULL, &tmp); - if (ACPI_FAILURE(status)) - return -ENODEV; - - tz->temperature = tmp; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Temperature is %lu dK\n", - tz->temperature)); - - return 0; -} - -static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz) -{ - acpi_status status = AE_OK; - unsigned long long tmp; - - if (!tz) - return -EINVAL; - - status = acpi_evaluate_integer(tz->device->handle, "_TZP", NULL, &tmp); - if (ACPI_FAILURE(status)) - return -ENODEV; - - tz->polling_frequency = tmp; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency is %lu dS\n", - tz->polling_frequency)); - - return 0; -} - -static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode) -{ - if (!tz) - return -EINVAL; - - if (!acpi_has_method(tz->device->handle, "_SCP")) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n")); - return -ENODEV; - } else if (ACPI_FAILURE(acpi_execute_simple_method(tz->device->handle, - "_SCP", mode))) { - return -ENODEV; - } - - return 0; -} - -#define ACPI_TRIPS_CRITICAL 0x01 -#define ACPI_TRIPS_HOT 0x02 -#define ACPI_TRIPS_PASSIVE 0x04 -#define ACPI_TRIPS_ACTIVE 0x08 -#define ACPI_TRIPS_DEVICES 0x10 - -#define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE) -#define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES - -#define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \ - ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \ - ACPI_TRIPS_DEVICES) - -/* - * This exception is thrown out in two cases: - * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid - * when re-evaluating the AML code. - * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change. - * We need to re-bind the cooling devices of a thermal zone when this occurs. - */ -#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str) \ -do { \ - if (flags != ACPI_TRIPS_INIT) \ - ACPI_EXCEPTION((AE_INFO, AE_ERROR, \ - "ACPI thermal trip point %s changed\n" \ - "Please send acpidump to linux-acpi@vger.kernel.org", str)); \ -} while (0) - -static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) -{ - acpi_status status = AE_OK; - unsigned long long tmp; - struct acpi_handle_list devices; - int valid = 0; - int i; - - /* Critical Shutdown */ - if (flag & ACPI_TRIPS_CRITICAL) { - status = acpi_evaluate_integer(tz->device->handle, - "_CRT", NULL, &tmp); - tz->trips.critical.temperature = tmp; - /* - * Treat freezing temperatures as invalid as well; some - * BIOSes return really low values and cause reboots at startup. - * Below zero (Celsius) values clearly aren't right for sure.. - * ... so lets discard those as invalid. - */ - if (ACPI_FAILURE(status)) { - tz->trips.critical.flags.valid = 0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "No critical threshold\n")); - } else if (tmp <= 2732) { - pr_warn(FW_BUG "Invalid critical threshold (%llu)\n", - tmp); - tz->trips.critical.flags.valid = 0; - } else { - tz->trips.critical.flags.valid = 1; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Found critical threshold [%lu]\n", - tz->trips.critical.temperature)); - } - if (tz->trips.critical.flags.valid == 1) { - if (crt == -1) { - tz->trips.critical.flags.valid = 0; - } else if (crt > 0) { - unsigned long crt_k = CELSIUS_TO_DECI_KELVIN(crt); - /* - * Allow override critical threshold - */ - if (crt_k > tz->trips.critical.temperature) - pr_warn(PREFIX "Critical threshold %d C\n", - crt); - tz->trips.critical.temperature = crt_k; - } - } - } - - /* Critical Sleep (optional) */ - if (flag & ACPI_TRIPS_HOT) { - status = acpi_evaluate_integer(tz->device->handle, - "_HOT", NULL, &tmp); - if (ACPI_FAILURE(status)) { - tz->trips.hot.flags.valid = 0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "No hot threshold\n")); - } else { - tz->trips.hot.temperature = tmp; - tz->trips.hot.flags.valid = 1; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Found hot threshold [%lu]\n", - tz->trips.hot.temperature)); - } - } - - /* Passive (optional) */ - if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.flags.valid) || - (flag == ACPI_TRIPS_INIT)) { - valid = tz->trips.passive.flags.valid; - if (psv == -1) { - status = AE_SUPPORT; - } else if (psv > 0) { - tmp = CELSIUS_TO_DECI_KELVIN(psv); - status = AE_OK; - } else { - status = acpi_evaluate_integer(tz->device->handle, - "_PSV", NULL, &tmp); - } - - if (ACPI_FAILURE(status)) - tz->trips.passive.flags.valid = 0; - else { - tz->trips.passive.temperature = tmp; - tz->trips.passive.flags.valid = 1; - if (flag == ACPI_TRIPS_INIT) { - status = acpi_evaluate_integer( - tz->device->handle, "_TC1", - NULL, &tmp); - if (ACPI_FAILURE(status)) - tz->trips.passive.flags.valid = 0; - else - tz->trips.passive.tc1 = tmp; - status = acpi_evaluate_integer( - tz->device->handle, "_TC2", - NULL, &tmp); - if (ACPI_FAILURE(status)) - tz->trips.passive.flags.valid = 0; - else - tz->trips.passive.tc2 = tmp; - status = acpi_evaluate_integer( - tz->device->handle, "_TSP", - NULL, &tmp); - if (ACPI_FAILURE(status)) - tz->trips.passive.flags.valid = 0; - else - tz->trips.passive.tsp = tmp; - } - } - } - if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) { - memset(&devices, 0, sizeof(struct acpi_handle_list)); - status = acpi_evaluate_reference(tz->device->handle, "_PSL", - NULL, &devices); - if (ACPI_FAILURE(status)) { - pr_warn(PREFIX "Invalid passive threshold\n"); - tz->trips.passive.flags.valid = 0; - } - else - tz->trips.passive.flags.valid = 1; - - if (memcmp(&tz->trips.passive.devices, &devices, - sizeof(struct acpi_handle_list))) { - memcpy(&tz->trips.passive.devices, &devices, - sizeof(struct acpi_handle_list)); - ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); - } - } - if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) { - if (valid != tz->trips.passive.flags.valid) - ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state"); - } - - /* Active (optional) */ - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - char name[5] = { '_', 'A', 'C', ('0' + i), '\0' }; - valid = tz->trips.active[i].flags.valid; - - if (act == -1) - break; /* disable all active trip points */ - - if ((flag == ACPI_TRIPS_INIT) || ((flag & ACPI_TRIPS_ACTIVE) && - tz->trips.active[i].flags.valid)) { - status = acpi_evaluate_integer(tz->device->handle, - name, NULL, &tmp); - if (ACPI_FAILURE(status)) { - tz->trips.active[i].flags.valid = 0; - if (i == 0) - break; - if (act <= 0) - break; - if (i == 1) - tz->trips.active[0].temperature = - CELSIUS_TO_DECI_KELVIN(act); - else - /* - * Don't allow override higher than - * the next higher trip point - */ - tz->trips.active[i - 1].temperature = - (tz->trips.active[i - 2].temperature < - CELSIUS_TO_DECI_KELVIN(act) ? - tz->trips.active[i - 2].temperature : - CELSIUS_TO_DECI_KELVIN(act)); - break; - } else { - tz->trips.active[i].temperature = tmp; - tz->trips.active[i].flags.valid = 1; - } - } - - name[2] = 'L'; - if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) { - memset(&devices, 0, sizeof(struct acpi_handle_list)); - status = acpi_evaluate_reference(tz->device->handle, - name, NULL, &devices); - if (ACPI_FAILURE(status)) { - pr_warn(PREFIX "Invalid active%d threshold\n", - i); - tz->trips.active[i].flags.valid = 0; - } - else - tz->trips.active[i].flags.valid = 1; - - if (memcmp(&tz->trips.active[i].devices, &devices, - sizeof(struct acpi_handle_list))) { - memcpy(&tz->trips.active[i].devices, &devices, - sizeof(struct acpi_handle_list)); - ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); - } - } - if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES)) - if (valid != tz->trips.active[i].flags.valid) - ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state"); - - if (!tz->trips.active[i].flags.valid) - break; - } - - if ((flag & ACPI_TRIPS_DEVICES) - && acpi_has_method(tz->device->handle, "_TZD")) { - memset(&devices, 0, sizeof(devices)); - status = acpi_evaluate_reference(tz->device->handle, "_TZD", - NULL, &devices); - if (ACPI_SUCCESS(status) - && memcmp(&tz->devices, &devices, sizeof(devices))) { - tz->devices = devices; - ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); - } - } - - return 0; -} - -static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) -{ - int i, valid, ret = acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT); - - if (ret) - return ret; - - valid = tz->trips.critical.flags.valid | - tz->trips.hot.flags.valid | - tz->trips.passive.flags.valid; - - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) - valid |= tz->trips.active[i].flags.valid; - - if (!valid) { - pr_warn(FW_BUG "No valid trip found\n"); - return -ENODEV; - } - return 0; -} - -static void acpi_thermal_check(void *data) -{ - struct acpi_thermal *tz = data; - - if (!tz->tz_enabled) - return; - - thermal_zone_device_update(tz->thermal_zone); -} - -/* sys I/F for generic thermal sysfs support */ - -static int thermal_get_temp(struct thermal_zone_device *thermal, int *temp) -{ - struct acpi_thermal *tz = thermal->devdata; - int result; - - if (!tz) - return -EINVAL; - - result = acpi_thermal_get_temperature(tz); - if (result) - return result; - - *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(tz->temperature, - tz->kelvin_offset); - return 0; -} - -static int thermal_get_mode(struct thermal_zone_device *thermal, - enum thermal_device_mode *mode) -{ - struct acpi_thermal *tz = thermal->devdata; - - if (!tz) - return -EINVAL; - - *mode = tz->tz_enabled ? THERMAL_DEVICE_ENABLED : - THERMAL_DEVICE_DISABLED; - - return 0; -} - -static int thermal_set_mode(struct thermal_zone_device *thermal, - enum thermal_device_mode mode) -{ - struct acpi_thermal *tz = thermal->devdata; - int enable; - - if (!tz) - return -EINVAL; - - /* - * enable/disable thermal management from ACPI thermal driver - */ - if (mode == THERMAL_DEVICE_ENABLED) - enable = 1; - else if (mode == THERMAL_DEVICE_DISABLED) { - enable = 0; - pr_warn("thermal zone will be disabled\n"); - } else - return -EINVAL; - - if (enable != tz->tz_enabled) { - tz->tz_enabled = enable; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "%s kernel ACPI thermal control\n", - tz->tz_enabled ? "Enable" : "Disable")); - acpi_thermal_check(tz); - } - return 0; -} - -static int thermal_get_trip_type(struct thermal_zone_device *thermal, - int trip, enum thermal_trip_type *type) -{ - struct acpi_thermal *tz = thermal->devdata; - int i; - - if (!tz || trip < 0) - return -EINVAL; - - if (tz->trips.critical.flags.valid) { - if (!trip) { - *type = THERMAL_TRIP_CRITICAL; - return 0; - } - trip--; - } - - if (tz->trips.hot.flags.valid) { - if (!trip) { - *type = THERMAL_TRIP_HOT; - return 0; - } - trip--; - } - - if (tz->trips.passive.flags.valid) { - if (!trip) { - *type = THERMAL_TRIP_PASSIVE; - return 0; - } - trip--; - } - - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && - tz->trips.active[i].flags.valid; i++) { - if (!trip) { - *type = THERMAL_TRIP_ACTIVE; - return 0; - } - trip--; - } - - return -EINVAL; -} - -static int thermal_get_trip_temp(struct thermal_zone_device *thermal, - int trip, int *temp) -{ - struct acpi_thermal *tz = thermal->devdata; - int i; - - if (!tz || trip < 0) - return -EINVAL; - - if (tz->trips.critical.flags.valid) { - if (!trip) { - *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( - tz->trips.critical.temperature, - tz->kelvin_offset); - return 0; - } - trip--; - } - - if (tz->trips.hot.flags.valid) { - if (!trip) { - *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( - tz->trips.hot.temperature, - tz->kelvin_offset); - return 0; - } - trip--; - } - - if (tz->trips.passive.flags.valid) { - if (!trip) { - *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( - tz->trips.passive.temperature, - tz->kelvin_offset); - return 0; - } - trip--; - } - - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && - tz->trips.active[i].flags.valid; i++) { - if (!trip) { - *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( - tz->trips.active[i].temperature, - tz->kelvin_offset); - return 0; - } - trip--; - } - - return -EINVAL; -} - -static int thermal_get_crit_temp(struct thermal_zone_device *thermal, - int *temperature) -{ - struct acpi_thermal *tz = thermal->devdata; - - if (tz->trips.critical.flags.valid) { - *temperature = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( - tz->trips.critical.temperature, - tz->kelvin_offset); - return 0; - } else - return -EINVAL; -} - -static int thermal_get_trend(struct thermal_zone_device *thermal, - int trip, enum thermal_trend *trend) -{ - struct acpi_thermal *tz = thermal->devdata; - enum thermal_trip_type type; - int i; - - if (thermal_get_trip_type(thermal, trip, &type)) - return -EINVAL; - - if (type == THERMAL_TRIP_ACTIVE) { - int trip_temp; - int temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( - tz->temperature, tz->kelvin_offset); - if (thermal_get_trip_temp(thermal, trip, &trip_temp)) - return -EINVAL; - - if (temp > trip_temp) { - *trend = THERMAL_TREND_RAISING; - return 0; - } else { - /* Fall back on default trend */ - return -EINVAL; - } - } - - /* - * tz->temperature has already been updated by generic thermal layer, - * before this callback being invoked - */ - i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature)) - + (tz->trips.passive.tc2 - * (tz->temperature - tz->trips.passive.temperature)); - - if (i > 0) - *trend = THERMAL_TREND_RAISING; - else if (i < 0) - *trend = THERMAL_TREND_DROPPING; - else - *trend = THERMAL_TREND_STABLE; - return 0; -} - - -static int thermal_notify(struct thermal_zone_device *thermal, int trip, - enum thermal_trip_type trip_type) -{ - u8 type = 0; - struct acpi_thermal *tz = thermal->devdata; - - if (trip_type == THERMAL_TRIP_CRITICAL) - type = ACPI_THERMAL_NOTIFY_CRITICAL; - else if (trip_type == THERMAL_TRIP_HOT) - type = ACPI_THERMAL_NOTIFY_HOT; - else - return 0; - - acpi_bus_generate_netlink_event(tz->device->pnp.device_class, - dev_name(&tz->device->dev), type, 1); - - if (trip_type == THERMAL_TRIP_CRITICAL && nocrt) - return 1; - - return 0; -} - -static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev, - bool bind) -{ - struct acpi_device *device = cdev->devdata; - struct acpi_thermal *tz = thermal->devdata; - struct acpi_device *dev; - acpi_status status; - acpi_handle handle; - int i; - int j; - int trip = -1; - int result = 0; - - if (tz->trips.critical.flags.valid) - trip++; - - if (tz->trips.hot.flags.valid) - trip++; - - if (tz->trips.passive.flags.valid) { - trip++; - for (i = 0; i < tz->trips.passive.devices.count; - i++) { - handle = tz->trips.passive.devices.handles[i]; - status = acpi_bus_get_device(handle, &dev); - if (ACPI_FAILURE(status) || dev != device) - continue; - if (bind) - result = - thermal_zone_bind_cooling_device - (thermal, trip, cdev, - THERMAL_NO_LIMIT, THERMAL_NO_LIMIT, - THERMAL_WEIGHT_DEFAULT); - else - result = - thermal_zone_unbind_cooling_device - (thermal, trip, cdev); - if (result) - goto failed; - } - } - - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - if (!tz->trips.active[i].flags.valid) - break; - trip++; - for (j = 0; - j < tz->trips.active[i].devices.count; - j++) { - handle = tz->trips.active[i].devices.handles[j]; - status = acpi_bus_get_device(handle, &dev); - if (ACPI_FAILURE(status) || dev != device) - continue; - if (bind) - result = thermal_zone_bind_cooling_device - (thermal, trip, cdev, - THERMAL_NO_LIMIT, THERMAL_NO_LIMIT, - THERMAL_WEIGHT_DEFAULT); - else - result = thermal_zone_unbind_cooling_device - (thermal, trip, cdev); - if (result) - goto failed; - } - } - - for (i = 0; i < tz->devices.count; i++) { - handle = tz->devices.handles[i]; - status = acpi_bus_get_device(handle, &dev); - if (ACPI_SUCCESS(status) && (dev == device)) { - if (bind) - result = thermal_zone_bind_cooling_device - (thermal, THERMAL_TRIPS_NONE, - cdev, THERMAL_NO_LIMIT, - THERMAL_NO_LIMIT, - THERMAL_WEIGHT_DEFAULT); - else - result = thermal_zone_unbind_cooling_device - (thermal, THERMAL_TRIPS_NONE, - cdev); - if (result) - goto failed; - } - } - -failed: - return result; -} - -static int -acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev) -{ - return acpi_thermal_cooling_device_cb(thermal, cdev, true); -} - -static int -acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev) -{ - return acpi_thermal_cooling_device_cb(thermal, cdev, false); -} - -static struct thermal_zone_device_ops acpi_thermal_zone_ops = { - .bind = acpi_thermal_bind_cooling_device, - .unbind = acpi_thermal_unbind_cooling_device, - .get_temp = thermal_get_temp, - .get_mode = thermal_get_mode, - .set_mode = thermal_set_mode, - .get_trip_type = thermal_get_trip_type, - .get_trip_temp = thermal_get_trip_temp, - .get_crit_temp = thermal_get_crit_temp, - .get_trend = thermal_get_trend, - .notify = thermal_notify, -}; - -static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) -{ - int trips = 0; - int result; - acpi_status status; - int i; - - if (tz->trips.critical.flags.valid) - trips++; - - if (tz->trips.hot.flags.valid) - trips++; - - if (tz->trips.passive.flags.valid) - trips++; - - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && - tz->trips.active[i].flags.valid; i++, trips++); - - if (tz->trips.passive.flags.valid) - tz->thermal_zone = - thermal_zone_device_register("acpitz", trips, 0, tz, - &acpi_thermal_zone_ops, NULL, - tz->trips.passive.tsp*100, - tz->polling_frequency*100); - else - tz->thermal_zone = - thermal_zone_device_register("acpitz", trips, 0, tz, - &acpi_thermal_zone_ops, NULL, - 0, tz->polling_frequency*100); - if (IS_ERR(tz->thermal_zone)) - return -ENODEV; - - result = sysfs_create_link(&tz->device->dev.kobj, - &tz->thermal_zone->device.kobj, "thermal_zone"); - if (result) - return result; - - result = sysfs_create_link(&tz->thermal_zone->device.kobj, - &tz->device->dev.kobj, "device"); - if (result) - return result; - - status = acpi_bus_attach_private_data(tz->device->handle, - tz->thermal_zone); - if (ACPI_FAILURE(status)) - return -ENODEV; - - tz->tz_enabled = 1; - - dev_info(&tz->device->dev, "registered as thermal_zone%d\n", - tz->thermal_zone->id); - return 0; -} - -static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) -{ - sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); - sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); - thermal_zone_device_unregister(tz->thermal_zone); - tz->thermal_zone = NULL; - acpi_bus_detach_private_data(tz->device->handle); -} - - -/* -------------------------------------------------------------------------- - Driver Interface - -------------------------------------------------------------------------- */ - -static void acpi_thermal_notify(struct acpi_device *device, u32 event) -{ - struct acpi_thermal *tz = acpi_driver_data(device); - - - if (!tz) - return; - - switch (event) { - case ACPI_THERMAL_NOTIFY_TEMPERATURE: - acpi_thermal_check(tz); - break; - case ACPI_THERMAL_NOTIFY_THRESHOLDS: - acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS); - acpi_thermal_check(tz); - acpi_bus_generate_netlink_event(device->pnp.device_class, - dev_name(&device->dev), event, 0); - break; - case ACPI_THERMAL_NOTIFY_DEVICES: - acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES); - acpi_thermal_check(tz); - acpi_bus_generate_netlink_event(device->pnp.device_class, - dev_name(&device->dev), event, 0); - break; - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); - break; - } -} - -/* - * On some platforms, the AML code has dependency about - * the evaluating order of _TMP and _CRT/_HOT/_PSV/_ACx. - * 1. On HP Pavilion G4-1016tx, _TMP must be invoked after - * /_CRT/_HOT/_PSV/_ACx, or else system will be power off. - * 2. On HP Compaq 6715b/6715s, the return value of _PSV is 0 - * if _TMP has never been evaluated. - * - * As this dependency is totally transparent to OS, evaluate - * all of them once, in the order of _CRT/_HOT/_PSV/_ACx, - * _TMP, before they are actually used. - */ -static void acpi_thermal_aml_dependency_fix(struct acpi_thermal *tz) -{ - acpi_handle handle = tz->device->handle; - unsigned long long value; - int i; - - acpi_evaluate_integer(handle, "_CRT", NULL, &value); - acpi_evaluate_integer(handle, "_HOT", NULL, &value); - acpi_evaluate_integer(handle, "_PSV", NULL, &value); - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - char name[5] = { '_', 'A', 'C', ('0' + i), '\0' }; - acpi_status status; - - status = acpi_evaluate_integer(handle, name, NULL, &value); - if (status == AE_NOT_FOUND) - break; - } - acpi_evaluate_integer(handle, "_TMP", NULL, &value); -} - -static int acpi_thermal_get_info(struct acpi_thermal *tz) -{ - int result = 0; - - - if (!tz) - return -EINVAL; - - acpi_thermal_aml_dependency_fix(tz); - - /* Get trip points [_CRT, _PSV, etc.] (required) */ - result = acpi_thermal_get_trip_points(tz); - if (result) - return result; - - /* Get temperature [_TMP] (required) */ - result = acpi_thermal_get_temperature(tz); - if (result) - return result; - - /* Set the cooling mode [_SCP] to active cooling (default) */ - result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE); - if (!result) - tz->flags.cooling_mode = 1; - - /* Get default polling frequency [_TZP] (optional) */ - if (tzp) - tz->polling_frequency = tzp; - else - acpi_thermal_get_polling_frequency(tz); - - return 0; -} - -/* - * The exact offset between Kelvin and degree Celsius is 273.15. However ACPI - * handles temperature values with a single decimal place. As a consequence, - * some implementations use an offset of 273.1 and others use an offset of - * 273.2. Try to find out which one is being used, to present the most - * accurate and visually appealing number. - * - * The heuristic below should work for all ACPI thermal zones which have a - * critical trip point with a value being a multiple of 0.5 degree Celsius. - */ -static void acpi_thermal_guess_offset(struct acpi_thermal *tz) -{ - if (tz->trips.critical.flags.valid && - (tz->trips.critical.temperature % 5) == 1) - tz->kelvin_offset = 2731; - else - tz->kelvin_offset = 2732; -} - -static void acpi_thermal_check_fn(struct work_struct *work) -{ - struct acpi_thermal *tz = container_of(work, struct acpi_thermal, - thermal_check_work); - acpi_thermal_check(tz); -} - -static int acpi_thermal_add(struct acpi_device *device) -{ - int result = 0; - struct acpi_thermal *tz = NULL; - - - if (!device) - return -EINVAL; - - tz = kzalloc(sizeof(struct acpi_thermal), GFP_KERNEL); - if (!tz) - return -ENOMEM; - - tz->device = device; - strcpy(tz->name, device->pnp.bus_id); - strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS); - device->driver_data = tz; - - result = acpi_thermal_get_info(tz); - if (result) - goto free_memory; - - acpi_thermal_guess_offset(tz); - - result = acpi_thermal_register_thermal_zone(tz); - if (result) - goto free_memory; - - INIT_WORK(&tz->thermal_check_work, acpi_thermal_check_fn); - - pr_info(PREFIX "%s [%s] (%ld C)\n", acpi_device_name(device), - acpi_device_bid(device), DECI_KELVIN_TO_CELSIUS(tz->temperature)); - goto end; - -free_memory: - kfree(tz); -end: - return result; -} - -static int acpi_thermal_remove(struct acpi_device *device) -{ - struct acpi_thermal *tz = NULL; - - if (!device || !acpi_driver_data(device)) - return -EINVAL; - - flush_workqueue(acpi_thermal_pm_queue); - tz = acpi_driver_data(device); - - acpi_thermal_unregister_thermal_zone(tz); - kfree(tz); - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int acpi_thermal_suspend(struct device *dev) -{ - /* Make sure the previously queued thermal check work has been done */ - flush_workqueue(acpi_thermal_pm_queue); - return 0; -} - -static int acpi_thermal_resume(struct device *dev) -{ - struct acpi_thermal *tz; - int i, j, power_state, result; - - if (!dev) - return -EINVAL; - - tz = acpi_driver_data(to_acpi_device(dev)); - if (!tz) - return -EINVAL; - - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - if (!(&tz->trips.active[i])) - break; - if (!tz->trips.active[i].flags.valid) - break; - tz->trips.active[i].flags.enabled = 1; - for (j = 0; j < tz->trips.active[i].devices.count; j++) { - result = acpi_bus_update_power( - tz->trips.active[i].devices.handles[j], - &power_state); - if (result || (power_state != ACPI_STATE_D0)) { - tz->trips.active[i].flags.enabled = 0; - break; - } - } - tz->state.active |= tz->trips.active[i].flags.enabled; - } - - queue_work(acpi_thermal_pm_queue, &tz->thermal_check_work); - - return AE_OK; -} -#endif - -static int thermal_act(const struct dmi_system_id *d) { - - if (act == 0) { - pr_notice(PREFIX "%s detected: " - "disabling all active thermal trip points\n", d->ident); - act = -1; - } - return 0; -} -static int thermal_nocrt(const struct dmi_system_id *d) { - - pr_notice(PREFIX "%s detected: " - "disabling all critical thermal trip point actions.\n", d->ident); - nocrt = 1; - return 0; -} -static int thermal_tzp(const struct dmi_system_id *d) { - - if (tzp == 0) { - pr_notice(PREFIX "%s detected: " - "enabling thermal zone polling\n", d->ident); - tzp = 300; /* 300 dS = 30 Seconds */ - } - return 0; -} -static int thermal_psv(const struct dmi_system_id *d) { - - if (psv == 0) { - pr_notice(PREFIX "%s detected: " - "disabling all passive thermal trip points\n", d->ident); - psv = -1; - } - return 0; -} - -static struct dmi_system_id thermal_dmi_table[] __initdata = { - /* - * Award BIOS on this AOpen makes thermal control almost worthless. - * http://bugzilla.kernel.org/show_bug.cgi?id=8842 - */ - { - .callback = thermal_act, - .ident = "AOpen i915GMm-HFS", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), - DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), - }, - }, - { - .callback = thermal_psv, - .ident = "AOpen i915GMm-HFS", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), - DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), - }, - }, - { - .callback = thermal_tzp, - .ident = "AOpen i915GMm-HFS", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), - DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), - }, - }, - { - .callback = thermal_nocrt, - .ident = "Gigabyte GA-7ZX", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."), - DMI_MATCH(DMI_BOARD_NAME, "7ZX"), - }, - }, - {} -}; - -static int __init acpi_thermal_init(void) -{ - int result = 0; - - dmi_check_system(thermal_dmi_table); - - if (off) { - pr_notice(PREFIX "thermal control disabled\n"); - return -ENODEV; - } - - acpi_thermal_pm_queue = create_workqueue("acpi_thermal_pm"); - if (!acpi_thermal_pm_queue) - return -ENODEV; - - result = acpi_bus_register_driver(&acpi_thermal_driver); - if (result < 0) { - destroy_workqueue(acpi_thermal_pm_queue); - return -ENODEV; - } - - return 0; -} - -static void __exit acpi_thermal_exit(void) -{ - acpi_bus_unregister_driver(&acpi_thermal_driver); - destroy_workqueue(acpi_thermal_pm_queue); - - return; -} - -module_init(acpi_thermal_init); -module_exit(acpi_thermal_exit); diff --git a/src/4.x/drivers/cdrom/Makefile b/src/4.x/drivers/cdrom/Makefile deleted file mode 100644 index 05c18204e..000000000 --- a/src/4.x/drivers/cdrom/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# Makefile for the kernel cdrom device drivers. -# -# 30 Jan 1998, Michael Elizabeth Chastain, -# Rewritten to use lists instead of if-statements. - -# Each configuration option enables a list of files. - -#obj-$(CONFIG_BLK_DEV_IDECD) += cdrom.o -obj-$(CONFIG_BLK_DEV_SR) += cdrom.o -#obj-$(CONFIG_PARIDE_PCD) += cdrom.o -#obj-$(CONFIG_CDROM_PKTCDVD) += cdrom.o - -#obj-$(CONFIG_GDROM) += gdrom.o cdrom.o diff --git a/src/4.x/drivers/cdrom/cdrom.c b/src/4.x/drivers/cdrom/cdrom.c deleted file mode 100644 index aee23092f..000000000 --- a/src/4.x/drivers/cdrom/cdrom.c +++ /dev/null @@ -1,3729 +0,0 @@ -/* linux/drivers/cdrom/cdrom.c - Copyright (c) 1996, 1997 David A. van Leeuwen. - Copyright (c) 1997, 1998 Erik Andersen - Copyright (c) 1998, 1999 Jens Axboe - - May be copied or modified under the terms of the GNU General Public - License. See linux/COPYING for more information. - - Uniform CD-ROM driver for Linux. - See Documentation/cdrom/cdrom-standard.tex for usage information. - - The routines in the file provide a uniform interface between the - software that uses CD-ROMs and the various low-level drivers that - actually talk to the hardware. Suggestions are welcome. - Patches that work are more welcome though. ;-) - - To Do List: - ---------------------------------- - - -- Modify sysctl/proc interface. I plan on having one directory per - drive, with entries for outputing general drive information, and sysctl - based tunable parameters such as whether the tray should auto-close for - that drive. Suggestions (or patches) for this welcome! - - - Revision History - ---------------------------------- - 1.00 Date Unknown -- David van Leeuwen - -- Initial version by David A. van Leeuwen. I don't have a detailed - changelog for the 1.x series, David? - -2.00 Dec 2, 1997 -- Erik Andersen - -- New maintainer! As David A. van Leeuwen has been too busy to actively - maintain and improve this driver, I am now carrying on the torch. If - you have a problem with this driver, please feel free to contact me. - - -- Added (rudimentary) sysctl interface. I realize this is really weak - right now, and is _very_ badly implemented. It will be improved... - - -- Modified CDROM_DISC_STATUS so that it is now incorporated into - the Uniform CD-ROM driver via the cdrom_count_tracks function. - The cdrom_count_tracks function helps resolve some of the false - assumptions of the CDROM_DISC_STATUS ioctl, and is also used to check - for the correct media type when mounting or playing audio from a CD. - - -- Remove the calls to verify_area and only use the copy_from_user and - copy_to_user stuff, since these calls now provide their own memory - checking with the 2.1.x kernels. - - -- Major update to return codes so that errors from low-level drivers - are passed on through (thanks to Gerd Knorr for pointing out this - problem). - - -- Made it so if a function isn't implemented in a low-level driver, - ENOSYS is now returned instead of EINVAL. - - -- Simplified some complex logic so that the source code is easier to read. - - -- Other stuff I probably forgot to mention (lots of changes). - -2.01 to 2.11 Dec 1997-Jan 1998 - -- TO-DO! Write changelogs for 2.01 to 2.12. - -2.12 Jan 24, 1998 -- Erik Andersen - -- Fixed a bug in the IOCTL_IN and IOCTL_OUT macros. It turns out that - copy_*_user does not return EFAULT on error, but instead returns the number - of bytes not copied. I was returning whatever non-zero stuff came back from - the copy_*_user functions directly, which would result in strange errors. - -2.13 July 17, 1998 -- Erik Andersen - -- Fixed a bug in CDROM_SELECT_SPEED where you couldn't lower the speed - of the drive. Thanks to Tobias Ringstr|m for pointing - this out and providing a simple fix. - -- Fixed the procfs-unload-module bug with the fill_inode procfs callback. - thanks to Andrea Arcangeli - -- Fixed it so that the /proc entry now also shows up when cdrom is - compiled into the kernel. Before it only worked when loaded as a module. - - 2.14 August 17, 1998 -- Erik Andersen - -- Fixed a bug in cdrom_media_changed and handling of reporting that - the media had changed for devices that _don't_ implement media_changed. - Thanks to Grant R. Guenther for spotting this bug. - -- Made a few things more pedanticly correct. - -2.50 Oct 19, 1998 - Jens Axboe - -- New maintainers! Erik was too busy to continue the work on the driver, - so now Chris Zwilling and Jens Axboe - will do their best to follow in his footsteps - - 2.51 Dec 20, 1998 - Jens Axboe - -- Check if drive is capable of doing what we ask before blindly changing - cdi->options in various ioctl. - -- Added version to proc entry. - - 2.52 Jan 16, 1999 - Jens Axboe - -- Fixed an error in open_for_data where we would sometimes not return - the correct error value. Thanks Huba Gaspar . - -- Fixed module usage count - usage was based on /proc/sys/dev - instead of /proc/sys/dev/cdrom. This could lead to an oops when other - modules had entries in dev. Feb 02 - real bug was in sysctl.c where - dev would be removed even though it was used. cdrom.c just illuminated - that bug. - - 2.53 Feb 22, 1999 - Jens Axboe - -- Fixup of several ioctl calls, in particular CDROM_SET_OPTIONS has - been "rewritten" because capabilities and options aren't in sync. They - should be... - -- Added CDROM_LOCKDOOR ioctl. Locks the door and keeps it that way. - -- Added CDROM_RESET ioctl. - -- Added CDROM_DEBUG ioctl. Enable debug messages on-the-fly. - -- Added CDROM_GET_CAPABILITY ioctl. This relieves userspace programs - from parsing /proc/sys/dev/cdrom/info. - - 2.54 Mar 15, 1999 - Jens Axboe - -- Check capability mask from low level driver when counting tracks as - per suggestion from Corey J. Scotts . - - 2.55 Apr 25, 1999 - Jens Axboe - -- autoclose was mistakenly checked against CDC_OPEN_TRAY instead of - CDC_CLOSE_TRAY. - -- proc info didn't mask against capabilities mask. - - 3.00 Aug 5, 1999 - Jens Axboe - -- Unified audio ioctl handling across CD-ROM drivers. A lot of the - code was duplicated before. Drives that support the generic packet - interface are now being fed packets from here instead. - -- First attempt at adding support for MMC2 commands - for DVD and - CD-R(W) drives. Only the DVD parts are in now - the interface used is - the same as for the audio ioctls. - -- ioctl cleanups. if a drive couldn't play audio, it didn't get - a change to perform device specific ioctls as well. - -- Defined CDROM_CAN(CDC_XXX) for checking the capabilities. - -- Put in sysctl files for autoclose, autoeject, check_media, debug, - and lock. - -- /proc/sys/dev/cdrom/info has been updated to also contain info about - CD-Rx and DVD capabilities. - -- Now default to checking media type. - -- CDROM_SEND_PACKET ioctl added. The infrastructure was in place for - doing this anyway, with the generic_packet addition. - - 3.01 Aug 6, 1999 - Jens Axboe - -- Fix up the sysctl handling so that the option flags get set - correctly. - -- Fix up ioctl handling so the device specific ones actually get - called :). - - 3.02 Aug 8, 1999 - Jens Axboe - -- Fixed volume control on SCSI drives (or others with longer audio - page). - -- Fixed a couple of DVD minors. Thanks to Andrew T. Veliath - for telling me and for having defined the various - DVD structures and ioctls in the first place! He designed the original - DVD patches for ide-cd and while I rearranged and unified them, the - interface is still the same. - - 3.03 Sep 1, 1999 - Jens Axboe - -- Moved the rest of the audio ioctls from the CD-ROM drivers here. Only - CDROMREADTOCENTRY and CDROMREADTOCHDR are left. - -- Moved the CDROMREADxxx ioctls in here. - -- Defined the cdrom_get_last_written and cdrom_get_next_block as ioctls - and exported functions. - -- Erik Andersen modified all SCMD_ commands - to now read GPCMD_ for the new generic packet interface. All low level - drivers are updated as well. - -- Various other cleanups. - - 3.04 Sep 12, 1999 - Jens Axboe - -- Fixed a couple of possible memory leaks (if an operation failed and - we didn't free the buffer before returning the error). - -- Integrated Uniform CD Changer handling from Richard Sharman - . - -- Defined CD_DVD and CD_CHANGER log levels. - -- Fixed the CDROMREADxxx ioctls. - -- CDROMPLAYTRKIND uses the GPCMD_PLAY_AUDIO_MSF command - too few - drives supported it. We lose the index part, however. - -- Small modifications to accommodate opens of /dev/hdc1, required - for ide-cd to handle multisession discs. - -- Export cdrom_mode_sense and cdrom_mode_select. - -- init_cdrom_command() for setting up a cgc command. - - 3.05 Oct 24, 1999 - Jens Axboe - -- Changed the interface for CDROM_SEND_PACKET. Before it was virtually - impossible to send the drive data in a sensible way. - -- Lowered stack usage in mmc_ioctl(), dvd_read_disckey(), and - dvd_read_manufact. - -- Added setup of write mode for packet writing. - -- Fixed CDDA ripping with cdda2wav - accept much larger requests of - number of frames and split the reads in blocks of 8. - - 3.06 Dec 13, 1999 - Jens Axboe - -- Added support for changing the region of DVD drives. - -- Added sense data to generic command. - - 3.07 Feb 2, 2000 - Jens Axboe - -- Do same "read header length" trick in cdrom_get_disc_info() as - we do in cdrom_get_track_info() -- some drive don't obey specs and - fail if they can't supply the full Mt Fuji size table. - -- Deleted stuff related to setting up write modes. It has a different - home now. - -- Clear header length in mode_select unconditionally. - -- Removed the register_disk() that was added, not needed here. - - 3.08 May 1, 2000 - Jens Axboe - -- Fix direction flag in setup_send_key and setup_report_key. This - gave some SCSI adapters problems. - -- Always return -EROFS for write opens - -- Convert to module_init/module_exit style init and remove some - of the #ifdef MODULE stuff - -- Fix several dvd errors - DVD_LU_SEND_ASF should pass agid, - DVD_HOST_SEND_RPC_STATE did not set buffer size in cdb, and - dvd_do_auth passed uninitialized data to drive because init_cdrom_command - did not clear a 0 sized buffer. - - 3.09 May 12, 2000 - Jens Axboe - -- Fix Video-CD on SCSI drives that don't support READ_CD command. In - that case switch block size and issue plain READ_10 again, then switch - back. - - 3.10 Jun 10, 2000 - Jens Axboe - -- Fix volume control on CD's - old SCSI-II drives now use their own - code, as doing MODE6 stuff in here is really not my intention. - -- Use READ_DISC_INFO for more reliable end-of-disc. - - 3.11 Jun 12, 2000 - Jens Axboe - -- Fix bug in getting rpc phase 2 region info. - -- Reinstate "correct" CDROMPLAYTRKIND - - 3.12 Oct 18, 2000 - Jens Axboe - -- Use quiet bit on packet commands not known to work - - 3.20 Dec 17, 2003 - Jens Axboe - -- Various fixes and lots of cleanups not listed :-) - -- Locking fixes - -- Mt Rainier support - -- DVD-RAM write open fixes - - Nov 5 2001, Aug 8 2002. Modified by Andy Polyakov - to support MMC-3 compliant DVD+RW units. - - Modified by Nigel Kukard - support DVD+RW - 2.4.x patch by Andy Polyakov - --------------------------------------------------------------------------*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define REVISION "Revision: 3.20" -#define VERSION "Id: cdrom.c 3.20 2003/12/17" - -/* I use an error-log mask to give fine grain control over the type of - messages dumped to the system logs. The available masks include: */ -#define CD_NOTHING 0x0 -#define CD_WARNING 0x1 -#define CD_REG_UNREG 0x2 -#define CD_DO_IOCTL 0x4 -#define CD_OPEN 0x8 -#define CD_CLOSE 0x10 -#define CD_COUNT_TRACKS 0x20 -#define CD_CHANGER 0x40 -#define CD_DVD 0x80 - -/* Define this to remove _all_ the debugging messages */ -/* #define ERRLOGMASK CD_NOTHING */ -#define ERRLOGMASK CD_WARNING -/* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_COUNT_TRACKS|CD_CLOSE) */ -/* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE|CD_COUNT_TRACKS) */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* used to tell the module to turn on full debugging messages */ -static bool debug; -/* default compatibility mode */ -static bool autoclose=1; -static bool autoeject; -static bool lockdoor = 1; -/* will we ever get to use this... sigh. */ -static bool check_media_type; -/* automatically restart mrw format */ -static bool mrw_format_restart = 1; -module_param(debug, bool, 0); -module_param(autoclose, bool, 0); -module_param(autoeject, bool, 0); -module_param(lockdoor, bool, 0); -module_param(check_media_type, bool, 0); -module_param(mrw_format_restart, bool, 0); - -static DEFINE_MUTEX(cdrom_mutex); - -static const char *mrw_format_status[] = { - "not mrw", - "bgformat inactive", - "bgformat active", - "mrw complete", -}; - -static const char *mrw_address_space[] = { "DMA", "GAA" }; - -#if (ERRLOGMASK != CD_NOTHING) -#define cd_dbg(type, fmt, ...) \ -do { \ - if ((ERRLOGMASK & type) || debug == 1) \ - pr_debug(fmt, ##__VA_ARGS__); \ -} while (0) -#else -#define cd_dbg(type, fmt, ...) \ -do { \ - if (0 && (ERRLOGMASK & type) || debug == 1) \ - pr_debug(fmt, ##__VA_ARGS__); \ -} while (0) -#endif - -/* The (cdo->capability & ~cdi->mask & CDC_XXX) construct was used in - a lot of places. This macro makes the code more clear. */ -#define CDROM_CAN(type) (cdi->ops->capability & ~cdi->mask & (type)) - -/* - * Another popular OS uses 7 seconds as the hard timeout for default - * commands, so it is a good choice for us as well. - */ -#define CDROM_DEF_TIMEOUT (7 * HZ) - -/* Not-exported routines. */ - -static void cdrom_sysctl_register(void); - -static LIST_HEAD(cdrom_list); - -static int cdrom_dummy_generic_packet(struct cdrom_device_info *cdi, - struct packet_command *cgc) -{ - if (cgc->sense) { - cgc->sense->sense_key = 0x05; - cgc->sense->asc = 0x20; - cgc->sense->ascq = 0x00; - } - - cgc->stat = -EIO; - return -EIO; -} - -static int cdrom_flush_cache(struct cdrom_device_info *cdi) -{ - struct packet_command cgc; - - init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); - cgc.cmd[0] = GPCMD_FLUSH_CACHE; - - cgc.timeout = 5 * 60 * HZ; - - return cdi->ops->generic_packet(cdi, &cgc); -} - -/* requires CD R/RW */ -static int cdrom_get_disc_info(struct cdrom_device_info *cdi, - disc_information *di) -{ - struct cdrom_device_ops *cdo = cdi->ops; - struct packet_command cgc; - int ret, buflen; - - /* set up command and get the disc info */ - init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_DISC_INFO; - cgc.cmd[8] = cgc.buflen = 2; - cgc.quiet = 1; - - ret = cdo->generic_packet(cdi, &cgc); - if (ret) - return ret; - - /* not all drives have the same disc_info length, so requeue - * packet with the length the drive tells us it can supply - */ - buflen = be16_to_cpu(di->disc_information_length) + - sizeof(di->disc_information_length); - - if (buflen > sizeof(disc_information)) - buflen = sizeof(disc_information); - - cgc.cmd[8] = cgc.buflen = buflen; - ret = cdo->generic_packet(cdi, &cgc); - if (ret) - return ret; - - /* return actual fill size */ - return buflen; -} - -/* This macro makes sure we don't have to check on cdrom_device_ops - * existence in the run-time routines below. Change_capability is a - * hack to have the capability flags defined const, while we can still - * change it here without gcc complaining at every line. - */ -#define ENSURE(call, bits) \ -do { \ - if (cdo->call == NULL) \ - *change_capability &= ~(bits); \ -} while (0) - -/* - * the first prototypes used 0x2c as the page code for the mrw mode page, - * subsequently this was changed to 0x03. probe the one used by this drive - */ -static int cdrom_mrw_probe_pc(struct cdrom_device_info *cdi) -{ - struct packet_command cgc; - char buffer[16]; - - init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); - - cgc.timeout = HZ; - cgc.quiet = 1; - - if (!cdrom_mode_sense(cdi, &cgc, MRW_MODE_PC, 0)) { - cdi->mrw_mode_page = MRW_MODE_PC; - return 0; - } else if (!cdrom_mode_sense(cdi, &cgc, MRW_MODE_PC_PRE1, 0)) { - cdi->mrw_mode_page = MRW_MODE_PC_PRE1; - return 0; - } - - return 1; -} - -static int cdrom_is_mrw(struct cdrom_device_info *cdi, int *write) -{ - struct packet_command cgc; - struct mrw_feature_desc *mfd; - unsigned char buffer[16]; - int ret; - - *write = 0; - - init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); - - cgc.cmd[0] = GPCMD_GET_CONFIGURATION; - cgc.cmd[3] = CDF_MRW; - cgc.cmd[8] = sizeof(buffer); - cgc.quiet = 1; - - if ((ret = cdi->ops->generic_packet(cdi, &cgc))) - return ret; - - mfd = (struct mrw_feature_desc *)&buffer[sizeof(struct feature_header)]; - if (be16_to_cpu(mfd->feature_code) != CDF_MRW) - return 1; - *write = mfd->write; - - if ((ret = cdrom_mrw_probe_pc(cdi))) { - *write = 0; - return ret; - } - - return 0; -} - -static int cdrom_mrw_bgformat(struct cdrom_device_info *cdi, int cont) -{ - struct packet_command cgc; - unsigned char buffer[12]; - int ret; - - pr_info("%sstarting format\n", cont ? "Re" : ""); - - /* - * FmtData bit set (bit 4), format type is 1 - */ - init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_WRITE); - cgc.cmd[0] = GPCMD_FORMAT_UNIT; - cgc.cmd[1] = (1 << 4) | 1; - - cgc.timeout = 5 * 60 * HZ; - - /* - * 4 byte format list header, 8 byte format list descriptor - */ - buffer[1] = 1 << 1; - buffer[3] = 8; - - /* - * nr_blocks field - */ - buffer[4] = 0xff; - buffer[5] = 0xff; - buffer[6] = 0xff; - buffer[7] = 0xff; - - buffer[8] = 0x24 << 2; - buffer[11] = cont; - - ret = cdi->ops->generic_packet(cdi, &cgc); - if (ret) - pr_info("bgformat failed\n"); - - return ret; -} - -static int cdrom_mrw_bgformat_susp(struct cdrom_device_info *cdi, int immed) -{ - struct packet_command cgc; - - init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); - cgc.cmd[0] = GPCMD_CLOSE_TRACK; - - /* - * Session = 1, Track = 0 - */ - cgc.cmd[1] = !!immed; - cgc.cmd[2] = 1 << 1; - - cgc.timeout = 5 * 60 * HZ; - - return cdi->ops->generic_packet(cdi, &cgc); -} - -static int cdrom_mrw_exit(struct cdrom_device_info *cdi) -{ - disc_information di; - int ret; - - ret = cdrom_get_disc_info(cdi, &di); - if (ret < 0 || ret < (int)offsetof(typeof(di),disc_type)) - return 1; - - ret = 0; - if (di.mrw_status == CDM_MRW_BGFORMAT_ACTIVE) { - pr_info("issuing MRW background format suspend\n"); - ret = cdrom_mrw_bgformat_susp(cdi, 0); - } - - if (!ret && cdi->media_written) - ret = cdrom_flush_cache(cdi); - - return ret; -} - -static int cdrom_mrw_set_lba_space(struct cdrom_device_info *cdi, int space) -{ - struct packet_command cgc; - struct mode_page_header *mph; - char buffer[16]; - int ret, offset, size; - - init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); - - cgc.buffer = buffer; - cgc.buflen = sizeof(buffer); - - ret = cdrom_mode_sense(cdi, &cgc, cdi->mrw_mode_page, 0); - if (ret) - return ret; - - mph = (struct mode_page_header *)buffer; - offset = be16_to_cpu(mph->desc_length); - size = be16_to_cpu(mph->mode_data_length) + 2; - - buffer[offset + 3] = space; - cgc.buflen = size; - - ret = cdrom_mode_select(cdi, &cgc); - if (ret) - return ret; - - pr_info("%s: mrw address space %s selected\n", - cdi->name, mrw_address_space[space]); - return 0; -} - -int register_cdrom(struct cdrom_device_info *cdi) -{ - static char banner_printed; - struct cdrom_device_ops *cdo = cdi->ops; - int *change_capability = (int *)&cdo->capability; /* hack */ - - cd_dbg(CD_OPEN, "entering register_cdrom\n"); - - if (cdo->open == NULL || cdo->release == NULL) - return -EINVAL; - if (!banner_printed) { - pr_info("Uniform CD-ROM driver " REVISION "\n"); - banner_printed = 1; - cdrom_sysctl_register(); - } - - ENSURE(drive_status, CDC_DRIVE_STATUS); - if (cdo->check_events == NULL && cdo->media_changed == NULL) - *change_capability = ~(CDC_MEDIA_CHANGED | CDC_SELECT_DISC); - ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY); - ENSURE(lock_door, CDC_LOCK); - ENSURE(select_speed, CDC_SELECT_SPEED); - ENSURE(get_last_session, CDC_MULTI_SESSION); - ENSURE(get_mcn, CDC_MCN); - ENSURE(reset, CDC_RESET); - ENSURE(generic_packet, CDC_GENERIC_PACKET); - cdi->mc_flags = 0; - cdo->n_minors = 0; - cdi->options = CDO_USE_FFLAGS; - - if (autoclose == 1 && CDROM_CAN(CDC_CLOSE_TRAY)) - cdi->options |= (int) CDO_AUTO_CLOSE; - if (autoeject == 1 && CDROM_CAN(CDC_OPEN_TRAY)) - cdi->options |= (int) CDO_AUTO_EJECT; - if (lockdoor == 1) - cdi->options |= (int) CDO_LOCK; - if (check_media_type == 1) - cdi->options |= (int) CDO_CHECK_TYPE; - - if (CDROM_CAN(CDC_MRW_W)) - cdi->exit = cdrom_mrw_exit; - - if (cdi->disk) - cdi->cdda_method = CDDA_BPC_FULL; - else - cdi->cdda_method = CDDA_OLD; - - if (!cdo->generic_packet) - cdo->generic_packet = cdrom_dummy_generic_packet; - - cd_dbg(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); - mutex_lock(&cdrom_mutex); - list_add(&cdi->list, &cdrom_list); - mutex_unlock(&cdrom_mutex); - return 0; -} -#undef ENSURE - -void unregister_cdrom(struct cdrom_device_info *cdi) -{ - cd_dbg(CD_OPEN, "entering unregister_cdrom\n"); - - mutex_lock(&cdrom_mutex); - list_del(&cdi->list); - mutex_unlock(&cdrom_mutex); - - if (cdi->exit) - cdi->exit(cdi); - - cdi->ops->n_minors--; - cd_dbg(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name); -} - -int cdrom_get_media_event(struct cdrom_device_info *cdi, - struct media_event_desc *med) -{ - struct packet_command cgc; - unsigned char buffer[8]; - struct event_header *eh = (struct event_header *)buffer; - - init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); - cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION; - cgc.cmd[1] = 1; /* IMMED */ - cgc.cmd[4] = 1 << 4; /* media event */ - cgc.cmd[8] = sizeof(buffer); - cgc.quiet = 1; - - if (cdi->ops->generic_packet(cdi, &cgc)) - return 1; - - if (be16_to_cpu(eh->data_len) < sizeof(*med)) - return 1; - - if (eh->nea || eh->notification_class != 0x4) - return 1; - - memcpy(med, &buffer[sizeof(*eh)], sizeof(*med)); - return 0; -} - -static int cdrom_get_random_writable(struct cdrom_device_info *cdi, - struct rwrt_feature_desc *rfd) -{ - struct packet_command cgc; - char buffer[24]; - int ret; - - init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); - - cgc.cmd[0] = GPCMD_GET_CONFIGURATION; /* often 0x46 */ - cgc.cmd[3] = CDF_RWRT; /* often 0x0020 */ - cgc.cmd[8] = sizeof(buffer); /* often 0x18 */ - cgc.quiet = 1; - - if ((ret = cdi->ops->generic_packet(cdi, &cgc))) - return ret; - - memcpy(rfd, &buffer[sizeof(struct feature_header)], sizeof (*rfd)); - return 0; -} - -static int cdrom_has_defect_mgt(struct cdrom_device_info *cdi) -{ - struct packet_command cgc; - char buffer[16]; - __be16 *feature_code; - int ret; - - init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); - - cgc.cmd[0] = GPCMD_GET_CONFIGURATION; - cgc.cmd[3] = CDF_HWDM; - cgc.cmd[8] = sizeof(buffer); - cgc.quiet = 1; - - if ((ret = cdi->ops->generic_packet(cdi, &cgc))) - return ret; - - feature_code = (__be16 *) &buffer[sizeof(struct feature_header)]; - if (be16_to_cpu(*feature_code) == CDF_HWDM) - return 0; - - return 1; -} - - -static int cdrom_is_random_writable(struct cdrom_device_info *cdi, int *write) -{ - struct rwrt_feature_desc rfd; - int ret; - - *write = 0; - - if ((ret = cdrom_get_random_writable(cdi, &rfd))) - return ret; - - if (CDF_RWRT == be16_to_cpu(rfd.feature_code)) - *write = 1; - - return 0; -} - -static int cdrom_media_erasable(struct cdrom_device_info *cdi) -{ - disc_information di; - int ret; - - ret = cdrom_get_disc_info(cdi, &di); - if (ret < 0 || ret < offsetof(typeof(di), n_first_track)) - return -1; - - return di.erasable; -} - -/* - * FIXME: check RO bit - */ -static int cdrom_dvdram_open_write(struct cdrom_device_info *cdi) -{ - int ret = cdrom_media_erasable(cdi); - - /* - * allow writable open if media info read worked and media is - * erasable, _or_ if it fails since not all drives support it - */ - if (!ret) - return 1; - - return 0; -} - -static int cdrom_mrw_open_write(struct cdrom_device_info *cdi) -{ - disc_information di; - int ret; - - /* - * always reset to DMA lba space on open - */ - if (cdrom_mrw_set_lba_space(cdi, MRW_LBA_DMA)) { - pr_err("failed setting lba address space\n"); - return 1; - } - - ret = cdrom_get_disc_info(cdi, &di); - if (ret < 0 || ret < offsetof(typeof(di),disc_type)) - return 1; - - if (!di.erasable) - return 1; - - /* - * mrw_status - * 0 - not MRW formatted - * 1 - MRW bgformat started, but not running or complete - * 2 - MRW bgformat in progress - * 3 - MRW formatting complete - */ - ret = 0; - pr_info("open: mrw_status '%s'\n", mrw_format_status[di.mrw_status]); - if (!di.mrw_status) - ret = 1; - else if (di.mrw_status == CDM_MRW_BGFORMAT_INACTIVE && - mrw_format_restart) - ret = cdrom_mrw_bgformat(cdi, 1); - - return ret; -} - -static int mo_open_write(struct cdrom_device_info *cdi) -{ - struct packet_command cgc; - char buffer[255]; - int ret; - - init_cdrom_command(&cgc, &buffer, 4, CGC_DATA_READ); - cgc.quiet = 1; - - /* - * obtain write protect information as per - * drivers/scsi/sd.c:sd_read_write_protect_flag - */ - - ret = cdrom_mode_sense(cdi, &cgc, GPMODE_ALL_PAGES, 0); - if (ret) - ret = cdrom_mode_sense(cdi, &cgc, GPMODE_VENDOR_PAGE, 0); - if (ret) { - cgc.buflen = 255; - ret = cdrom_mode_sense(cdi, &cgc, GPMODE_ALL_PAGES, 0); - } - - /* drive gave us no info, let the user go ahead */ - if (ret) - return 0; - - return buffer[3] & 0x80; -} - -static int cdrom_ram_open_write(struct cdrom_device_info *cdi) -{ - struct rwrt_feature_desc rfd; - int ret; - - if ((ret = cdrom_has_defect_mgt(cdi))) - return ret; - - if ((ret = cdrom_get_random_writable(cdi, &rfd))) - return ret; - else if (CDF_RWRT == be16_to_cpu(rfd.feature_code)) - ret = !rfd.curr; - - cd_dbg(CD_OPEN, "can open for random write\n"); - return ret; -} - -static void cdrom_mmc3_profile(struct cdrom_device_info *cdi) -{ - struct packet_command cgc; - char buffer[32]; - int ret, mmc3_profile; - - init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); - - cgc.cmd[0] = GPCMD_GET_CONFIGURATION; - cgc.cmd[1] = 0; - cgc.cmd[2] = cgc.cmd[3] = 0; /* Starting Feature Number */ - cgc.cmd[8] = sizeof(buffer); /* Allocation Length */ - cgc.quiet = 1; - - if ((ret = cdi->ops->generic_packet(cdi, &cgc))) - mmc3_profile = 0xffff; - else - mmc3_profile = (buffer[6] << 8) | buffer[7]; - - cdi->mmc3_profile = mmc3_profile; -} - -static int cdrom_is_dvd_rw(struct cdrom_device_info *cdi) -{ - switch (cdi->mmc3_profile) { - case 0x12: /* DVD-RAM */ - case 0x1A: /* DVD+RW */ - case 0x43: /* BD-RE */ - return 0; - default: - return 1; - } -} - -/* - * returns 0 for ok to open write, non-0 to disallow - */ -static int cdrom_open_write(struct cdrom_device_info *cdi) -{ - int mrw, mrw_write, ram_write; - int ret = 1; - - mrw = 0; - if (!cdrom_is_mrw(cdi, &mrw_write)) - mrw = 1; - - if (CDROM_CAN(CDC_MO_DRIVE)) - ram_write = 1; - else - (void) cdrom_is_random_writable(cdi, &ram_write); - - if (mrw) - cdi->mask &= ~CDC_MRW; - else - cdi->mask |= CDC_MRW; - - if (mrw_write) - cdi->mask &= ~CDC_MRW_W; - else - cdi->mask |= CDC_MRW_W; - - if (ram_write) - cdi->mask &= ~CDC_RAM; - else - cdi->mask |= CDC_RAM; - - if (CDROM_CAN(CDC_MRW_W)) - ret = cdrom_mrw_open_write(cdi); - else if (CDROM_CAN(CDC_DVD_RAM)) - ret = cdrom_dvdram_open_write(cdi); - else if (CDROM_CAN(CDC_RAM) && - !CDROM_CAN(CDC_CD_R|CDC_CD_RW|CDC_DVD|CDC_DVD_R|CDC_MRW|CDC_MO_DRIVE)) - ret = cdrom_ram_open_write(cdi); - else if (CDROM_CAN(CDC_MO_DRIVE)) - ret = mo_open_write(cdi); - else if (!cdrom_is_dvd_rw(cdi)) - ret = 0; - - return ret; -} - -static void cdrom_dvd_rw_close_write(struct cdrom_device_info *cdi) -{ - struct packet_command cgc; - - if (cdi->mmc3_profile != 0x1a) { - cd_dbg(CD_CLOSE, "%s: No DVD+RW\n", cdi->name); - return; - } - - if (!cdi->media_written) { - cd_dbg(CD_CLOSE, "%s: DVD+RW media clean\n", cdi->name); - return; - } - - pr_info("%s: dirty DVD+RW media, \"finalizing\"\n", cdi->name); - - init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); - cgc.cmd[0] = GPCMD_FLUSH_CACHE; - cgc.timeout = 30*HZ; - cdi->ops->generic_packet(cdi, &cgc); - - init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); - cgc.cmd[0] = GPCMD_CLOSE_TRACK; - cgc.timeout = 3000*HZ; - cgc.quiet = 1; - cdi->ops->generic_packet(cdi, &cgc); - - init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); - cgc.cmd[0] = GPCMD_CLOSE_TRACK; - cgc.cmd[2] = 2; /* Close session */ - cgc.quiet = 1; - cgc.timeout = 3000*HZ; - cdi->ops->generic_packet(cdi, &cgc); - - cdi->media_written = 0; -} - -static int cdrom_close_write(struct cdrom_device_info *cdi) -{ -#if 0 - return cdrom_flush_cache(cdi); -#else - return 0; -#endif -} - -/* badly broken, I know. Is due for a fixup anytime. */ -static void cdrom_count_tracks(struct cdrom_device_info *cdi, tracktype *tracks) -{ - struct cdrom_tochdr header; - struct cdrom_tocentry entry; - int ret, i; - tracks->data = 0; - tracks->audio = 0; - tracks->cdi = 0; - tracks->xa = 0; - tracks->error = 0; - cd_dbg(CD_COUNT_TRACKS, "entering cdrom_count_tracks\n"); - /* Grab the TOC header so we can see how many tracks there are */ - ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header); - if (ret) { - if (ret == -ENOMEDIUM) - tracks->error = CDS_NO_DISC; - else - tracks->error = CDS_NO_INFO; - return; - } - /* check what type of tracks are on this disc */ - entry.cdte_format = CDROM_MSF; - for (i = header.cdth_trk0; i <= header.cdth_trk1; i++) { - entry.cdte_track = i; - if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry)) { - tracks->error = CDS_NO_INFO; - return; - } - if (entry.cdte_ctrl & CDROM_DATA_TRACK) { - if (entry.cdte_format == 0x10) - tracks->cdi++; - else if (entry.cdte_format == 0x20) - tracks->xa++; - else - tracks->data++; - } else { - tracks->audio++; - } - cd_dbg(CD_COUNT_TRACKS, "track %d: format=%d, ctrl=%d\n", - i, entry.cdte_format, entry.cdte_ctrl); - } - cd_dbg(CD_COUNT_TRACKS, "disc has %d tracks: %d=audio %d=data %d=Cd-I %d=XA\n", - header.cdth_trk1, tracks->audio, tracks->data, - tracks->cdi, tracks->xa); -} - -static -int open_for_data(struct cdrom_device_info *cdi) -{ - int ret; - struct cdrom_device_ops *cdo = cdi->ops; - tracktype tracks; - cd_dbg(CD_OPEN, "entering open_for_data\n"); - /* Check if the driver can report drive status. If it can, we - can do clever things. If it can't, well, we at least tried! */ - if (cdo->drive_status != NULL) { - ret = cdo->drive_status(cdi, CDSL_CURRENT); - cd_dbg(CD_OPEN, "drive_status=%d\n", ret); - if (ret == CDS_TRAY_OPEN) { - cd_dbg(CD_OPEN, "the tray is open...\n"); - /* can/may i close it? */ - if (CDROM_CAN(CDC_CLOSE_TRAY) && - cdi->options & CDO_AUTO_CLOSE) { - cd_dbg(CD_OPEN, "trying to close the tray\n"); - ret=cdo->tray_move(cdi,0); - if (ret) { - cd_dbg(CD_OPEN, "bummer. tried to close the tray but failed.\n"); - /* Ignore the error from the low - level driver. We don't care why it - couldn't close the tray. We only care - that there is no disc in the drive, - since that is the _REAL_ problem here.*/ - ret=-ENOMEDIUM; - goto clean_up_and_return; - } - } else { - cd_dbg(CD_OPEN, "bummer. this drive can't close the tray.\n"); - ret=-ENOMEDIUM; - goto clean_up_and_return; - } - /* Ok, the door should be closed now.. Check again */ - ret = cdo->drive_status(cdi, CDSL_CURRENT); - if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) { - cd_dbg(CD_OPEN, "bummer. the tray is still not closed.\n"); - cd_dbg(CD_OPEN, "tray might not contain a medium\n"); - ret=-ENOMEDIUM; - goto clean_up_and_return; - } - cd_dbg(CD_OPEN, "the tray is now closed\n"); - } - /* the door should be closed now, check for the disc */ - ret = cdo->drive_status(cdi, CDSL_CURRENT); - if (ret!=CDS_DISC_OK) { - ret = -ENOMEDIUM; - goto clean_up_and_return; - } - } - cdrom_count_tracks(cdi, &tracks); - if (tracks.error == CDS_NO_DISC) { - cd_dbg(CD_OPEN, "bummer. no disc.\n"); - ret=-ENOMEDIUM; - goto clean_up_and_return; - } - /* CD-Players which don't use O_NONBLOCK, workman - * for example, need bit CDO_CHECK_TYPE cleared! */ - if (tracks.data==0) { - if (cdi->options & CDO_CHECK_TYPE) { - /* give people a warning shot, now that CDO_CHECK_TYPE - is the default case! */ - cd_dbg(CD_OPEN, "bummer. wrong media type.\n"); - cd_dbg(CD_WARNING, "pid %d must open device O_NONBLOCK!\n", - (unsigned int)task_pid_nr(current)); - ret=-EMEDIUMTYPE; - goto clean_up_and_return; - } - else { - cd_dbg(CD_OPEN, "wrong media type, but CDO_CHECK_TYPE not set\n"); - } - } - - cd_dbg(CD_OPEN, "all seems well, opening the devicen"); - - /* all seems well, we can open the device */ - ret = cdo->open(cdi, 0); /* open for data */ - cd_dbg(CD_OPEN, "opening the device gave me %d\n", ret); - /* After all this careful checking, we shouldn't have problems - opening the device, but we don't want the device locked if - this somehow fails... */ - if (ret) { - cd_dbg(CD_OPEN, "open device failed\n"); - goto clean_up_and_return; - } - if (CDROM_CAN(CDC_LOCK) && (cdi->options & CDO_LOCK)) { - cdo->lock_door(cdi, 1); - cd_dbg(CD_OPEN, "door locked\n"); - } - cd_dbg(CD_OPEN, "device opened successfully\n"); - return ret; - - /* Something failed. Try to unlock the drive, because some drivers - (notably ide-cd) lock the drive after every command. This produced - a nasty bug where after mount failed, the drive would remain locked! - This ensures that the drive gets unlocked after a mount fails. This - is a goto to avoid bloating the driver with redundant code. */ -clean_up_and_return: - cd_dbg(CD_OPEN, "open failed\n"); - if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) { - cdo->lock_door(cdi, 0); - cd_dbg(CD_OPEN, "door unlocked\n"); - } - return ret; -} - -/* We use the open-option O_NONBLOCK to indicate that the - * purpose of opening is only for subsequent ioctl() calls; no device - * integrity checks are performed. - * - * We hope that all cd-player programs will adopt this convention. It - * is in their own interest: device control becomes a lot easier - * this way. - */ -int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev, - fmode_t mode) -{ - int ret; - - cd_dbg(CD_OPEN, "entering cdrom_open\n"); - - /* if this was a O_NONBLOCK open and we should honor the flags, - * do a quick open without drive/disc integrity checks. */ - cdi->use_count++; - if ((mode & FMODE_NDELAY) && (cdi->options & CDO_USE_FFLAGS)) { - ret = cdi->ops->open(cdi, 1); - } else { - ret = open_for_data(cdi); - if (ret) - goto err; - cdrom_mmc3_profile(cdi); - if (mode & FMODE_WRITE) { - ret = -EROFS; - if (cdrom_open_write(cdi)) - goto err_release; - if (!CDROM_CAN(CDC_RAM)) - goto err_release; - ret = 0; - cdi->media_written = 0; - } - } - - if (ret) - goto err; - - cd_dbg(CD_OPEN, "Use count for \"/dev/%s\" now %d\n", - cdi->name, cdi->use_count); - return 0; -err_release: - if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) { - cdi->ops->lock_door(cdi, 0); - cd_dbg(CD_OPEN, "door unlocked\n"); - } - cdi->ops->release(cdi); -err: - cdi->use_count--; - return ret; -} - -/* This code is similar to that in open_for_data. The routine is called - whenever an audio play operation is requested. -*/ -static int check_for_audio_disc(struct cdrom_device_info * cdi, - struct cdrom_device_ops * cdo) -{ - int ret; - tracktype tracks; - cd_dbg(CD_OPEN, "entering check_for_audio_disc\n"); - if (!(cdi->options & CDO_CHECK_TYPE)) - return 0; - if (cdo->drive_status != NULL) { - ret = cdo->drive_status(cdi, CDSL_CURRENT); - cd_dbg(CD_OPEN, "drive_status=%d\n", ret); - if (ret == CDS_TRAY_OPEN) { - cd_dbg(CD_OPEN, "the tray is open...\n"); - /* can/may i close it? */ - if (CDROM_CAN(CDC_CLOSE_TRAY) && - cdi->options & CDO_AUTO_CLOSE) { - cd_dbg(CD_OPEN, "trying to close the tray\n"); - ret=cdo->tray_move(cdi,0); - if (ret) { - cd_dbg(CD_OPEN, "bummer. tried to close tray but failed.\n"); - /* Ignore the error from the low - level driver. We don't care why it - couldn't close the tray. We only care - that there is no disc in the drive, - since that is the _REAL_ problem here.*/ - return -ENOMEDIUM; - } - } else { - cd_dbg(CD_OPEN, "bummer. this driver can't close the tray.\n"); - return -ENOMEDIUM; - } - /* Ok, the door should be closed now.. Check again */ - ret = cdo->drive_status(cdi, CDSL_CURRENT); - if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) { - cd_dbg(CD_OPEN, "bummer. the tray is still not closed.\n"); - return -ENOMEDIUM; - } - if (ret!=CDS_DISC_OK) { - cd_dbg(CD_OPEN, "bummer. disc isn't ready.\n"); - return -EIO; - } - cd_dbg(CD_OPEN, "the tray is now closed\n"); - } - } - cdrom_count_tracks(cdi, &tracks); - if (tracks.error) - return(tracks.error); - - if (tracks.audio==0) - return -EMEDIUMTYPE; - - return 0; -} - -void cdrom_release(struct cdrom_device_info *cdi, fmode_t mode) -{ - struct cdrom_device_ops *cdo = cdi->ops; - int opened_for_data; - - cd_dbg(CD_CLOSE, "entering cdrom_release\n"); - - if (cdi->use_count > 0) - cdi->use_count--; - - if (cdi->use_count == 0) { - cd_dbg(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", - cdi->name); - cdrom_dvd_rw_close_write(cdi); - - if ((cdo->capability & CDC_LOCK) && !cdi->keeplocked) { - cd_dbg(CD_CLOSE, "Unlocking door!\n"); - cdo->lock_door(cdi, 0); - } - } - - opened_for_data = !(cdi->options & CDO_USE_FFLAGS) || - !(mode & FMODE_NDELAY); - - /* - * flush cache on last write release - */ - if (CDROM_CAN(CDC_RAM) && !cdi->use_count && cdi->for_data) - cdrom_close_write(cdi); - - cdo->release(cdi); - if (cdi->use_count == 0) { /* last process that closes dev*/ - if (opened_for_data && - cdi->options & CDO_AUTO_EJECT && CDROM_CAN(CDC_OPEN_TRAY)) - cdo->tray_move(cdi, 1); - } -} - -static int cdrom_read_mech_status(struct cdrom_device_info *cdi, - struct cdrom_changer_info *buf) -{ - struct packet_command cgc; - struct cdrom_device_ops *cdo = cdi->ops; - int length; - - /* - * Sanyo changer isn't spec compliant (doesn't use regular change - * LOAD_UNLOAD command, and it doesn't implement the mech status - * command below - */ - if (cdi->sanyo_slot) { - buf->hdr.nslots = 3; - buf->hdr.curslot = cdi->sanyo_slot == 3 ? 0 : cdi->sanyo_slot; - for (length = 0; length < 3; length++) { - buf->slots[length].disc_present = 1; - buf->slots[length].change = 0; - } - return 0; - } - - length = sizeof(struct cdrom_mechstat_header) + - cdi->capacity * sizeof(struct cdrom_slot); - - init_cdrom_command(&cgc, buf, length, CGC_DATA_READ); - cgc.cmd[0] = GPCMD_MECHANISM_STATUS; - cgc.cmd[8] = (length >> 8) & 0xff; - cgc.cmd[9] = length & 0xff; - return cdo->generic_packet(cdi, &cgc); -} - -static int cdrom_slot_status(struct cdrom_device_info *cdi, int slot) -{ - struct cdrom_changer_info *info; - int ret; - - cd_dbg(CD_CHANGER, "entering cdrom_slot_status()\n"); - if (cdi->sanyo_slot) - return CDS_NO_INFO; - - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - if ((ret = cdrom_read_mech_status(cdi, info))) - goto out_free; - - if (info->slots[slot].disc_present) - ret = CDS_DISC_OK; - else - ret = CDS_NO_DISC; - -out_free: - kfree(info); - return ret; -} - -/* Return the number of slots for an ATAPI/SCSI cdrom, - * return 1 if not a changer. - */ -int cdrom_number_of_slots(struct cdrom_device_info *cdi) -{ - int status; - int nslots = 1; - struct cdrom_changer_info *info; - - cd_dbg(CD_CHANGER, "entering cdrom_number_of_slots()\n"); - /* cdrom_read_mech_status requires a valid value for capacity: */ - cdi->capacity = 0; - - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - if ((status = cdrom_read_mech_status(cdi, info)) == 0) - nslots = info->hdr.nslots; - - kfree(info); - return nslots; -} - - -/* If SLOT < 0, unload the current slot. Otherwise, try to load SLOT. */ -static int cdrom_load_unload(struct cdrom_device_info *cdi, int slot) -{ - struct packet_command cgc; - - cd_dbg(CD_CHANGER, "entering cdrom_load_unload()\n"); - if (cdi->sanyo_slot && slot < 0) - return 0; - - init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); - cgc.cmd[0] = GPCMD_LOAD_UNLOAD; - cgc.cmd[4] = 2 + (slot >= 0); - cgc.cmd[8] = slot; - cgc.timeout = 60 * HZ; - - /* The Sanyo 3 CD changer uses byte 7 of the - GPCMD_TEST_UNIT_READY to command to switch CDs instead of - using the GPCMD_LOAD_UNLOAD opcode. */ - if (cdi->sanyo_slot && -1 < slot) { - cgc.cmd[0] = GPCMD_TEST_UNIT_READY; - cgc.cmd[7] = slot; - cgc.cmd[4] = cgc.cmd[8] = 0; - cdi->sanyo_slot = slot ? slot : 3; - } - - return cdi->ops->generic_packet(cdi, &cgc); -} - -static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot) -{ - struct cdrom_changer_info *info; - int curslot; - int ret; - - cd_dbg(CD_CHANGER, "entering cdrom_select_disc()\n"); - if (!CDROM_CAN(CDC_SELECT_DISC)) - return -EDRIVE_CANT_DO_THIS; - - if (cdi->ops->check_events) - cdi->ops->check_events(cdi, 0, slot); - else - cdi->ops->media_changed(cdi, slot); - - if (slot == CDSL_NONE) { - /* set media changed bits, on both queues */ - cdi->mc_flags = 0x3; - return cdrom_load_unload(cdi, -1); - } - - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - if ((ret = cdrom_read_mech_status(cdi, info))) { - kfree(info); - return ret; - } - - curslot = info->hdr.curslot; - kfree(info); - - if (cdi->use_count > 1 || cdi->keeplocked) { - if (slot == CDSL_CURRENT) { - return curslot; - } else { - return -EBUSY; - } - } - - /* Specifying CDSL_CURRENT will attempt to load the currnet slot, - which is useful if it had been previously unloaded. - Whether it can or not, it returns the current slot. - Similarly, if slot happens to be the current one, we still - try and load it. */ - if (slot == CDSL_CURRENT) - slot = curslot; - - /* set media changed bits on both queues */ - cdi->mc_flags = 0x3; - if ((ret = cdrom_load_unload(cdi, slot))) - return ret; - - return slot; -} - -/* - * As cdrom implements an extra ioctl consumer for media changed - * event, it needs to buffer ->check_events() output, such that event - * is not lost for both the usual VFS and ioctl paths. - * cdi->{vfs|ioctl}_events are used to buffer pending events for each - * path. - * - * XXX: Locking is non-existent. cdi->ops->check_events() can be - * called in parallel and buffering fields are accessed without any - * exclusion. The original media_changed code had the same problem. - * It might be better to simply deprecate CDROM_MEDIA_CHANGED ioctl - * and remove this cruft altogether. It doesn't have much usefulness - * at this point. - */ -static void cdrom_update_events(struct cdrom_device_info *cdi, - unsigned int clearing) -{ - unsigned int events; - - events = cdi->ops->check_events(cdi, clearing, CDSL_CURRENT); - cdi->vfs_events |= events; - cdi->ioctl_events |= events; -} - -unsigned int cdrom_check_events(struct cdrom_device_info *cdi, - unsigned int clearing) -{ - unsigned int events; - - cdrom_update_events(cdi, clearing); - events = cdi->vfs_events; - cdi->vfs_events = 0; - return events; -} -EXPORT_SYMBOL(cdrom_check_events); - -/* We want to make media_changed accessible to the user through an - * ioctl. The main problem now is that we must double-buffer the - * low-level implementation, to assure that the VFS and the user both - * see a medium change once. - */ - -static -int media_changed(struct cdrom_device_info *cdi, int queue) -{ - unsigned int mask = (1 << (queue & 1)); - int ret = !!(cdi->mc_flags & mask); - bool changed; - - if (!CDROM_CAN(CDC_MEDIA_CHANGED)) - return ret; - - /* changed since last call? */ - if (cdi->ops->check_events) { - BUG_ON(!queue); /* shouldn't be called from VFS path */ - cdrom_update_events(cdi, DISK_EVENT_MEDIA_CHANGE); - changed = cdi->ioctl_events & DISK_EVENT_MEDIA_CHANGE; - cdi->ioctl_events = 0; - } else - changed = cdi->ops->media_changed(cdi, CDSL_CURRENT); - - if (changed) { - cdi->mc_flags = 0x3; /* set bit on both queues */ - ret |= 1; - cdi->media_written = 0; - } - - cdi->mc_flags &= ~mask; /* clear bit */ - return ret; -} - -int cdrom_media_changed(struct cdrom_device_info *cdi) -{ - /* This talks to the VFS, which doesn't like errors - just 1 or 0. - * Returning "0" is always safe (media hasn't been changed). Do that - * if the low-level cdrom driver dosn't support media changed. */ - if (cdi == NULL || cdi->ops->media_changed == NULL) - return 0; - if (!CDROM_CAN(CDC_MEDIA_CHANGED)) - return 0; - return media_changed(cdi, 0); -} - -/* Requests to the low-level drivers will /always/ be done in the - following format convention: - - CDROM_LBA: all data-related requests. - CDROM_MSF: all audio-related requests. - - However, a low-level implementation is allowed to refuse this - request, and return information in its own favorite format. - - It doesn't make sense /at all/ to ask for a play_audio in LBA - format, or ask for multi-session info in MSF format. However, for - backward compatibility these format requests will be satisfied, but - the requests to the low-level drivers will be sanitized in the more - meaningful format indicated above. - */ - -static -void sanitize_format(union cdrom_addr *addr, - u_char * curr, u_char requested) -{ - if (*curr == requested) - return; /* nothing to be done! */ - if (requested == CDROM_LBA) { - addr->lba = (int) addr->msf.frame + - 75 * (addr->msf.second - 2 + 60 * addr->msf.minute); - } else { /* CDROM_MSF */ - int lba = addr->lba; - addr->msf.frame = lba % 75; - lba /= 75; - lba += 2; - addr->msf.second = lba % 60; - addr->msf.minute = lba / 60; - } - *curr = requested; -} - -void init_cdrom_command(struct packet_command *cgc, void *buf, int len, - int type) -{ - memset(cgc, 0, sizeof(struct packet_command)); - if (buf) - memset(buf, 0, len); - cgc->buffer = (char *) buf; - cgc->buflen = len; - cgc->data_direction = type; - cgc->timeout = CDROM_DEF_TIMEOUT; -} - -/* DVD handling */ - -#define copy_key(dest,src) memcpy((dest), (src), sizeof(dvd_key)) -#define copy_chal(dest,src) memcpy((dest), (src), sizeof(dvd_challenge)) - -static void setup_report_key(struct packet_command *cgc, unsigned agid, unsigned type) -{ - cgc->cmd[0] = GPCMD_REPORT_KEY; - cgc->cmd[10] = type | (agid << 6); - switch (type) { - case 0: case 8: case 5: { - cgc->buflen = 8; - break; - } - case 1: { - cgc->buflen = 16; - break; - } - case 2: case 4: { - cgc->buflen = 12; - break; - } - } - cgc->cmd[9] = cgc->buflen; - cgc->data_direction = CGC_DATA_READ; -} - -static void setup_send_key(struct packet_command *cgc, unsigned agid, unsigned type) -{ - cgc->cmd[0] = GPCMD_SEND_KEY; - cgc->cmd[10] = type | (agid << 6); - switch (type) { - case 1: { - cgc->buflen = 16; - break; - } - case 3: { - cgc->buflen = 12; - break; - } - case 6: { - cgc->buflen = 8; - break; - } - } - cgc->cmd[9] = cgc->buflen; - cgc->data_direction = CGC_DATA_WRITE; -} - -static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) -{ - int ret; - u_char buf[20]; - struct packet_command cgc; - struct cdrom_device_ops *cdo = cdi->ops; - rpc_state_t rpc_state; - - memset(buf, 0, sizeof(buf)); - init_cdrom_command(&cgc, buf, 0, CGC_DATA_READ); - - switch (ai->type) { - /* LU data send */ - case DVD_LU_SEND_AGID: - cd_dbg(CD_DVD, "entering DVD_LU_SEND_AGID\n"); - cgc.quiet = 1; - setup_report_key(&cgc, ai->lsa.agid, 0); - - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - - ai->lsa.agid = buf[7] >> 6; - /* Returning data, let host change state */ - break; - - case DVD_LU_SEND_KEY1: - cd_dbg(CD_DVD, "entering DVD_LU_SEND_KEY1\n"); - setup_report_key(&cgc, ai->lsk.agid, 2); - - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - - copy_key(ai->lsk.key, &buf[4]); - /* Returning data, let host change state */ - break; - - case DVD_LU_SEND_CHALLENGE: - cd_dbg(CD_DVD, "entering DVD_LU_SEND_CHALLENGE\n"); - setup_report_key(&cgc, ai->lsc.agid, 1); - - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - - copy_chal(ai->lsc.chal, &buf[4]); - /* Returning data, let host change state */ - break; - - /* Post-auth key */ - case DVD_LU_SEND_TITLE_KEY: - cd_dbg(CD_DVD, "entering DVD_LU_SEND_TITLE_KEY\n"); - cgc.quiet = 1; - setup_report_key(&cgc, ai->lstk.agid, 4); - cgc.cmd[5] = ai->lstk.lba; - cgc.cmd[4] = ai->lstk.lba >> 8; - cgc.cmd[3] = ai->lstk.lba >> 16; - cgc.cmd[2] = ai->lstk.lba >> 24; - - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - - ai->lstk.cpm = (buf[4] >> 7) & 1; - ai->lstk.cp_sec = (buf[4] >> 6) & 1; - ai->lstk.cgms = (buf[4] >> 4) & 3; - copy_key(ai->lstk.title_key, &buf[5]); - /* Returning data, let host change state */ - break; - - case DVD_LU_SEND_ASF: - cd_dbg(CD_DVD, "entering DVD_LU_SEND_ASF\n"); - setup_report_key(&cgc, ai->lsasf.agid, 5); - - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - - ai->lsasf.asf = buf[7] & 1; - break; - - /* LU data receive (LU changes state) */ - case DVD_HOST_SEND_CHALLENGE: - cd_dbg(CD_DVD, "entering DVD_HOST_SEND_CHALLENGE\n"); - setup_send_key(&cgc, ai->hsc.agid, 1); - buf[1] = 0xe; - copy_chal(&buf[4], ai->hsc.chal); - - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - - ai->type = DVD_LU_SEND_KEY1; - break; - - case DVD_HOST_SEND_KEY2: - cd_dbg(CD_DVD, "entering DVD_HOST_SEND_KEY2\n"); - setup_send_key(&cgc, ai->hsk.agid, 3); - buf[1] = 0xa; - copy_key(&buf[4], ai->hsk.key); - - if ((ret = cdo->generic_packet(cdi, &cgc))) { - ai->type = DVD_AUTH_FAILURE; - return ret; - } - ai->type = DVD_AUTH_ESTABLISHED; - break; - - /* Misc */ - case DVD_INVALIDATE_AGID: - cgc.quiet = 1; - cd_dbg(CD_DVD, "entering DVD_INVALIDATE_AGID\n"); - setup_report_key(&cgc, ai->lsa.agid, 0x3f); - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - break; - - /* Get region settings */ - case DVD_LU_SEND_RPC_STATE: - cd_dbg(CD_DVD, "entering DVD_LU_SEND_RPC_STATE\n"); - setup_report_key(&cgc, 0, 8); - memset(&rpc_state, 0, sizeof(rpc_state_t)); - cgc.buffer = (char *) &rpc_state; - - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - - ai->lrpcs.type = rpc_state.type_code; - ai->lrpcs.vra = rpc_state.vra; - ai->lrpcs.ucca = rpc_state.ucca; - ai->lrpcs.region_mask = rpc_state.region_mask; - ai->lrpcs.rpc_scheme = rpc_state.rpc_scheme; - break; - - /* Set region settings */ - case DVD_HOST_SEND_RPC_STATE: - cd_dbg(CD_DVD, "entering DVD_HOST_SEND_RPC_STATE\n"); - setup_send_key(&cgc, 0, 6); - buf[1] = 6; - buf[4] = ai->hrpcs.pdrc; - - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - break; - - default: - cd_dbg(CD_WARNING, "Invalid DVD key ioctl (%d)\n", ai->type); - return -ENOTTY; - } - - return 0; -} - -static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s, - struct packet_command *cgc) -{ - unsigned char buf[21], *base; - struct dvd_layer *layer; - struct cdrom_device_ops *cdo = cdi->ops; - int ret, layer_num = s->physical.layer_num; - - if (layer_num >= DVD_LAYERS) - return -EINVAL; - - init_cdrom_command(cgc, buf, sizeof(buf), CGC_DATA_READ); - cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc->cmd[6] = layer_num; - cgc->cmd[7] = s->type; - cgc->cmd[9] = cgc->buflen & 0xff; - - /* - * refrain from reporting errors on non-existing layers (mainly) - */ - cgc->quiet = 1; - - ret = cdo->generic_packet(cdi, cgc); - if (ret) - return ret; - - base = &buf[4]; - layer = &s->physical.layer[layer_num]; - - /* - * place the data... really ugly, but at least we won't have to - * worry about endianess in userspace. - */ - memset(layer, 0, sizeof(*layer)); - layer->book_version = base[0] & 0xf; - layer->book_type = base[0] >> 4; - layer->min_rate = base[1] & 0xf; - layer->disc_size = base[1] >> 4; - layer->layer_type = base[2] & 0xf; - layer->track_path = (base[2] >> 4) & 1; - layer->nlayers = (base[2] >> 5) & 3; - layer->track_density = base[3] & 0xf; - layer->linear_density = base[3] >> 4; - layer->start_sector = base[5] << 16 | base[6] << 8 | base[7]; - layer->end_sector = base[9] << 16 | base[10] << 8 | base[11]; - layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15]; - layer->bca = base[16] >> 7; - - return 0; -} - -static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s, - struct packet_command *cgc) -{ - int ret; - u_char buf[8]; - struct cdrom_device_ops *cdo = cdi->ops; - - init_cdrom_command(cgc, buf, sizeof(buf), CGC_DATA_READ); - cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc->cmd[6] = s->copyright.layer_num; - cgc->cmd[7] = s->type; - cgc->cmd[8] = cgc->buflen >> 8; - cgc->cmd[9] = cgc->buflen & 0xff; - - ret = cdo->generic_packet(cdi, cgc); - if (ret) - return ret; - - s->copyright.cpst = buf[4]; - s->copyright.rmi = buf[5]; - - return 0; -} - -static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s, - struct packet_command *cgc) -{ - int ret, size; - u_char *buf; - struct cdrom_device_ops *cdo = cdi->ops; - - size = sizeof(s->disckey.value) + 4; - - buf = kmalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - init_cdrom_command(cgc, buf, size, CGC_DATA_READ); - cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc->cmd[7] = s->type; - cgc->cmd[8] = size >> 8; - cgc->cmd[9] = size & 0xff; - cgc->cmd[10] = s->disckey.agid << 6; - - ret = cdo->generic_packet(cdi, cgc); - if (!ret) - memcpy(s->disckey.value, &buf[4], sizeof(s->disckey.value)); - - kfree(buf); - return ret; -} - -static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s, - struct packet_command *cgc) -{ - int ret, size = 4 + 188; - u_char *buf; - struct cdrom_device_ops *cdo = cdi->ops; - - buf = kmalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - init_cdrom_command(cgc, buf, size, CGC_DATA_READ); - cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc->cmd[7] = s->type; - cgc->cmd[9] = cgc->buflen & 0xff; - - ret = cdo->generic_packet(cdi, cgc); - if (ret) - goto out; - - s->bca.len = buf[0] << 8 | buf[1]; - if (s->bca.len < 12 || s->bca.len > 188) { - cd_dbg(CD_WARNING, "Received invalid BCA length (%d)\n", - s->bca.len); - ret = -EIO; - goto out; - } - memcpy(s->bca.value, &buf[4], s->bca.len); - ret = 0; -out: - kfree(buf); - return ret; -} - -static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s, - struct packet_command *cgc) -{ - int ret = 0, size; - u_char *buf; - struct cdrom_device_ops *cdo = cdi->ops; - - size = sizeof(s->manufact.value) + 4; - - buf = kmalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - init_cdrom_command(cgc, buf, size, CGC_DATA_READ); - cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc->cmd[7] = s->type; - cgc->cmd[8] = size >> 8; - cgc->cmd[9] = size & 0xff; - - ret = cdo->generic_packet(cdi, cgc); - if (ret) - goto out; - - s->manufact.len = buf[0] << 8 | buf[1]; - if (s->manufact.len < 0) { - cd_dbg(CD_WARNING, "Received invalid manufacture info length (%d)\n", - s->manufact.len); - ret = -EIO; - } else { - if (s->manufact.len > 2048) { - cd_dbg(CD_WARNING, "Received invalid manufacture info length (%d): truncating to 2048\n", - s->manufact.len); - s->manufact.len = 2048; - } - memcpy(s->manufact.value, &buf[4], s->manufact.len); - } - -out: - kfree(buf); - return ret; -} - -static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s, - struct packet_command *cgc) -{ - switch (s->type) { - case DVD_STRUCT_PHYSICAL: - return dvd_read_physical(cdi, s, cgc); - - case DVD_STRUCT_COPYRIGHT: - return dvd_read_copyright(cdi, s, cgc); - - case DVD_STRUCT_DISCKEY: - return dvd_read_disckey(cdi, s, cgc); - - case DVD_STRUCT_BCA: - return dvd_read_bca(cdi, s, cgc); - - case DVD_STRUCT_MANUFACT: - return dvd_read_manufact(cdi, s, cgc); - - default: - cd_dbg(CD_WARNING, ": Invalid DVD structure read requested (%d)\n", - s->type); - return -EINVAL; - } -} - -int cdrom_mode_sense(struct cdrom_device_info *cdi, - struct packet_command *cgc, - int page_code, int page_control) -{ - struct cdrom_device_ops *cdo = cdi->ops; - - memset(cgc->cmd, 0, sizeof(cgc->cmd)); - - cgc->cmd[0] = GPCMD_MODE_SENSE_10; - cgc->cmd[2] = page_code | (page_control << 6); - cgc->cmd[7] = cgc->buflen >> 8; - cgc->cmd[8] = cgc->buflen & 0xff; - cgc->data_direction = CGC_DATA_READ; - return cdo->generic_packet(cdi, cgc); -} - -int cdrom_mode_select(struct cdrom_device_info *cdi, - struct packet_command *cgc) -{ - struct cdrom_device_ops *cdo = cdi->ops; - - memset(cgc->cmd, 0, sizeof(cgc->cmd)); - memset(cgc->buffer, 0, 2); - cgc->cmd[0] = GPCMD_MODE_SELECT_10; - cgc->cmd[1] = 0x10; /* PF */ - cgc->cmd[7] = cgc->buflen >> 8; - cgc->cmd[8] = cgc->buflen & 0xff; - cgc->data_direction = CGC_DATA_WRITE; - return cdo->generic_packet(cdi, cgc); -} - -static int cdrom_read_subchannel(struct cdrom_device_info *cdi, - struct cdrom_subchnl *subchnl, int mcn) -{ - struct cdrom_device_ops *cdo = cdi->ops; - struct packet_command cgc; - char buffer[32]; - int ret; - - init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_SUBCHANNEL; - cgc.cmd[1] = 2; /* MSF addressing */ - cgc.cmd[2] = 0x40; /* request subQ data */ - cgc.cmd[3] = mcn ? 2 : 1; - cgc.cmd[8] = 16; - - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - - subchnl->cdsc_audiostatus = cgc.buffer[1]; - subchnl->cdsc_format = CDROM_MSF; - subchnl->cdsc_ctrl = cgc.buffer[5] & 0xf; - subchnl->cdsc_trk = cgc.buffer[6]; - subchnl->cdsc_ind = cgc.buffer[7]; - - subchnl->cdsc_reladdr.msf.minute = cgc.buffer[13]; - subchnl->cdsc_reladdr.msf.second = cgc.buffer[14]; - subchnl->cdsc_reladdr.msf.frame = cgc.buffer[15]; - subchnl->cdsc_absaddr.msf.minute = cgc.buffer[9]; - subchnl->cdsc_absaddr.msf.second = cgc.buffer[10]; - subchnl->cdsc_absaddr.msf.frame = cgc.buffer[11]; - - return 0; -} - -/* - * Specific READ_10 interface - */ -static int cdrom_read_cd(struct cdrom_device_info *cdi, - struct packet_command *cgc, int lba, - int blocksize, int nblocks) -{ - struct cdrom_device_ops *cdo = cdi->ops; - - memset(&cgc->cmd, 0, sizeof(cgc->cmd)); - cgc->cmd[0] = GPCMD_READ_10; - cgc->cmd[2] = (lba >> 24) & 0xff; - cgc->cmd[3] = (lba >> 16) & 0xff; - cgc->cmd[4] = (lba >> 8) & 0xff; - cgc->cmd[5] = lba & 0xff; - cgc->cmd[6] = (nblocks >> 16) & 0xff; - cgc->cmd[7] = (nblocks >> 8) & 0xff; - cgc->cmd[8] = nblocks & 0xff; - cgc->buflen = blocksize * nblocks; - return cdo->generic_packet(cdi, cgc); -} - -/* very generic interface for reading the various types of blocks */ -static int cdrom_read_block(struct cdrom_device_info *cdi, - struct packet_command *cgc, - int lba, int nblocks, int format, int blksize) -{ - struct cdrom_device_ops *cdo = cdi->ops; - - memset(&cgc->cmd, 0, sizeof(cgc->cmd)); - cgc->cmd[0] = GPCMD_READ_CD; - /* expected sector size - cdda,mode1,etc. */ - cgc->cmd[1] = format << 2; - /* starting address */ - cgc->cmd[2] = (lba >> 24) & 0xff; - cgc->cmd[3] = (lba >> 16) & 0xff; - cgc->cmd[4] = (lba >> 8) & 0xff; - cgc->cmd[5] = lba & 0xff; - /* number of blocks */ - cgc->cmd[6] = (nblocks >> 16) & 0xff; - cgc->cmd[7] = (nblocks >> 8) & 0xff; - cgc->cmd[8] = nblocks & 0xff; - cgc->buflen = blksize * nblocks; - - /* set the header info returned */ - switch (blksize) { - case CD_FRAMESIZE_RAW0 : cgc->cmd[9] = 0x58; break; - case CD_FRAMESIZE_RAW1 : cgc->cmd[9] = 0x78; break; - case CD_FRAMESIZE_RAW : cgc->cmd[9] = 0xf8; break; - default : cgc->cmd[9] = 0x10; - } - - return cdo->generic_packet(cdi, cgc); -} - -static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, - int lba, int nframes) -{ - struct packet_command cgc; - int ret = 0; - int nr; - - cdi->last_sense = 0; - - memset(&cgc, 0, sizeof(cgc)); - - /* - * start with will ra.nframes size, back down if alloc fails - */ - nr = nframes; - do { - cgc.buffer = kmalloc(CD_FRAMESIZE_RAW * nr, GFP_KERNEL); - if (cgc.buffer) - break; - - nr >>= 1; - } while (nr); - - if (!nr) - return -ENOMEM; - - cgc.data_direction = CGC_DATA_READ; - while (nframes > 0) { - if (nr > nframes) - nr = nframes; - - ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW); - if (ret) - break; - if (copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr)) { - ret = -EFAULT; - break; - } - ubuf += CD_FRAMESIZE_RAW * nr; - nframes -= nr; - lba += nr; - } - kfree(cgc.buffer); - return ret; -} - -static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, - int lba, int nframes) -{ - struct request_queue *q = cdi->disk->queue; - struct request *rq; - struct bio *bio; - unsigned int len; - int nr, ret = 0; - - if (!q) - return -ENXIO; - - cdi->last_sense = 0; - - while (nframes) { - nr = nframes; - if (cdi->cdda_method == CDDA_BPC_SINGLE) - nr = 1; - if (nr * CD_FRAMESIZE_RAW > (queue_max_sectors(q) << 9)) - nr = (queue_max_sectors(q) << 9) / CD_FRAMESIZE_RAW; - - len = nr * CD_FRAMESIZE_RAW; - - rq = blk_get_request(q, READ, GFP_KERNEL); - if (IS_ERR(rq)) { - ret = PTR_ERR(rq); - break; - } - blk_rq_set_block_pc(rq); - - ret = blk_rq_map_user(q, rq, NULL, ubuf, len, GFP_KERNEL); - if (ret) { - blk_put_request(rq); - break; - } - - rq->cmd[0] = GPCMD_READ_CD; - rq->cmd[1] = 1 << 2; - rq->cmd[2] = (lba >> 24) & 0xff; - rq->cmd[3] = (lba >> 16) & 0xff; - rq->cmd[4] = (lba >> 8) & 0xff; - rq->cmd[5] = lba & 0xff; - rq->cmd[6] = (nr >> 16) & 0xff; - rq->cmd[7] = (nr >> 8) & 0xff; - rq->cmd[8] = nr & 0xff; - rq->cmd[9] = 0xf8; - - rq->cmd_len = 12; - rq->timeout = 60 * HZ; - bio = rq->bio; - - if (blk_execute_rq(q, cdi->disk, rq, 0)) { - struct request_sense *s = rq->sense; - ret = -EIO; - cdi->last_sense = s->sense_key; - } - - if (blk_rq_unmap_user(bio)) - ret = -EFAULT; - blk_put_request(rq); - - if (ret) - break; - - nframes -= nr; - lba += nr; - ubuf += len; - } - - return ret; -} - -static int cdrom_read_cdda(struct cdrom_device_info *cdi, __u8 __user *ubuf, - int lba, int nframes) -{ - int ret; - - if (cdi->cdda_method == CDDA_OLD) - return cdrom_read_cdda_old(cdi, ubuf, lba, nframes); - -retry: - /* - * for anything else than success and io error, we need to retry - */ - ret = cdrom_read_cdda_bpc(cdi, ubuf, lba, nframes); - if (!ret || ret != -EIO) - return ret; - - /* - * I've seen drives get sense 4/8/3 udma crc errors on multi - * frame dma, so drop to single frame dma if we need to - */ - if (cdi->cdda_method == CDDA_BPC_FULL && nframes > 1) { - pr_info("dropping to single frame dma\n"); - cdi->cdda_method = CDDA_BPC_SINGLE; - goto retry; - } - - /* - * so we have an io error of some sort with multi frame dma. if the - * condition wasn't a hardware error - * problems, not for any error - */ - if (cdi->last_sense != 0x04 && cdi->last_sense != 0x0b) - return ret; - - pr_info("dropping to old style cdda (sense=%x)\n", cdi->last_sense); - cdi->cdda_method = CDDA_OLD; - return cdrom_read_cdda_old(cdi, ubuf, lba, nframes); -} - -static int cdrom_ioctl_multisession(struct cdrom_device_info *cdi, - void __user *argp) -{ - struct cdrom_multisession ms_info; - u8 requested_format; - int ret; - - cd_dbg(CD_DO_IOCTL, "entering CDROMMULTISESSION\n"); - - if (!(cdi->ops->capability & CDC_MULTI_SESSION)) - return -ENOSYS; - - if (copy_from_user(&ms_info, argp, sizeof(ms_info))) - return -EFAULT; - - requested_format = ms_info.addr_format; - if (requested_format != CDROM_MSF && requested_format != CDROM_LBA) - return -EINVAL; - ms_info.addr_format = CDROM_LBA; - - ret = cdi->ops->get_last_session(cdi, &ms_info); - if (ret) - return ret; - - sanitize_format(&ms_info.addr, &ms_info.addr_format, requested_format); - - if (copy_to_user(argp, &ms_info, sizeof(ms_info))) - return -EFAULT; - - cd_dbg(CD_DO_IOCTL, "CDROMMULTISESSION successful\n"); - return 0; -} - -static int cdrom_ioctl_eject(struct cdrom_device_info *cdi) -{ - cd_dbg(CD_DO_IOCTL, "entering CDROMEJECT\n"); - - if (!CDROM_CAN(CDC_OPEN_TRAY)) - return -ENOSYS; - if (cdi->use_count != 1 || cdi->keeplocked) - return -EBUSY; - if (CDROM_CAN(CDC_LOCK)) { - int ret = cdi->ops->lock_door(cdi, 0); - if (ret) - return ret; - } - - return cdi->ops->tray_move(cdi, 1); -} - -static int cdrom_ioctl_closetray(struct cdrom_device_info *cdi) -{ - cd_dbg(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n"); - - if (!CDROM_CAN(CDC_CLOSE_TRAY)) - return -ENOSYS; - return cdi->ops->tray_move(cdi, 0); -} - -static int cdrom_ioctl_eject_sw(struct cdrom_device_info *cdi, - unsigned long arg) -{ - cd_dbg(CD_DO_IOCTL, "entering CDROMEJECT_SW\n"); - - if (!CDROM_CAN(CDC_OPEN_TRAY)) - return -ENOSYS; - if (cdi->keeplocked) - return -EBUSY; - - cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT); - if (arg) - cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT; - return 0; -} - -static int cdrom_ioctl_media_changed(struct cdrom_device_info *cdi, - unsigned long arg) -{ - struct cdrom_changer_info *info; - int ret; - - cd_dbg(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n"); - - if (!CDROM_CAN(CDC_MEDIA_CHANGED)) - return -ENOSYS; - - /* cannot select disc or select current disc */ - if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT) - return media_changed(cdi, 1); - - if (arg >= cdi->capacity) - return -EINVAL; - - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - ret = cdrom_read_mech_status(cdi, info); - if (!ret) - ret = info->slots[arg].change; - kfree(info); - return ret; -} - -static int cdrom_ioctl_set_options(struct cdrom_device_info *cdi, - unsigned long arg) -{ - cd_dbg(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n"); - - /* - * Options need to be in sync with capability. - * Too late for that, so we have to check each one separately. - */ - switch (arg) { - case CDO_USE_FFLAGS: - case CDO_CHECK_TYPE: - break; - case CDO_LOCK: - if (!CDROM_CAN(CDC_LOCK)) - return -ENOSYS; - break; - case 0: - return cdi->options; - /* default is basically CDO_[AUTO_CLOSE|AUTO_EJECT] */ - default: - if (!CDROM_CAN(arg)) - return -ENOSYS; - } - cdi->options |= (int) arg; - return cdi->options; -} - -static int cdrom_ioctl_clear_options(struct cdrom_device_info *cdi, - unsigned long arg) -{ - cd_dbg(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n"); - - cdi->options &= ~(int) arg; - return cdi->options; -} - -static int cdrom_ioctl_select_speed(struct cdrom_device_info *cdi, - unsigned long arg) -{ - cd_dbg(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\n"); - - if (!CDROM_CAN(CDC_SELECT_SPEED)) - return -ENOSYS; - return cdi->ops->select_speed(cdi, arg); -} - -static int cdrom_ioctl_select_disc(struct cdrom_device_info *cdi, - unsigned long arg) -{ - cd_dbg(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n"); - - if (!CDROM_CAN(CDC_SELECT_DISC)) - return -ENOSYS; - - if (arg != CDSL_CURRENT && arg != CDSL_NONE) { - if (arg >= cdi->capacity) - return -EINVAL; - } - - /* - * ->select_disc is a hook to allow a driver-specific way of - * seleting disc. However, since there is no equivalent hook for - * cdrom_slot_status this may not actually be useful... - */ - if (cdi->ops->select_disc) - return cdi->ops->select_disc(cdi, arg); - - cd_dbg(CD_CHANGER, "Using generic cdrom_select_disc()\n"); - return cdrom_select_disc(cdi, arg); -} - -static int cdrom_ioctl_reset(struct cdrom_device_info *cdi, - struct block_device *bdev) -{ - cd_dbg(CD_DO_IOCTL, "entering CDROM_RESET\n"); - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - if (!CDROM_CAN(CDC_RESET)) - return -ENOSYS; - invalidate_bdev(bdev); - return cdi->ops->reset(cdi); -} - -static int cdrom_ioctl_lock_door(struct cdrom_device_info *cdi, - unsigned long arg) -{ - cd_dbg(CD_DO_IOCTL, "%socking door\n", arg ? "L" : "Unl"); - - if (!CDROM_CAN(CDC_LOCK)) - return -EDRIVE_CANT_DO_THIS; - - cdi->keeplocked = arg ? 1 : 0; - - /* - * Don't unlock the door on multiple opens by default, but allow - * root to do so. - */ - if (cdi->use_count != 1 && !arg && !capable(CAP_SYS_ADMIN)) - return -EBUSY; - return cdi->ops->lock_door(cdi, arg); -} - -static int cdrom_ioctl_debug(struct cdrom_device_info *cdi, - unsigned long arg) -{ - cd_dbg(CD_DO_IOCTL, "%sabling debug\n", arg ? "En" : "Dis"); - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - debug = arg ? 1 : 0; - return debug; -} - -static int cdrom_ioctl_get_capability(struct cdrom_device_info *cdi) -{ - cd_dbg(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n"); - return (cdi->ops->capability & ~cdi->mask); -} - -/* - * The following function is implemented, although very few audio - * discs give Universal Product Code information, which should just be - * the Medium Catalog Number on the box. Note, that the way the code - * is written on the CD is /not/ uniform across all discs! - */ -static int cdrom_ioctl_get_mcn(struct cdrom_device_info *cdi, - void __user *argp) -{ - struct cdrom_mcn mcn; - int ret; - - cd_dbg(CD_DO_IOCTL, "entering CDROM_GET_MCN\n"); - - if (!(cdi->ops->capability & CDC_MCN)) - return -ENOSYS; - ret = cdi->ops->get_mcn(cdi, &mcn); - if (ret) - return ret; - - if (copy_to_user(argp, &mcn, sizeof(mcn))) - return -EFAULT; - cd_dbg(CD_DO_IOCTL, "CDROM_GET_MCN successful\n"); - return 0; -} - -static int cdrom_ioctl_drive_status(struct cdrom_device_info *cdi, - unsigned long arg) -{ - cd_dbg(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n"); - - if (!(cdi->ops->capability & CDC_DRIVE_STATUS)) - return -ENOSYS; - if (!CDROM_CAN(CDC_SELECT_DISC) || - (arg == CDSL_CURRENT || arg == CDSL_NONE)) - return cdi->ops->drive_status(cdi, CDSL_CURRENT); - if (arg >= cdi->capacity) - return -EINVAL; - return cdrom_slot_status(cdi, arg); -} - -/* - * Ok, this is where problems start. The current interface for the - * CDROM_DISC_STATUS ioctl is flawed. It makes the false assumption that - * CDs are all CDS_DATA_1 or all CDS_AUDIO, etc. Unfortunately, while this - * is often the case, it is also very common for CDs to have some tracks - * with data, and some tracks with audio. Just because I feel like it, - * I declare the following to be the best way to cope. If the CD has ANY - * data tracks on it, it will be returned as a data CD. If it has any XA - * tracks, I will return it as that. Now I could simplify this interface - * by combining these returns with the above, but this more clearly - * demonstrates the problem with the current interface. Too bad this - * wasn't designed to use bitmasks... -Erik - * - * Well, now we have the option CDS_MIXED: a mixed-type CD. - * User level programmers might feel the ioctl is not very useful. - * ---david - */ -static int cdrom_ioctl_disc_status(struct cdrom_device_info *cdi) -{ - tracktype tracks; - - cd_dbg(CD_DO_IOCTL, "entering CDROM_DISC_STATUS\n"); - - cdrom_count_tracks(cdi, &tracks); - if (tracks.error) - return tracks.error; - - /* Policy mode on */ - if (tracks.audio > 0) { - if (!tracks.data && !tracks.cdi && !tracks.xa) - return CDS_AUDIO; - else - return CDS_MIXED; - } - - if (tracks.cdi > 0) - return CDS_XA_2_2; - if (tracks.xa > 0) - return CDS_XA_2_1; - if (tracks.data > 0) - return CDS_DATA_1; - /* Policy mode off */ - - cd_dbg(CD_WARNING, "This disc doesn't have any tracks I recognize!\n"); - return CDS_NO_INFO; -} - -static int cdrom_ioctl_changer_nslots(struct cdrom_device_info *cdi) -{ - cd_dbg(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n"); - return cdi->capacity; -} - -static int cdrom_ioctl_get_subchnl(struct cdrom_device_info *cdi, - void __user *argp) -{ - struct cdrom_subchnl q; - u8 requested, back; - int ret; - - /* cd_dbg(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/ - - if (copy_from_user(&q, argp, sizeof(q))) - return -EFAULT; - - requested = q.cdsc_format; - if (requested != CDROM_MSF && requested != CDROM_LBA) - return -EINVAL; - q.cdsc_format = CDROM_MSF; - - ret = cdi->ops->audio_ioctl(cdi, CDROMSUBCHNL, &q); - if (ret) - return ret; - - back = q.cdsc_format; /* local copy */ - sanitize_format(&q.cdsc_absaddr, &back, requested); - sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested); - - if (copy_to_user(argp, &q, sizeof(q))) - return -EFAULT; - /* cd_dbg(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ - return 0; -} - -static int cdrom_ioctl_read_tochdr(struct cdrom_device_info *cdi, - void __user *argp) -{ - struct cdrom_tochdr header; - int ret; - - /* cd_dbg(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */ - - if (copy_from_user(&header, argp, sizeof(header))) - return -EFAULT; - - ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header); - if (ret) - return ret; - - if (copy_to_user(argp, &header, sizeof(header))) - return -EFAULT; - /* cd_dbg(CD_DO_IOCTL, "CDROMREADTOCHDR successful\n"); */ - return 0; -} - -static int cdrom_ioctl_read_tocentry(struct cdrom_device_info *cdi, - void __user *argp) -{ - struct cdrom_tocentry entry; - u8 requested_format; - int ret; - - /* cd_dbg(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */ - - if (copy_from_user(&entry, argp, sizeof(entry))) - return -EFAULT; - - requested_format = entry.cdte_format; - if (requested_format != CDROM_MSF && requested_format != CDROM_LBA) - return -EINVAL; - /* make interface to low-level uniform */ - entry.cdte_format = CDROM_MSF; - ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry); - if (ret) - return ret; - sanitize_format(&entry.cdte_addr, &entry.cdte_format, requested_format); - - if (copy_to_user(argp, &entry, sizeof(entry))) - return -EFAULT; - /* cd_dbg(CD_DO_IOCTL, "CDROMREADTOCENTRY successful\n"); */ - return 0; -} - -static int cdrom_ioctl_play_msf(struct cdrom_device_info *cdi, - void __user *argp) -{ - struct cdrom_msf msf; - - cd_dbg(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); - - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - if (copy_from_user(&msf, argp, sizeof(msf))) - return -EFAULT; - return cdi->ops->audio_ioctl(cdi, CDROMPLAYMSF, &msf); -} - -static int cdrom_ioctl_play_trkind(struct cdrom_device_info *cdi, - void __user *argp) -{ - struct cdrom_ti ti; - int ret; - - cd_dbg(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n"); - - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - if (copy_from_user(&ti, argp, sizeof(ti))) - return -EFAULT; - - ret = check_for_audio_disc(cdi, cdi->ops); - if (ret) - return ret; - return cdi->ops->audio_ioctl(cdi, CDROMPLAYTRKIND, &ti); -} -static int cdrom_ioctl_volctrl(struct cdrom_device_info *cdi, - void __user *argp) -{ - struct cdrom_volctrl volume; - - cd_dbg(CD_DO_IOCTL, "entering CDROMVOLCTRL\n"); - - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - if (copy_from_user(&volume, argp, sizeof(volume))) - return -EFAULT; - return cdi->ops->audio_ioctl(cdi, CDROMVOLCTRL, &volume); -} - -static int cdrom_ioctl_volread(struct cdrom_device_info *cdi, - void __user *argp) -{ - struct cdrom_volctrl volume; - int ret; - - cd_dbg(CD_DO_IOCTL, "entering CDROMVOLREAD\n"); - - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - - ret = cdi->ops->audio_ioctl(cdi, CDROMVOLREAD, &volume); - if (ret) - return ret; - - if (copy_to_user(argp, &volume, sizeof(volume))) - return -EFAULT; - return 0; -} - -static int cdrom_ioctl_audioctl(struct cdrom_device_info *cdi, - unsigned int cmd) -{ - int ret; - - cd_dbg(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n"); - - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - ret = check_for_audio_disc(cdi, cdi->ops); - if (ret) - return ret; - return cdi->ops->audio_ioctl(cdi, cmd, NULL); -} - -/* - * Required when we need to use READ_10 to issue other than 2048 block - * reads - */ -static int cdrom_switch_blocksize(struct cdrom_device_info *cdi, int size) -{ - struct cdrom_device_ops *cdo = cdi->ops; - struct packet_command cgc; - struct modesel_head mh; - - memset(&mh, 0, sizeof(mh)); - mh.block_desc_length = 0x08; - mh.block_length_med = (size >> 8) & 0xff; - mh.block_length_lo = size & 0xff; - - memset(&cgc, 0, sizeof(cgc)); - cgc.cmd[0] = 0x15; - cgc.cmd[1] = 1 << 4; - cgc.cmd[4] = 12; - cgc.buflen = sizeof(mh); - cgc.buffer = (char *) &mh; - cgc.data_direction = CGC_DATA_WRITE; - mh.block_desc_length = 0x08; - mh.block_length_med = (size >> 8) & 0xff; - mh.block_length_lo = size & 0xff; - - return cdo->generic_packet(cdi, &cgc); -} - -static int cdrom_get_track_info(struct cdrom_device_info *cdi, - __u16 track, __u8 type, track_information *ti) -{ - struct cdrom_device_ops *cdo = cdi->ops; - struct packet_command cgc; - int ret, buflen; - - init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO; - cgc.cmd[1] = type & 3; - cgc.cmd[4] = (track & 0xff00) >> 8; - cgc.cmd[5] = track & 0xff; - cgc.cmd[8] = 8; - cgc.quiet = 1; - - ret = cdo->generic_packet(cdi, &cgc); - if (ret) - return ret; - - buflen = be16_to_cpu(ti->track_information_length) + - sizeof(ti->track_information_length); - - if (buflen > sizeof(track_information)) - buflen = sizeof(track_information); - - cgc.cmd[8] = cgc.buflen = buflen; - ret = cdo->generic_packet(cdi, &cgc); - if (ret) - return ret; - - /* return actual fill size */ - return buflen; -} - -/* return the last written block on the CD-R media. this is for the udf - file system. */ -int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written) -{ - struct cdrom_tocentry toc; - disc_information di; - track_information ti; - __u32 last_track; - int ret = -1, ti_size; - - if (!CDROM_CAN(CDC_GENERIC_PACKET)) - goto use_toc; - - ret = cdrom_get_disc_info(cdi, &di); - if (ret < (int)(offsetof(typeof(di), last_track_lsb) - + sizeof(di.last_track_lsb))) - goto use_toc; - - /* if unit didn't return msb, it's zeroed by cdrom_get_disc_info */ - last_track = (di.last_track_msb << 8) | di.last_track_lsb; - ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti); - if (ti_size < (int)offsetof(typeof(ti), track_start)) - goto use_toc; - - /* if this track is blank, try the previous. */ - if (ti.blank) { - if (last_track == 1) - goto use_toc; - last_track--; - ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti); - } - - if (ti_size < (int)(offsetof(typeof(ti), track_size) - + sizeof(ti.track_size))) - goto use_toc; - - /* if last recorded field is valid, return it. */ - if (ti.lra_v && ti_size >= (int)(offsetof(typeof(ti), last_rec_address) - + sizeof(ti.last_rec_address))) { - *last_written = be32_to_cpu(ti.last_rec_address); - } else { - /* make it up instead */ - *last_written = be32_to_cpu(ti.track_start) + - be32_to_cpu(ti.track_size); - if (ti.free_blocks) - *last_written -= (be32_to_cpu(ti.free_blocks) + 7); - } - return 0; - - /* this is where we end up if the drive either can't do a - GPCMD_READ_DISC_INFO or GPCMD_READ_TRACK_RZONE_INFO or if - it doesn't give enough information or fails. then we return - the toc contents. */ -use_toc: - toc.cdte_format = CDROM_MSF; - toc.cdte_track = CDROM_LEADOUT; - if ((ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &toc))) - return ret; - sanitize_format(&toc.cdte_addr, &toc.cdte_format, CDROM_LBA); - *last_written = toc.cdte_addr.lba; - return 0; -} - -/* return the next writable block. also for udf file system. */ -static int cdrom_get_next_writable(struct cdrom_device_info *cdi, - long *next_writable) -{ - disc_information di; - track_information ti; - __u16 last_track; - int ret, ti_size; - - if (!CDROM_CAN(CDC_GENERIC_PACKET)) - goto use_last_written; - - ret = cdrom_get_disc_info(cdi, &di); - if (ret < 0 || ret < offsetof(typeof(di), last_track_lsb) - + sizeof(di.last_track_lsb)) - goto use_last_written; - - /* if unit didn't return msb, it's zeroed by cdrom_get_disc_info */ - last_track = (di.last_track_msb << 8) | di.last_track_lsb; - ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti); - if (ti_size < 0 || ti_size < offsetof(typeof(ti), track_start)) - goto use_last_written; - - /* if this track is blank, try the previous. */ - if (ti.blank) { - if (last_track == 1) - goto use_last_written; - last_track--; - ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti); - if (ti_size < 0) - goto use_last_written; - } - - /* if next recordable address field is valid, use it. */ - if (ti.nwa_v && ti_size >= offsetof(typeof(ti), next_writable) - + sizeof(ti.next_writable)) { - *next_writable = be32_to_cpu(ti.next_writable); - return 0; - } - -use_last_written: - ret = cdrom_get_last_written(cdi, next_writable); - if (ret) { - *next_writable = 0; - return ret; - } else { - *next_writable += 7; - return 0; - } -} - -static noinline int mmc_ioctl_cdrom_read_data(struct cdrom_device_info *cdi, - void __user *arg, - struct packet_command *cgc, - int cmd) -{ - struct request_sense sense; - struct cdrom_msf msf; - int blocksize = 0, format = 0, lba; - int ret; - - switch (cmd) { - case CDROMREADRAW: - blocksize = CD_FRAMESIZE_RAW; - break; - case CDROMREADMODE1: - blocksize = CD_FRAMESIZE; - format = 2; - break; - case CDROMREADMODE2: - blocksize = CD_FRAMESIZE_RAW0; - break; - } - if (copy_from_user(&msf, (struct cdrom_msf __user *)arg, sizeof(msf))) - return -EFAULT; - lba = msf_to_lba(msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0); - /* FIXME: we need upper bound checking, too!! */ - if (lba < 0) - return -EINVAL; - - cgc->buffer = kzalloc(blocksize, GFP_KERNEL); - if (cgc->buffer == NULL) - return -ENOMEM; - - memset(&sense, 0, sizeof(sense)); - cgc->sense = &sense; - cgc->data_direction = CGC_DATA_READ; - ret = cdrom_read_block(cdi, cgc, lba, 1, format, blocksize); - if (ret && sense.sense_key == 0x05 && - sense.asc == 0x20 && - sense.ascq == 0x00) { - /* - * SCSI-II devices are not required to support - * READ_CD, so let's try switching block size - */ - /* FIXME: switch back again... */ - ret = cdrom_switch_blocksize(cdi, blocksize); - if (ret) - goto out; - cgc->sense = NULL; - ret = cdrom_read_cd(cdi, cgc, lba, blocksize, 1); - ret |= cdrom_switch_blocksize(cdi, blocksize); - } - if (!ret && copy_to_user(arg, cgc->buffer, blocksize)) - ret = -EFAULT; -out: - kfree(cgc->buffer); - return ret; -} - -static noinline int mmc_ioctl_cdrom_read_audio(struct cdrom_device_info *cdi, - void __user *arg) -{ - struct cdrom_read_audio ra; - int lba; - - if (copy_from_user(&ra, (struct cdrom_read_audio __user *)arg, - sizeof(ra))) - return -EFAULT; - - if (ra.addr_format == CDROM_MSF) - lba = msf_to_lba(ra.addr.msf.minute, - ra.addr.msf.second, - ra.addr.msf.frame); - else if (ra.addr_format == CDROM_LBA) - lba = ra.addr.lba; - else - return -EINVAL; - - /* FIXME: we need upper bound checking, too!! */ - if (lba < 0 || ra.nframes <= 0 || ra.nframes > CD_FRAMES) - return -EINVAL; - - return cdrom_read_cdda(cdi, ra.buf, lba, ra.nframes); -} - -static noinline int mmc_ioctl_cdrom_subchannel(struct cdrom_device_info *cdi, - void __user *arg) -{ - int ret; - struct cdrom_subchnl q; - u_char requested, back; - if (copy_from_user(&q, (struct cdrom_subchnl __user *)arg, sizeof(q))) - return -EFAULT; - requested = q.cdsc_format; - if (!((requested == CDROM_MSF) || - (requested == CDROM_LBA))) - return -EINVAL; - q.cdsc_format = CDROM_MSF; - ret = cdrom_read_subchannel(cdi, &q, 0); - if (ret) - return ret; - back = q.cdsc_format; /* local copy */ - sanitize_format(&q.cdsc_absaddr, &back, requested); - sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested); - if (copy_to_user((struct cdrom_subchnl __user *)arg, &q, sizeof(q))) - return -EFAULT; - /* cd_dbg(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ - return 0; -} - -static noinline int mmc_ioctl_cdrom_play_msf(struct cdrom_device_info *cdi, - void __user *arg, - struct packet_command *cgc) -{ - struct cdrom_device_ops *cdo = cdi->ops; - struct cdrom_msf msf; - cd_dbg(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); - if (copy_from_user(&msf, (struct cdrom_msf __user *)arg, sizeof(msf))) - return -EFAULT; - cgc->cmd[0] = GPCMD_PLAY_AUDIO_MSF; - cgc->cmd[3] = msf.cdmsf_min0; - cgc->cmd[4] = msf.cdmsf_sec0; - cgc->cmd[5] = msf.cdmsf_frame0; - cgc->cmd[6] = msf.cdmsf_min1; - cgc->cmd[7] = msf.cdmsf_sec1; - cgc->cmd[8] = msf.cdmsf_frame1; - cgc->data_direction = CGC_DATA_NONE; - return cdo->generic_packet(cdi, cgc); -} - -static noinline int mmc_ioctl_cdrom_play_blk(struct cdrom_device_info *cdi, - void __user *arg, - struct packet_command *cgc) -{ - struct cdrom_device_ops *cdo = cdi->ops; - struct cdrom_blk blk; - cd_dbg(CD_DO_IOCTL, "entering CDROMPLAYBLK\n"); - if (copy_from_user(&blk, (struct cdrom_blk __user *)arg, sizeof(blk))) - return -EFAULT; - cgc->cmd[0] = GPCMD_PLAY_AUDIO_10; - cgc->cmd[2] = (blk.from >> 24) & 0xff; - cgc->cmd[3] = (blk.from >> 16) & 0xff; - cgc->cmd[4] = (blk.from >> 8) & 0xff; - cgc->cmd[5] = blk.from & 0xff; - cgc->cmd[7] = (blk.len >> 8) & 0xff; - cgc->cmd[8] = blk.len & 0xff; - cgc->data_direction = CGC_DATA_NONE; - return cdo->generic_packet(cdi, cgc); -} - -static noinline int mmc_ioctl_cdrom_volume(struct cdrom_device_info *cdi, - void __user *arg, - struct packet_command *cgc, - unsigned int cmd) -{ - struct cdrom_volctrl volctrl; - unsigned char buffer[32]; - char mask[sizeof(buffer)]; - unsigned short offset; - int ret; - - cd_dbg(CD_DO_IOCTL, "entering CDROMVOLUME\n"); - - if (copy_from_user(&volctrl, (struct cdrom_volctrl __user *)arg, - sizeof(volctrl))) - return -EFAULT; - - cgc->buffer = buffer; - cgc->buflen = 24; - ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 0); - if (ret) - return ret; - - /* originally the code depended on buffer[1] to determine - how much data is available for transfer. buffer[1] is - unfortunately ambigious and the only reliable way seem - to be to simply skip over the block descriptor... */ - offset = 8 + be16_to_cpu(*(__be16 *)(buffer + 6)); - - if (offset + 16 > sizeof(buffer)) - return -E2BIG; - - if (offset + 16 > cgc->buflen) { - cgc->buflen = offset + 16; - ret = cdrom_mode_sense(cdi, cgc, - GPMODE_AUDIO_CTL_PAGE, 0); - if (ret) - return ret; - } - - /* sanity check */ - if ((buffer[offset] & 0x3f) != GPMODE_AUDIO_CTL_PAGE || - buffer[offset + 1] < 14) - return -EINVAL; - - /* now we have the current volume settings. if it was only - a CDROMVOLREAD, return these values */ - if (cmd == CDROMVOLREAD) { - volctrl.channel0 = buffer[offset+9]; - volctrl.channel1 = buffer[offset+11]; - volctrl.channel2 = buffer[offset+13]; - volctrl.channel3 = buffer[offset+15]; - if (copy_to_user((struct cdrom_volctrl __user *)arg, &volctrl, - sizeof(volctrl))) - return -EFAULT; - return 0; - } - - /* get the volume mask */ - cgc->buffer = mask; - ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 1); - if (ret) - return ret; - - buffer[offset + 9] = volctrl.channel0 & mask[offset + 9]; - buffer[offset + 11] = volctrl.channel1 & mask[offset + 11]; - buffer[offset + 13] = volctrl.channel2 & mask[offset + 13]; - buffer[offset + 15] = volctrl.channel3 & mask[offset + 15]; - - /* set volume */ - cgc->buffer = buffer + offset - 8; - memset(cgc->buffer, 0, 8); - return cdrom_mode_select(cdi, cgc); -} - -static noinline int mmc_ioctl_cdrom_start_stop(struct cdrom_device_info *cdi, - struct packet_command *cgc, - int cmd) -{ - struct cdrom_device_ops *cdo = cdi->ops; - cd_dbg(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n"); - cgc->cmd[0] = GPCMD_START_STOP_UNIT; - cgc->cmd[1] = 1; - cgc->cmd[4] = (cmd == CDROMSTART) ? 1 : 0; - cgc->data_direction = CGC_DATA_NONE; - return cdo->generic_packet(cdi, cgc); -} - -static noinline int mmc_ioctl_cdrom_pause_resume(struct cdrom_device_info *cdi, - struct packet_command *cgc, - int cmd) -{ - struct cdrom_device_ops *cdo = cdi->ops; - cd_dbg(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n"); - cgc->cmd[0] = GPCMD_PAUSE_RESUME; - cgc->cmd[8] = (cmd == CDROMRESUME) ? 1 : 0; - cgc->data_direction = CGC_DATA_NONE; - return cdo->generic_packet(cdi, cgc); -} - -static noinline int mmc_ioctl_dvd_read_struct(struct cdrom_device_info *cdi, - void __user *arg, - struct packet_command *cgc) -{ - int ret; - dvd_struct *s; - int size = sizeof(dvd_struct); - - if (!CDROM_CAN(CDC_DVD)) - return -ENOSYS; - - s = kmalloc(size, GFP_KERNEL); - if (!s) - return -ENOMEM; - - cd_dbg(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n"); - if (copy_from_user(s, arg, size)) { - kfree(s); - return -EFAULT; - } - - ret = dvd_read_struct(cdi, s, cgc); - if (ret) - goto out; - - if (copy_to_user(arg, s, size)) - ret = -EFAULT; -out: - kfree(s); - return ret; -} - -static noinline int mmc_ioctl_dvd_auth(struct cdrom_device_info *cdi, - void __user *arg) -{ - int ret; - dvd_authinfo ai; - if (!CDROM_CAN(CDC_DVD)) - return -ENOSYS; - cd_dbg(CD_DO_IOCTL, "entering DVD_AUTH\n"); - if (copy_from_user(&ai, (dvd_authinfo __user *)arg, sizeof(ai))) - return -EFAULT; - ret = dvd_do_auth(cdi, &ai); - if (ret) - return ret; - if (copy_to_user((dvd_authinfo __user *)arg, &ai, sizeof(ai))) - return -EFAULT; - return 0; -} - -static noinline int mmc_ioctl_cdrom_next_writable(struct cdrom_device_info *cdi, - void __user *arg) -{ - int ret; - long next = 0; - cd_dbg(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n"); - ret = cdrom_get_next_writable(cdi, &next); - if (ret) - return ret; - if (copy_to_user((long __user *)arg, &next, sizeof(next))) - return -EFAULT; - return 0; -} - -static noinline int mmc_ioctl_cdrom_last_written(struct cdrom_device_info *cdi, - void __user *arg) -{ - int ret; - long last = 0; - cd_dbg(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n"); - ret = cdrom_get_last_written(cdi, &last); - if (ret) - return ret; - if (copy_to_user((long __user *)arg, &last, sizeof(last))) - return -EFAULT; - return 0; -} - -static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, - unsigned long arg) -{ - struct packet_command cgc; - void __user *userptr = (void __user *)arg; - - memset(&cgc, 0, sizeof(cgc)); - - /* build a unified command and queue it through - cdo->generic_packet() */ - switch (cmd) { - case CDROMREADRAW: - case CDROMREADMODE1: - case CDROMREADMODE2: - return mmc_ioctl_cdrom_read_data(cdi, userptr, &cgc, cmd); - case CDROMREADAUDIO: - return mmc_ioctl_cdrom_read_audio(cdi, userptr); - case CDROMSUBCHNL: - return mmc_ioctl_cdrom_subchannel(cdi, userptr); - case CDROMPLAYMSF: - return mmc_ioctl_cdrom_play_msf(cdi, userptr, &cgc); - case CDROMPLAYBLK: - return mmc_ioctl_cdrom_play_blk(cdi, userptr, &cgc); - case CDROMVOLCTRL: - case CDROMVOLREAD: - return mmc_ioctl_cdrom_volume(cdi, userptr, &cgc, cmd); - case CDROMSTART: - case CDROMSTOP: - return mmc_ioctl_cdrom_start_stop(cdi, &cgc, cmd); - case CDROMPAUSE: - case CDROMRESUME: - return mmc_ioctl_cdrom_pause_resume(cdi, &cgc, cmd); - case DVD_READ_STRUCT: - return mmc_ioctl_dvd_read_struct(cdi, userptr, &cgc); - case DVD_AUTH: - return mmc_ioctl_dvd_auth(cdi, userptr); - case CDROM_NEXT_WRITABLE: - return mmc_ioctl_cdrom_next_writable(cdi, userptr); - case CDROM_LAST_WRITTEN: - return mmc_ioctl_cdrom_last_written(cdi, userptr); - } - - return -ENOTTY; -} - -/* - * Just about every imaginable ioctl is supported in the Uniform layer - * these days. - * ATAPI / SCSI specific code now mainly resides in mmc_ioctl(). - */ -int cdrom_ioctl(struct cdrom_device_info *cdi, struct block_device *bdev, - fmode_t mode, unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int ret; - - /* - * Try the generic SCSI command ioctl's first. - */ - ret = scsi_cmd_blk_ioctl(bdev, mode, cmd, argp); - if (ret != -ENOTTY) - return ret; - - switch (cmd) { - case CDROMMULTISESSION: - return cdrom_ioctl_multisession(cdi, argp); - case CDROMEJECT: - return cdrom_ioctl_eject(cdi); - case CDROMCLOSETRAY: - return cdrom_ioctl_closetray(cdi); - case CDROMEJECT_SW: - return cdrom_ioctl_eject_sw(cdi, arg); - case CDROM_MEDIA_CHANGED: - return cdrom_ioctl_media_changed(cdi, arg); - case CDROM_SET_OPTIONS: - return cdrom_ioctl_set_options(cdi, arg); - case CDROM_CLEAR_OPTIONS: - return cdrom_ioctl_clear_options(cdi, arg); - case CDROM_SELECT_SPEED: - return cdrom_ioctl_select_speed(cdi, arg); - case CDROM_SELECT_DISC: - return cdrom_ioctl_select_disc(cdi, arg); - case CDROMRESET: - return cdrom_ioctl_reset(cdi, bdev); - case CDROM_LOCKDOOR: - return cdrom_ioctl_lock_door(cdi, arg); - case CDROM_DEBUG: - return cdrom_ioctl_debug(cdi, arg); - case CDROM_GET_CAPABILITY: - return cdrom_ioctl_get_capability(cdi); - case CDROM_GET_MCN: - return cdrom_ioctl_get_mcn(cdi, argp); - case CDROM_DRIVE_STATUS: - return cdrom_ioctl_drive_status(cdi, arg); - case CDROM_DISC_STATUS: - return cdrom_ioctl_disc_status(cdi); - case CDROM_CHANGER_NSLOTS: - return cdrom_ioctl_changer_nslots(cdi); - } - - /* - * Use the ioctls that are implemented through the generic_packet() - * interface. this may look at bit funny, but if -ENOTTY is - * returned that particular ioctl is not implemented and we - * let it go through the device specific ones. - */ - if (CDROM_CAN(CDC_GENERIC_PACKET)) { - ret = mmc_ioctl(cdi, cmd, arg); - if (ret != -ENOTTY) - return ret; - } - - /* - * Note: most of the cd_dbg() calls are commented out here, - * because they fill up the sys log when CD players poll - * the drive. - */ - switch (cmd) { - case CDROMSUBCHNL: - return cdrom_ioctl_get_subchnl(cdi, argp); - case CDROMREADTOCHDR: - return cdrom_ioctl_read_tochdr(cdi, argp); - case CDROMREADTOCENTRY: - return cdrom_ioctl_read_tocentry(cdi, argp); - case CDROMPLAYMSF: - return cdrom_ioctl_play_msf(cdi, argp); - case CDROMPLAYTRKIND: - return cdrom_ioctl_play_trkind(cdi, argp); - case CDROMVOLCTRL: - return cdrom_ioctl_volctrl(cdi, argp); - case CDROMVOLREAD: - return cdrom_ioctl_volread(cdi, argp); - case CDROMSTART: - case CDROMSTOP: - case CDROMPAUSE: - case CDROMRESUME: - return cdrom_ioctl_audioctl(cdi, cmd); - } - - return -ENOSYS; -} - -EXPORT_SYMBOL(cdrom_get_last_written); -EXPORT_SYMBOL(register_cdrom); -EXPORT_SYMBOL(unregister_cdrom); -EXPORT_SYMBOL(cdrom_open); -EXPORT_SYMBOL(cdrom_release); -EXPORT_SYMBOL(cdrom_ioctl); -EXPORT_SYMBOL(cdrom_media_changed); -EXPORT_SYMBOL(cdrom_number_of_slots); -EXPORT_SYMBOL(cdrom_mode_select); -EXPORT_SYMBOL(cdrom_mode_sense); -EXPORT_SYMBOL(init_cdrom_command); -EXPORT_SYMBOL(cdrom_get_media_event); - -#ifdef CONFIG_SYSCTL - -#define CDROM_STR_SIZE 1000 - -static struct cdrom_sysctl_settings { - char info[CDROM_STR_SIZE]; /* general info */ - int autoclose; /* close tray upon mount, etc */ - int autoeject; /* eject on umount */ - int debug; /* turn on debugging messages */ - int lock; /* lock the door on device open */ - int check; /* check media type */ -} cdrom_sysctl_settings; - -enum cdrom_print_option { - CTL_NAME, - CTL_SPEED, - CTL_SLOTS, - CTL_CAPABILITY -}; - -static int cdrom_print_info(const char *header, int val, char *info, - int *pos, enum cdrom_print_option option) -{ - const int max_size = sizeof(cdrom_sysctl_settings.info); - struct cdrom_device_info *cdi; - int ret; - - ret = scnprintf(info + *pos, max_size - *pos, header); - if (!ret) - return 1; - - *pos += ret; - - list_for_each_entry(cdi, &cdrom_list, list) { - switch (option) { - case CTL_NAME: - ret = scnprintf(info + *pos, max_size - *pos, - "\t%s", cdi->name); - break; - case CTL_SPEED: - ret = scnprintf(info + *pos, max_size - *pos, - "\t%d", cdi->speed); - break; - case CTL_SLOTS: - ret = scnprintf(info + *pos, max_size - *pos, - "\t%d", cdi->capacity); - break; - case CTL_CAPABILITY: - ret = scnprintf(info + *pos, max_size - *pos, - "\t%d", CDROM_CAN(val) != 0); - break; - default: - pr_info("invalid option%d\n", option); - return 1; - } - if (!ret) - return 1; - *pos += ret; - } - - return 0; -} - -static int cdrom_sysctl_info(struct ctl_table *ctl, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - int pos; - char *info = cdrom_sysctl_settings.info; - const int max_size = sizeof(cdrom_sysctl_settings.info); - - if (!*lenp || (*ppos && !write)) { - *lenp = 0; - return 0; - } - - mutex_lock(&cdrom_mutex); - - pos = sprintf(info, "CD-ROM information, " VERSION "\n"); - - if (cdrom_print_info("\ndrive name:\t", 0, info, &pos, CTL_NAME)) - goto done; - if (cdrom_print_info("\ndrive speed:\t", 0, info, &pos, CTL_SPEED)) - goto done; - if (cdrom_print_info("\ndrive # of slots:", 0, info, &pos, CTL_SLOTS)) - goto done; - if (cdrom_print_info("\nCan close tray:\t", - CDC_CLOSE_TRAY, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan open tray:\t", - CDC_OPEN_TRAY, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan lock tray:\t", - CDC_LOCK, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan change speed:", - CDC_SELECT_SPEED, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan select disk:", - CDC_SELECT_DISC, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan read multisession:", - CDC_MULTI_SESSION, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan read MCN:\t", - CDC_MCN, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nReports media changed:", - CDC_MEDIA_CHANGED, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan play audio:\t", - CDC_PLAY_AUDIO, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan write CD-R:\t", - CDC_CD_R, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan write CD-RW:", - CDC_CD_RW, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan read DVD:\t", - CDC_DVD, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan write DVD-R:", - CDC_DVD_R, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan write DVD-RAM:", - CDC_DVD_RAM, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan read MRW:\t", - CDC_MRW, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan write MRW:\t", - CDC_MRW_W, info, &pos, CTL_CAPABILITY)) - goto done; - if (cdrom_print_info("\nCan write RAM:\t", - CDC_RAM, info, &pos, CTL_CAPABILITY)) - goto done; - if (!scnprintf(info + pos, max_size - pos, "\n\n")) - goto done; -doit: - mutex_unlock(&cdrom_mutex); - return proc_dostring(ctl, write, buffer, lenp, ppos); -done: - pr_info("info buffer too small\n"); - goto doit; -} - -/* Unfortunately, per device settings are not implemented through - procfs/sysctl yet. When they are, this will naturally disappear. For now - just update all drives. Later this will become the template on which - new registered drives will be based. */ -static void cdrom_update_settings(void) -{ - struct cdrom_device_info *cdi; - - mutex_lock(&cdrom_mutex); - list_for_each_entry(cdi, &cdrom_list, list) { - if (autoclose && CDROM_CAN(CDC_CLOSE_TRAY)) - cdi->options |= CDO_AUTO_CLOSE; - else if (!autoclose) - cdi->options &= ~CDO_AUTO_CLOSE; - if (autoeject && CDROM_CAN(CDC_OPEN_TRAY)) - cdi->options |= CDO_AUTO_EJECT; - else if (!autoeject) - cdi->options &= ~CDO_AUTO_EJECT; - if (lockdoor && CDROM_CAN(CDC_LOCK)) - cdi->options |= CDO_LOCK; - else if (!lockdoor) - cdi->options &= ~CDO_LOCK; - if (check_media_type) - cdi->options |= CDO_CHECK_TYPE; - else - cdi->options &= ~CDO_CHECK_TYPE; - } - mutex_unlock(&cdrom_mutex); -} - -static int cdrom_sysctl_handler(struct ctl_table *ctl, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - int ret; - - ret = proc_dointvec(ctl, write, buffer, lenp, ppos); - - if (write) { - - /* we only care for 1 or 0. */ - autoclose = !!cdrom_sysctl_settings.autoclose; - autoeject = !!cdrom_sysctl_settings.autoeject; - debug = !!cdrom_sysctl_settings.debug; - lockdoor = !!cdrom_sysctl_settings.lock; - check_media_type = !!cdrom_sysctl_settings.check; - - /* update the option flags according to the changes. we - don't have per device options through sysctl yet, - but we will have and then this will disappear. */ - cdrom_update_settings(); - } - - return ret; -} - -/* Place files in /proc/sys/dev/cdrom */ -static struct ctl_table cdrom_table[] = { - { - .procname = "info", - .data = &cdrom_sysctl_settings.info, - .maxlen = CDROM_STR_SIZE, - .mode = 0444, - .proc_handler = cdrom_sysctl_info, - }, - { - .procname = "autoclose", - .data = &cdrom_sysctl_settings.autoclose, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = cdrom_sysctl_handler, - }, - { - .procname = "autoeject", - .data = &cdrom_sysctl_settings.autoeject, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = cdrom_sysctl_handler, - }, - { - .procname = "debug", - .data = &cdrom_sysctl_settings.debug, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = cdrom_sysctl_handler, - }, - { - .procname = "lock", - .data = &cdrom_sysctl_settings.lock, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = cdrom_sysctl_handler, - }, - { - .procname = "check_media", - .data = &cdrom_sysctl_settings.check, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = cdrom_sysctl_handler - }, - { } -}; - -static struct ctl_table cdrom_cdrom_table[] = { - { - .procname = "cdrom", - .maxlen = 0, - .mode = 0555, - .child = cdrom_table, - }, - { } -}; - -/* Make sure that /proc/sys/dev is there */ -static struct ctl_table cdrom_root_table[] = { - { - .procname = "dev", - .maxlen = 0, - .mode = 0555, - .child = cdrom_cdrom_table, - }, - { } -}; -static struct ctl_table_header *cdrom_sysctl_header; - -static void cdrom_sysctl_register(void) -{ - static atomic_t initialized = ATOMIC_INIT(0); - - if (!atomic_add_unless(&initialized, 1, 1)) - return; - - cdrom_sysctl_header = register_sysctl_table(cdrom_root_table); - - /* set the defaults */ - cdrom_sysctl_settings.autoclose = autoclose; - cdrom_sysctl_settings.autoeject = autoeject; - cdrom_sysctl_settings.debug = debug; - cdrom_sysctl_settings.lock = lockdoor; - cdrom_sysctl_settings.check = check_media_type; -} - -static void cdrom_sysctl_unregister(void) -{ - if (cdrom_sysctl_header) - unregister_sysctl_table(cdrom_sysctl_header); -} - -#else /* CONFIG_SYSCTL */ - -static void cdrom_sysctl_register(void) -{ -} - -static void cdrom_sysctl_unregister(void) -{ -} - -#endif /* CONFIG_SYSCTL */ - -static int __init cdrom_init(void) -{ - cdrom_sysctl_register(); - - return 0; -} - -static void __exit cdrom_exit(void) -{ - pr_info("Uniform CD-ROM driver unloaded\n"); - cdrom_sysctl_unregister(); -} - -module_init(cdrom_init); -module_exit(cdrom_exit); -MODULE_LICENSE("GPL"); diff --git a/src/4.x/drivers/cpufreq/Makefile b/src/4.x/drivers/cpufreq/Makefile deleted file mode 100644 index 41a0db36f..000000000 --- a/src/4.x/drivers/cpufreq/Makefile +++ /dev/null @@ -1,106 +0,0 @@ -# CPUfreq core -#obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o - -# CPUfreq stats -#obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o - -# CPUfreq governors -#obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o -#obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o -#obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o -obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o -obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o -obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o - -#obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o - -################################################################################## -# x86 drivers. -# Link order matters. K8 is preferred to ACPI because of firmware bugs in early -# K8 systems. This is still the case but acpi-cpufreq errors out so that -# powernow-k8 can load then. ACPI is preferred to all other hardware-specific drivers. -# speedstep-* is preferred over p4-clockmod. - -#obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o -#obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o -#obj-$(CONFIG_X86_PCC_CPUFREQ) += pcc-cpufreq.o -#obj-$(CONFIG_X86_POWERNOW_K6) += powernow-k6.o -#obj-$(CONFIG_X86_POWERNOW_K7) += powernow-k7.o -#obj-$(CONFIG_X86_LONGHAUL) += longhaul.o -#obj-$(CONFIG_X86_E_POWERSAVER) += e_powersaver.o -#obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o -#obj-$(CONFIG_SC520_CPUFREQ) += sc520_freq.o -#obj-$(CONFIG_X86_LONGRUN) += longrun.o -#obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.o -#obj-$(CONFIG_X86_SPEEDSTEP_ICH) += speedstep-ich.o -#obj-$(CONFIG_X86_SPEEDSTEP_LIB) += speedstep-lib.o -#obj-$(CONFIG_X86_SPEEDSTEP_SMI) += speedstep-smi.o -#obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o -#obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o -#obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o -#obj-$(CONFIG_X86_INTEL_PSTATE) += intel_pstate.o -#obj-$(CONFIG_X86_AMD_FREQ_SENSITIVITY) += amd_freq_sensitivity.o -#obj-$(CONFIG_X86_SFI_CPUFREQ) += sfi-cpufreq.o - -################################################################################## -# ARM SoC drivers -#obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o -# big LITTLE per platform glues. Keep DT_BL_CPUFREQ as the last entry in all big -# LITTLE drivers, so that it is probed last. -#obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o - -#obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o -#obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o -#obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o -#obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o -#obj-$(CONFIG_ARM_HISI_ACPU_CPUFREQ) += hisi-acpu-cpufreq.o -#obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o -#obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o -#obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o -#obj-$(CONFIG_ARM_MT8173_CPUFREQ) += mt8173-cpufreq.o -#obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o -#obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o -#obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o -#obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o -#obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o -#obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o -#obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o -#obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o -#obj-$(CONFIG_ARM_S3C2440_CPUFREQ) += s3c2440-cpufreq.o -#obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o -#obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o -#obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o -#obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o -#obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o -#obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o -#obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o -#obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o -#obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o -#obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o - - -################################################################################## -# PowerPC platform drivers -#obj-$(CONFIG_CPU_FREQ_CBE) += ppc-cbe-cpufreq.o -#ppc-cbe-cpufreq-y += ppc_cbe_cpufreq_pervasive.o ppc_cbe_cpufreq.o -#obj-$(CONFIG_CPU_FREQ_CBE_PMI) += ppc_cbe_cpufreq_pmi.o -#obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o -#obj-$(CONFIG_QORIQ_CPUFREQ) += qoriq-cpufreq.o -#obj-$(CONFIG_CPU_FREQ_PMAC) += pmac32-cpufreq.o -#obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o -#obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += pasemi-cpufreq.o -#obj-$(CONFIG_POWERNV_CPUFREQ) += powernv-cpufreq.o - -################################################################################## -# Other platform drivers -#obj-$(CONFIG_AVR32_AT32AP_CPUFREQ) += at32ap-cpufreq.o -#obj-$(CONFIG_BFIN_CPU_FREQ) += blackfin-cpufreq.o -#obj-$(CONFIG_CRIS_MACH_ARTPEC3) += cris-artpec3-cpufreq.o -#obj-$(CONFIG_ETRAXFS) += cris-etraxfs-cpufreq.o -#obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o -#obj-$(CONFIG_LOONGSON2_CPUFREQ) += loongson2_cpufreq.o -#obj-$(CONFIG_LOONGSON1_CPUFREQ) += ls1x-cpufreq.o -#obj-$(CONFIG_SH_CPU_FREQ) += sh-cpufreq.o -#obj-$(CONFIG_SPARC_US2E_CPUFREQ) += sparc-us2e-cpufreq.o -#obj-$(CONFIG_SPARC_US3_CPUFREQ) += sparc-us3-cpufreq.o -#obj-$(CONFIG_UNICORE32) += unicore2-cpufreq.o diff --git a/src/4.x/drivers/cpufreq/acpi-cpufreq.c b/src/4.x/drivers/cpufreq/acpi-cpufreq.c deleted file mode 100644 index cec1ee2d2..000000000 --- a/src/4.x/drivers/cpufreq/acpi-cpufreq.c +++ /dev/null @@ -1,1023 +0,0 @@ -/* - * acpi-cpufreq.c - ACPI Processor P-States Driver - * - * Copyright (C) 2001, 2002 Andy Grover - * Copyright (C) 2001, 2002 Paul Diefenbaugh - * Copyright (C) 2002 - 2004 Dominik Brodowski - * Copyright (C) 2006 Denis Sadykov - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include - -MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski"); -MODULE_DESCRIPTION("ACPI Processor P-States Driver"); -MODULE_LICENSE("GPL"); - -#define PFX "acpi-cpufreq: " - -enum { - UNDEFINED_CAPABLE = 0, - SYSTEM_INTEL_MSR_CAPABLE, - SYSTEM_AMD_MSR_CAPABLE, - SYSTEM_IO_CAPABLE, -}; - -#define INTEL_MSR_RANGE (0xffff) -#define AMD_MSR_RANGE (0x7) - -#define MSR_K7_HWCR_CPB_DIS (1ULL << 25) - -struct acpi_cpufreq_data { - struct cpufreq_frequency_table *freq_table; - unsigned int resume; - unsigned int cpu_feature; - unsigned int acpi_perf_cpu; - cpumask_var_t freqdomain_cpus; -}; - -/* acpi_perf_data is a pointer to percpu data. */ -static struct acpi_processor_performance __percpu *acpi_perf_data; - -static inline struct acpi_processor_performance *to_perf_data(struct acpi_cpufreq_data *data) -{ - return per_cpu_ptr(acpi_perf_data, data->acpi_perf_cpu); -} - -static struct cpufreq_driver acpi_cpufreq_driver; - -static unsigned int acpi_pstate_strict; -static struct msr __percpu *msrs; - -static bool boost_state(unsigned int cpu) -{ - u32 lo, hi; - u64 msr; - - switch (boot_cpu_data.x86_vendor) { - case X86_VENDOR_INTEL: - rdmsr_on_cpu(cpu, MSR_IA32_MISC_ENABLE, &lo, &hi); - msr = lo | ((u64)hi << 32); - return !(msr & MSR_IA32_MISC_ENABLE_TURBO_DISABLE); - case X86_VENDOR_AMD: - rdmsr_on_cpu(cpu, MSR_K7_HWCR, &lo, &hi); - msr = lo | ((u64)hi << 32); - return !(msr & MSR_K7_HWCR_CPB_DIS); - } - return false; -} - -static void boost_set_msrs(bool enable, const struct cpumask *cpumask) -{ - u32 cpu; - u32 msr_addr; - u64 msr_mask; - - switch (boot_cpu_data.x86_vendor) { - case X86_VENDOR_INTEL: - msr_addr = MSR_IA32_MISC_ENABLE; - msr_mask = MSR_IA32_MISC_ENABLE_TURBO_DISABLE; - break; - case X86_VENDOR_AMD: - msr_addr = MSR_K7_HWCR; - msr_mask = MSR_K7_HWCR_CPB_DIS; - break; - default: - return; - } - - rdmsr_on_cpus(cpumask, msr_addr, msrs); - - for_each_cpu(cpu, cpumask) { - struct msr *reg = per_cpu_ptr(msrs, cpu); - if (enable) - reg->q &= ~msr_mask; - else - reg->q |= msr_mask; - } - - wrmsr_on_cpus(cpumask, msr_addr, msrs); -} - -static int _store_boost(int val) -{ - get_online_cpus(); - boost_set_msrs(val, cpu_online_mask); - put_online_cpus(); - pr_debug("Core Boosting %sabled.\n", val ? "en" : "dis"); - - return 0; -} - -static ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf) -{ - struct acpi_cpufreq_data *data = policy->driver_data; - - if (unlikely(!data)) - return -ENODEV; - - return cpufreq_show_cpus(data->freqdomain_cpus, buf); -} - -cpufreq_freq_attr_ro(freqdomain_cpus); - -#ifdef CONFIG_X86_ACPI_CPUFREQ_CPB -static ssize_t store_boost(const char *buf, size_t count) -{ - int ret; - unsigned long val = 0; - - if (!acpi_cpufreq_driver.boost_supported) - return -EINVAL; - - ret = kstrtoul(buf, 10, &val); - if (ret || (val > 1)) - return -EINVAL; - - _store_boost((int) val); - - return count; -} - -static ssize_t store_cpb(struct cpufreq_policy *policy, const char *buf, - size_t count) -{ - return store_boost(buf, count); -} - -static ssize_t show_cpb(struct cpufreq_policy *policy, char *buf) -{ - return sprintf(buf, "%u\n", acpi_cpufreq_driver.boost_enabled); -} - -cpufreq_freq_attr_rw(cpb); -#endif - -static int check_est_cpu(unsigned int cpuid) -{ - struct cpuinfo_x86 *cpu = &cpu_data(cpuid); - - return cpu_has(cpu, X86_FEATURE_EST); -} - -static int check_amd_hwpstate_cpu(unsigned int cpuid) -{ - struct cpuinfo_x86 *cpu = &cpu_data(cpuid); - - return cpu_has(cpu, X86_FEATURE_HW_PSTATE); -} - -static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data) -{ - struct acpi_processor_performance *perf; - int i; - - perf = to_perf_data(data); - - for (i = 0; i < perf->state_count; i++) { - if (value == perf->states[i].status) - return data->freq_table[i].frequency; - } - return 0; -} - -static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) -{ - struct cpufreq_frequency_table *pos; - struct acpi_processor_performance *perf; - - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) - msr &= AMD_MSR_RANGE; - else - msr &= INTEL_MSR_RANGE; - - perf = to_perf_data(data); - - cpufreq_for_each_entry(pos, data->freq_table) - if (msr == perf->states[pos->driver_data].status) - return pos->frequency; - return data->freq_table[0].frequency; -} - -static unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data) -{ - switch (data->cpu_feature) { - case SYSTEM_INTEL_MSR_CAPABLE: - case SYSTEM_AMD_MSR_CAPABLE: - return extract_msr(val, data); - case SYSTEM_IO_CAPABLE: - return extract_io(val, data); - default: - return 0; - } -} - -struct msr_addr { - u32 reg; -}; - -struct io_addr { - u16 port; - u8 bit_width; -}; - -struct drv_cmd { - unsigned int type; - const struct cpumask *mask; - union { - struct msr_addr msr; - struct io_addr io; - } addr; - u32 val; -}; - -/* Called via smp_call_function_single(), on the target CPU */ -static void do_drv_read(void *_cmd) -{ - struct drv_cmd *cmd = _cmd; - u32 h; - - switch (cmd->type) { - case SYSTEM_INTEL_MSR_CAPABLE: - case SYSTEM_AMD_MSR_CAPABLE: - rdmsr(cmd->addr.msr.reg, cmd->val, h); - break; - case SYSTEM_IO_CAPABLE: - acpi_os_read_port((acpi_io_address)cmd->addr.io.port, - &cmd->val, - (u32)cmd->addr.io.bit_width); - break; - default: - break; - } -} - -/* Called via smp_call_function_many(), on the target CPUs */ -static void do_drv_write(void *_cmd) -{ - struct drv_cmd *cmd = _cmd; - u32 lo, hi; - - switch (cmd->type) { - case SYSTEM_INTEL_MSR_CAPABLE: - rdmsr(cmd->addr.msr.reg, lo, hi); - lo = (lo & ~INTEL_MSR_RANGE) | (cmd->val & INTEL_MSR_RANGE); - wrmsr(cmd->addr.msr.reg, lo, hi); - break; - case SYSTEM_AMD_MSR_CAPABLE: - wrmsr(cmd->addr.msr.reg, cmd->val, 0); - break; - case SYSTEM_IO_CAPABLE: - acpi_os_write_port((acpi_io_address)cmd->addr.io.port, - cmd->val, - (u32)cmd->addr.io.bit_width); - break; - default: - break; - } -} - -static void drv_read(struct drv_cmd *cmd) -{ - int err; - cmd->val = 0; - - err = smp_call_function_any(cmd->mask, do_drv_read, cmd, 1); - WARN_ON_ONCE(err); /* smp_call_function_any() was buggy? */ -} - -static void drv_write(struct drv_cmd *cmd) -{ - int this_cpu; - - this_cpu = get_cpu(); - if (cpumask_test_cpu(this_cpu, cmd->mask)) - do_drv_write(cmd); - smp_call_function_many(cmd->mask, do_drv_write, cmd, 1); - put_cpu(); -} - -static u32 -get_cur_val(const struct cpumask *mask, struct acpi_cpufreq_data *data) -{ - struct acpi_processor_performance *perf; - struct drv_cmd cmd; - - if (unlikely(cpumask_empty(mask))) - return 0; - - switch (data->cpu_feature) { - case SYSTEM_INTEL_MSR_CAPABLE: - cmd.type = SYSTEM_INTEL_MSR_CAPABLE; - cmd.addr.msr.reg = MSR_IA32_PERF_CTL; - break; - case SYSTEM_AMD_MSR_CAPABLE: - cmd.type = SYSTEM_AMD_MSR_CAPABLE; - cmd.addr.msr.reg = MSR_AMD_PERF_CTL; - break; - case SYSTEM_IO_CAPABLE: - cmd.type = SYSTEM_IO_CAPABLE; - perf = to_perf_data(data); - cmd.addr.io.port = perf->control_register.address; - cmd.addr.io.bit_width = perf->control_register.bit_width; - break; - default: - return 0; - } - - cmd.mask = mask; - drv_read(&cmd); - - pr_debug("get_cur_val = %u\n", cmd.val); - - return cmd.val; -} - -static unsigned int get_cur_freq_on_cpu(unsigned int cpu) -{ - struct acpi_cpufreq_data *data; - struct cpufreq_policy *policy; - unsigned int freq; - unsigned int cached_freq; - - pr_debug("get_cur_freq_on_cpu (%d)\n", cpu); - - policy = cpufreq_cpu_get_raw(cpu); - if (unlikely(!policy)) - return 0; - - data = policy->driver_data; - if (unlikely(!data || !data->freq_table)) - return 0; - - cached_freq = data->freq_table[to_perf_data(data)->state].frequency; - freq = extract_freq(get_cur_val(cpumask_of(cpu), data), data); - if (freq != cached_freq) { - /* - * The dreaded BIOS frequency change behind our back. - * Force set the frequency on next target call. - */ - data->resume = 1; - } - - pr_debug("cur freq = %u\n", freq); - - return freq; -} - -static unsigned int check_freqs(const struct cpumask *mask, unsigned int freq, - struct acpi_cpufreq_data *data) -{ - unsigned int cur_freq; - unsigned int i; - - for (i = 0; i < 100; i++) { - cur_freq = extract_freq(get_cur_val(mask, data), data); - if (cur_freq == freq) - return 1; - udelay(10); - } - return 0; -} - -static int acpi_cpufreq_target(struct cpufreq_policy *policy, - unsigned int index) -{ - struct acpi_cpufreq_data *data = policy->driver_data; - struct acpi_processor_performance *perf; - struct drv_cmd cmd; - unsigned int next_perf_state = 0; /* Index into perf table */ - int result = 0; - - if (unlikely(data == NULL || data->freq_table == NULL)) { - return -ENODEV; - } - - perf = to_perf_data(data); - next_perf_state = data->freq_table[index].driver_data; - if (perf->state == next_perf_state) { - if (unlikely(data->resume)) { - pr_debug("Called after resume, resetting to P%d\n", - next_perf_state); - data->resume = 0; - } else { - pr_debug("Already at target state (P%d)\n", - next_perf_state); - goto out; - } - } - - switch (data->cpu_feature) { - case SYSTEM_INTEL_MSR_CAPABLE: - cmd.type = SYSTEM_INTEL_MSR_CAPABLE; - cmd.addr.msr.reg = MSR_IA32_PERF_CTL; - cmd.val = (u32) perf->states[next_perf_state].control; - break; - case SYSTEM_AMD_MSR_CAPABLE: - cmd.type = SYSTEM_AMD_MSR_CAPABLE; - cmd.addr.msr.reg = MSR_AMD_PERF_CTL; - cmd.val = (u32) perf->states[next_perf_state].control; - break; - case SYSTEM_IO_CAPABLE: - cmd.type = SYSTEM_IO_CAPABLE; - cmd.addr.io.port = perf->control_register.address; - cmd.addr.io.bit_width = perf->control_register.bit_width; - cmd.val = (u32) perf->states[next_perf_state].control; - break; - default: - result = -ENODEV; - goto out; - } - - /* cpufreq holds the hotplug lock, so we are safe from here on */ - if (policy->shared_type != CPUFREQ_SHARED_TYPE_ANY) - cmd.mask = policy->cpus; - else - cmd.mask = cpumask_of(policy->cpu); - - drv_write(&cmd); - - if (acpi_pstate_strict) { - if (!check_freqs(cmd.mask, data->freq_table[index].frequency, - data)) { - pr_debug("acpi_cpufreq_target failed (%d)\n", - policy->cpu); - result = -EAGAIN; - } - } - - if (!result) - perf->state = next_perf_state; - -out: - return result; -} - -static unsigned long -acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu) -{ - struct acpi_processor_performance *perf; - - perf = to_perf_data(data); - if (cpu_khz) { - /* search the closest match to cpu_khz */ - unsigned int i; - unsigned long freq; - unsigned long freqn = perf->states[0].core_frequency * 1000; - - for (i = 0; i < (perf->state_count-1); i++) { - freq = freqn; - freqn = perf->states[i+1].core_frequency * 1000; - if ((2 * cpu_khz) > (freqn + freq)) { - perf->state = i; - return freq; - } - } - perf->state = perf->state_count-1; - return freqn; - } else { - /* assume CPU is at P0... */ - perf->state = 0; - return perf->states[0].core_frequency * 1000; - } -} - -static void free_acpi_perf_data(void) -{ - unsigned int i; - - /* Freeing a NULL pointer is OK, and alloc_percpu zeroes. */ - for_each_possible_cpu(i) - free_cpumask_var(per_cpu_ptr(acpi_perf_data, i) - ->shared_cpu_map); - free_percpu(acpi_perf_data); -} - -static int boost_notify(struct notifier_block *nb, unsigned long action, - void *hcpu) -{ - unsigned cpu = (long)hcpu; - const struct cpumask *cpumask; - - cpumask = get_cpu_mask(cpu); - - /* - * Clear the boost-disable bit on the CPU_DOWN path so that - * this cpu cannot block the remaining ones from boosting. On - * the CPU_UP path we simply keep the boost-disable flag in - * sync with the current global state. - */ - - switch (action) { - case CPU_UP_PREPARE: - case CPU_UP_PREPARE_FROZEN: - boost_set_msrs(acpi_cpufreq_driver.boost_enabled, cpumask); - break; - - case CPU_DOWN_PREPARE: - case CPU_DOWN_PREPARE_FROZEN: - boost_set_msrs(1, cpumask); - break; - - default: - break; - } - - return NOTIFY_OK; -} - - -static struct notifier_block boost_nb = { - .notifier_call = boost_notify, -}; - -/* - * acpi_cpufreq_early_init - initialize ACPI P-States library - * - * Initialize the ACPI P-States library (drivers/acpi/processor_perflib.c) - * in order to determine correct frequency and voltage pairings. We can - * do _PDC and _PSD and find out the processor dependency for the - * actual init that will happen later... - */ -static int __init acpi_cpufreq_early_init(void) -{ - unsigned int i; - pr_debug("acpi_cpufreq_early_init\n"); - - acpi_perf_data = alloc_percpu(struct acpi_processor_performance); - if (!acpi_perf_data) { - pr_debug("Memory allocation error for acpi_perf_data.\n"); - return -ENOMEM; - } - for_each_possible_cpu(i) { - if (!zalloc_cpumask_var_node( - &per_cpu_ptr(acpi_perf_data, i)->shared_cpu_map, - GFP_KERNEL, cpu_to_node(i))) { - - /* Freeing a NULL pointer is OK: alloc_percpu zeroes. */ - free_acpi_perf_data(); - return -ENOMEM; - } - } - - /* Do initialization in ACPI core */ - acpi_processor_preregister_performance(acpi_perf_data); - return 0; -} - -#ifdef CONFIG_SMP -/* - * Some BIOSes do SW_ANY coordination internally, either set it up in hw - * or do it in BIOS firmware and won't inform about it to OS. If not - * detected, this has a side effect of making CPU run at a different speed - * than OS intended it to run at. Detect it and handle it cleanly. - */ -static int bios_with_sw_any_bug; - -static int sw_any_bug_found(const struct dmi_system_id *d) -{ - bios_with_sw_any_bug = 1; - return 0; -} - -static const struct dmi_system_id sw_any_bug_dmi_table[] = { - { - .callback = sw_any_bug_found, - .ident = "Supermicro Server X6DLP", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"), - DMI_MATCH(DMI_BIOS_VERSION, "080010"), - DMI_MATCH(DMI_PRODUCT_NAME, "X6DLP"), - }, - }, - { } -}; - -static int acpi_cpufreq_blacklist(struct cpuinfo_x86 *c) -{ - /* Intel Xeon Processor 7100 Series Specification Update - * http://www.intel.com/Assets/PDF/specupdate/314554.pdf - * AL30: A Machine Check Exception (MCE) Occurring during an - * Enhanced Intel SpeedStep Technology Ratio Change May Cause - * Both Processor Cores to Lock Up. */ - if (c->x86_vendor == X86_VENDOR_INTEL) { - if ((c->x86 == 15) && - (c->x86_model == 6) && - (c->x86_mask == 8)) { - printk(KERN_INFO "acpi-cpufreq: Intel(R) " - "Xeon(R) 7100 Errata AL30, processors may " - "lock up on frequency changes: disabling " - "acpi-cpufreq.\n"); - return -ENODEV; - } - } - return 0; -} -#endif - -static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) -{ - unsigned int i; - unsigned int valid_states = 0; - unsigned int cpu = policy->cpu; - struct acpi_cpufreq_data *data; - unsigned int result = 0; - struct cpuinfo_x86 *c = &cpu_data(policy->cpu); - struct acpi_processor_performance *perf; -#ifdef CONFIG_SMP - static int blacklisted; -#endif - - pr_debug("acpi_cpufreq_cpu_init\n"); - -#ifdef CONFIG_SMP - if (blacklisted) - return blacklisted; - blacklisted = acpi_cpufreq_blacklist(c); - if (blacklisted) - return blacklisted; -#endif - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - if (!zalloc_cpumask_var(&data->freqdomain_cpus, GFP_KERNEL)) { - result = -ENOMEM; - goto err_free; - } - - perf = per_cpu_ptr(acpi_perf_data, cpu); - data->acpi_perf_cpu = cpu; - policy->driver_data = data; - - if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) - acpi_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS; - - result = acpi_processor_register_performance(perf, cpu); - if (result) - goto err_free_mask; - - policy->shared_type = perf->shared_type; - - /* - * Will let policy->cpus know about dependency only when software - * coordination is required. - */ - if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL || - policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) { - cpumask_copy(policy->cpus, perf->shared_cpu_map); - } - cpumask_copy(data->freqdomain_cpus, perf->shared_cpu_map); - -#ifdef CONFIG_SMP - dmi_check_system(sw_any_bug_dmi_table); - if (bios_with_sw_any_bug && !policy_is_shared(policy)) { - policy->shared_type = CPUFREQ_SHARED_TYPE_ALL; - cpumask_copy(policy->cpus, topology_core_cpumask(cpu)); - } - - if (check_amd_hwpstate_cpu(cpu) && !acpi_pstate_strict) { - cpumask_clear(policy->cpus); - cpumask_set_cpu(cpu, policy->cpus); - cpumask_copy(data->freqdomain_cpus, - topology_sibling_cpumask(cpu)); - policy->shared_type = CPUFREQ_SHARED_TYPE_HW; - pr_info_once(PFX "overriding BIOS provided _PSD data\n"); - } -#endif - - /* capability check */ - if (perf->state_count <= 1) { - pr_debug("No P-States\n"); - result = -ENODEV; - goto err_unreg; - } - - if (perf->control_register.space_id != perf->status_register.space_id) { - result = -ENODEV; - goto err_unreg; - } - - switch (perf->control_register.space_id) { - case ACPI_ADR_SPACE_SYSTEM_IO: - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && - boot_cpu_data.x86 == 0xf) { - pr_debug("AMD K8 systems must use native drivers.\n"); - result = -ENODEV; - goto err_unreg; - } - pr_debug("SYSTEM IO addr space\n"); - data->cpu_feature = SYSTEM_IO_CAPABLE; - break; - case ACPI_ADR_SPACE_FIXED_HARDWARE: - pr_debug("HARDWARE addr space\n"); - if (check_est_cpu(cpu)) { - data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE; - break; - } - if (check_amd_hwpstate_cpu(cpu)) { - data->cpu_feature = SYSTEM_AMD_MSR_CAPABLE; - break; - } - result = -ENODEV; - goto err_unreg; - default: - pr_debug("Unknown addr space %d\n", - (u32) (perf->control_register.space_id)); - result = -ENODEV; - goto err_unreg; - } - - data->freq_table = kzalloc(sizeof(*data->freq_table) * - (perf->state_count+1), GFP_KERNEL); - if (!data->freq_table) { - result = -ENOMEM; - goto err_unreg; - } - - /* detect transition latency */ - policy->cpuinfo.transition_latency = 0; - for (i = 0; i < perf->state_count; i++) { - if ((perf->states[i].transition_latency * 1000) > - policy->cpuinfo.transition_latency) - policy->cpuinfo.transition_latency = - perf->states[i].transition_latency * 1000; - } - - /* Check for high latency (>20uS) from buggy BIOSes, like on T42 */ - if (perf->control_register.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE && - policy->cpuinfo.transition_latency > 20 * 1000) { - policy->cpuinfo.transition_latency = 20 * 1000; - printk_once(KERN_INFO - "P-state transition latency capped at 20 uS\n"); - } - - /* table init */ - for (i = 0; i < perf->state_count; i++) { - if (i > 0 && perf->states[i].core_frequency >= - data->freq_table[valid_states-1].frequency / 1000) - continue; - - data->freq_table[valid_states].driver_data = i; - data->freq_table[valid_states].frequency = - perf->states[i].core_frequency * 1000; - valid_states++; - } - data->freq_table[valid_states].frequency = CPUFREQ_TABLE_END; - perf->state = 0; - - result = cpufreq_table_validate_and_show(policy, data->freq_table); - if (result) - goto err_freqfree; - - if (perf->states[0].core_frequency * 1000 != policy->cpuinfo.max_freq) - printk(KERN_WARNING FW_WARN "P-state 0 is not max freq\n"); - - switch (perf->control_register.space_id) { - case ACPI_ADR_SPACE_SYSTEM_IO: - /* - * The core will not set policy->cur, because - * cpufreq_driver->get is NULL, so we need to set it here. - * However, we have to guess it, because the current speed is - * unknown and not detectable via IO ports. - */ - policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu); - break; - case ACPI_ADR_SPACE_FIXED_HARDWARE: - acpi_cpufreq_driver.get = get_cur_freq_on_cpu; - break; - default: - break; - } - - /* notify BIOS that we exist */ - acpi_processor_notify_smm(THIS_MODULE); - - pr_debug("CPU%u - ACPI performance management activated.\n", cpu); - for (i = 0; i < perf->state_count; i++) - pr_debug(" %cP%d: %d MHz, %d mW, %d uS\n", - (i == perf->state ? '*' : ' '), i, - (u32) perf->states[i].core_frequency, - (u32) perf->states[i].power, - (u32) perf->states[i].transition_latency); - - /* - * the first call to ->target() should result in us actually - * writing something to the appropriate registers. - */ - data->resume = 1; - - return result; - -err_freqfree: - kfree(data->freq_table); -err_unreg: - acpi_processor_unregister_performance(cpu); -err_free_mask: - free_cpumask_var(data->freqdomain_cpus); -err_free: - kfree(data); - policy->driver_data = NULL; - - return result; -} - -static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy) -{ - struct acpi_cpufreq_data *data = policy->driver_data; - - pr_debug("acpi_cpufreq_cpu_exit\n"); - - if (data) { - policy->driver_data = NULL; - acpi_processor_unregister_performance(data->acpi_perf_cpu); - free_cpumask_var(data->freqdomain_cpus); - kfree(data->freq_table); - kfree(data); - } - - return 0; -} - -static int acpi_cpufreq_resume(struct cpufreq_policy *policy) -{ - struct acpi_cpufreq_data *data = policy->driver_data; - - pr_debug("acpi_cpufreq_resume\n"); - - data->resume = 1; - - return 0; -} - -static struct freq_attr *acpi_cpufreq_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - &freqdomain_cpus, -#ifdef CONFIG_X86_ACPI_CPUFREQ_CPB - &cpb, -#endif - NULL, -}; - -static struct cpufreq_driver acpi_cpufreq_driver = { - .verify = cpufreq_generic_frequency_table_verify, - .target_index = acpi_cpufreq_target, - .bios_limit = acpi_processor_get_bios_limit, - .init = acpi_cpufreq_cpu_init, - .exit = acpi_cpufreq_cpu_exit, - .resume = acpi_cpufreq_resume, - .name = "acpi-cpufreq", - .attr = acpi_cpufreq_attr, - .set_boost = _store_boost, -}; - -static void __init acpi_cpufreq_boost_init(void) -{ - if (boot_cpu_has(X86_FEATURE_CPB) || boot_cpu_has(X86_FEATURE_IDA)) { - msrs = msrs_alloc(); - - if (!msrs) - return; - - acpi_cpufreq_driver.boost_supported = true; - acpi_cpufreq_driver.boost_enabled = boost_state(0); - - cpu_notifier_register_begin(); - - /* Force all MSRs to the same value */ - boost_set_msrs(acpi_cpufreq_driver.boost_enabled, - cpu_online_mask); - - __register_cpu_notifier(&boost_nb); - - cpu_notifier_register_done(); - } -} - -static void acpi_cpufreq_boost_exit(void) -{ - if (msrs) { - unregister_cpu_notifier(&boost_nb); - - msrs_free(msrs); - msrs = NULL; - } -} - -static int __init acpi_cpufreq_init(void) -{ - int ret; - - if (acpi_disabled) - return -ENODEV; - - /* don't keep reloading if cpufreq_driver exists */ - if (cpufreq_get_current_driver()) - return -EEXIST; - - pr_debug("acpi_cpufreq_init\n"); - - ret = acpi_cpufreq_early_init(); - if (ret) - return ret; - -#ifdef CONFIG_X86_ACPI_CPUFREQ_CPB - /* this is a sysfs file with a strange name and an even stranger - * semantic - per CPU instantiation, but system global effect. - * Lets enable it only on AMD CPUs for compatibility reasons and - * only if configured. This is considered legacy code, which - * will probably be removed at some point in the future. - */ - if (!check_amd_hwpstate_cpu(0)) { - struct freq_attr **attr; - - pr_debug("CPB unsupported, do not expose it\n"); - - for (attr = acpi_cpufreq_attr; *attr; attr++) - if (*attr == &cpb) { - *attr = NULL; - break; - } - } -#endif - acpi_cpufreq_boost_init(); - - ret = cpufreq_register_driver(&acpi_cpufreq_driver); - if (ret) { - free_acpi_perf_data(); - acpi_cpufreq_boost_exit(); - } - return ret; -} - -static void __exit acpi_cpufreq_exit(void) -{ - pr_debug("acpi_cpufreq_exit\n"); - - acpi_cpufreq_boost_exit(); - - cpufreq_unregister_driver(&acpi_cpufreq_driver); - - free_acpi_perf_data(); -} - -module_param(acpi_pstate_strict, uint, 0644); -MODULE_PARM_DESC(acpi_pstate_strict, - "value 0 or non-zero. non-zero -> strict ACPI checks are " - "performed during frequency changes."); - -late_initcall(acpi_cpufreq_init); -module_exit(acpi_cpufreq_exit); - -static const struct x86_cpu_id acpi_cpufreq_ids[] = { - X86_FEATURE_MATCH(X86_FEATURE_ACPI), - X86_FEATURE_MATCH(X86_FEATURE_HW_PSTATE), - {} -}; -MODULE_DEVICE_TABLE(x86cpu, acpi_cpufreq_ids); - -static const struct acpi_device_id processor_device_ids[] = { - {ACPI_PROCESSOR_OBJECT_HID, }, - {ACPI_PROCESSOR_DEVICE_HID, }, - {}, -}; -MODULE_DEVICE_TABLE(acpi, processor_device_ids); - -MODULE_ALIAS("acpi"); diff --git a/src/4.x/drivers/cpufreq/cpufreq_conservative.c b/src/4.x/drivers/cpufreq/cpufreq_conservative.c deleted file mode 100644 index c395f9198..000000000 --- a/src/4.x/drivers/cpufreq/cpufreq_conservative.c +++ /dev/null @@ -1,406 +0,0 @@ -/* - * drivers/cpufreq/cpufreq_conservative.c - * - * Copyright (C) 2001 Russell King - * (C) 2003 Venkatesh Pallipadi . - * Jun Nakajima - * (C) 2009 Alexander Clouter - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include "cpufreq_governor.h" - -/* Conservative governor macros */ -#define DEF_FREQUENCY_UP_THRESHOLD (80) -#define DEF_FREQUENCY_DOWN_THRESHOLD (20) -#define DEF_FREQUENCY_STEP (5) -#define DEF_SAMPLING_DOWN_FACTOR (1) -#define MAX_SAMPLING_DOWN_FACTOR (10) - -static DEFINE_PER_CPU(struct cs_cpu_dbs_info_s, cs_cpu_dbs_info); - -static int cs_cpufreq_governor_dbs(struct cpufreq_policy *policy, - unsigned int event); - -#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE -static -#endif -struct cpufreq_governor cpufreq_gov_conservative = { - .name = "conservative", - .governor = cs_cpufreq_governor_dbs, - .max_transition_latency = TRANSITION_LATENCY_LIMIT, - .owner = THIS_MODULE, -}; - -static inline unsigned int get_freq_target(struct cs_dbs_tuners *cs_tuners, - struct cpufreq_policy *policy) -{ - unsigned int freq_target = (cs_tuners->freq_step * policy->max) / 100; - - /* max freq cannot be less than 100. But who knows... */ - if (unlikely(freq_target == 0)) - freq_target = DEF_FREQUENCY_STEP; - - return freq_target; -} - -/* - * Every sampling_rate, we check, if current idle time is less than 20% - * (default), then we try to increase frequency. Every sampling_rate * - * sampling_down_factor, we check, if current idle time is more than 80% - * (default), then we try to decrease frequency - * - * Any frequency increase takes it to the maximum frequency. Frequency reduction - * happens at minimum steps of 5% (default) of maximum frequency - */ -static void cs_check_cpu(int cpu, unsigned int load) -{ - struct cs_cpu_dbs_info_s *dbs_info = &per_cpu(cs_cpu_dbs_info, cpu); - struct cpufreq_policy *policy = dbs_info->cdbs.shared->policy; - struct dbs_data *dbs_data = policy->governor_data; - struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - - /* - * break out if we 'cannot' reduce the speed as the user might - * want freq_step to be zero - */ - if (cs_tuners->freq_step == 0) - return; - - /* Check for frequency increase */ - if (load > cs_tuners->up_threshold) { - dbs_info->down_skip = 0; - - /* if we are already at full speed then break out early */ - if (dbs_info->requested_freq == policy->max) - return; - - dbs_info->requested_freq += get_freq_target(cs_tuners, policy); - - if (dbs_info->requested_freq > policy->max) - dbs_info->requested_freq = policy->max; - - __cpufreq_driver_target(policy, dbs_info->requested_freq, - CPUFREQ_RELATION_H); - return; - } - - /* if sampling_down_factor is active break out early */ - if (++dbs_info->down_skip < cs_tuners->sampling_down_factor) - return; - dbs_info->down_skip = 0; - - /* Check for frequency decrease */ - if (load < cs_tuners->down_threshold) { - unsigned int freq_target; - /* - * if we cannot reduce the frequency anymore, break out early - */ - if (policy->cur == policy->min) - return; - - freq_target = get_freq_target(cs_tuners, policy); - if (dbs_info->requested_freq > freq_target) - dbs_info->requested_freq -= freq_target; - else - dbs_info->requested_freq = policy->min; - - __cpufreq_driver_target(policy, dbs_info->requested_freq, - CPUFREQ_RELATION_L); - return; - } -} - -static unsigned int cs_dbs_timer(struct cpu_dbs_info *cdbs, - struct dbs_data *dbs_data, bool modify_all) -{ - struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - - if (modify_all) - dbs_check_cpu(dbs_data, cdbs->shared->policy->cpu); - - return delay_for_sampling_rate(cs_tuners->sampling_rate); -} - -static int dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val, - void *data) -{ - struct cpufreq_freqs *freq = data; - struct cs_cpu_dbs_info_s *dbs_info = - &per_cpu(cs_cpu_dbs_info, freq->cpu); - struct cpufreq_policy *policy = cpufreq_cpu_get_raw(freq->cpu); - - if (!policy) - return 0; - - /* policy isn't governed by conservative governor */ - if (policy->governor != &cpufreq_gov_conservative) - return 0; - - /* - * we only care if our internally tracked freq moves outside the 'valid' - * ranges of frequency available to us otherwise we do not change it - */ - if (dbs_info->requested_freq > policy->max - || dbs_info->requested_freq < policy->min) - dbs_info->requested_freq = freq->new; - - return 0; -} - -static struct notifier_block cs_cpufreq_notifier_block = { - .notifier_call = dbs_cpufreq_notifier, -}; - -/************************** sysfs interface ************************/ -static struct common_dbs_data cs_dbs_cdata; - -static ssize_t store_sampling_down_factor(struct dbs_data *dbs_data, - const char *buf, size_t count) -{ - struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - unsigned int input; - int ret; - ret = sscanf(buf, "%u", &input); - - if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1) - return -EINVAL; - - cs_tuners->sampling_down_factor = input; - return count; -} - -static ssize_t store_sampling_rate(struct dbs_data *dbs_data, const char *buf, - size_t count) -{ - struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - unsigned int input; - int ret; - ret = sscanf(buf, "%u", &input); - - if (ret != 1) - return -EINVAL; - - cs_tuners->sampling_rate = max(input, dbs_data->min_sampling_rate); - return count; -} - -static ssize_t store_up_threshold(struct dbs_data *dbs_data, const char *buf, - size_t count) -{ - struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - unsigned int input; - int ret; - ret = sscanf(buf, "%u", &input); - - if (ret != 1 || input > 100 || input <= cs_tuners->down_threshold) - return -EINVAL; - - cs_tuners->up_threshold = input; - return count; -} - -static ssize_t store_down_threshold(struct dbs_data *dbs_data, const char *buf, - size_t count) -{ - struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - unsigned int input; - int ret; - ret = sscanf(buf, "%u", &input); - - /* cannot be lower than 1 otherwise freq will not fall */ - if (ret != 1 || input < 1 || input > 100 || - input >= cs_tuners->up_threshold) - return -EINVAL; - - cs_tuners->down_threshold = input; - return count; -} - -static ssize_t store_ignore_nice_load(struct dbs_data *dbs_data, - const char *buf, size_t count) -{ - struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - unsigned int input, j; - int ret; - - ret = sscanf(buf, "%u", &input); - if (ret != 1) - return -EINVAL; - - if (input > 1) - input = 1; - - if (input == cs_tuners->ignore_nice_load) /* nothing to do */ - return count; - - cs_tuners->ignore_nice_load = input; - - /* we need to re-evaluate prev_cpu_idle */ - for_each_online_cpu(j) { - struct cs_cpu_dbs_info_s *dbs_info; - dbs_info = &per_cpu(cs_cpu_dbs_info, j); - dbs_info->cdbs.prev_cpu_idle = get_cpu_idle_time(j, - &dbs_info->cdbs.prev_cpu_wall, 0); - if (cs_tuners->ignore_nice_load) - dbs_info->cdbs.prev_cpu_nice = - kcpustat_cpu(j).cpustat[CPUTIME_NICE]; - } - return count; -} - -static ssize_t store_freq_step(struct dbs_data *dbs_data, const char *buf, - size_t count) -{ - struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - unsigned int input; - int ret; - ret = sscanf(buf, "%u", &input); - - if (ret != 1) - return -EINVAL; - - if (input > 100) - input = 100; - - /* - * no need to test here if freq_step is zero as the user might actually - * want this, they would be crazy though :) - */ - cs_tuners->freq_step = input; - return count; -} - -show_store_one(cs, sampling_rate); -show_store_one(cs, sampling_down_factor); -show_store_one(cs, up_threshold); -show_store_one(cs, down_threshold); -show_store_one(cs, ignore_nice_load); -show_store_one(cs, freq_step); -declare_show_sampling_rate_min(cs); - -gov_sys_pol_attr_rw(sampling_rate); -gov_sys_pol_attr_rw(sampling_down_factor); -gov_sys_pol_attr_rw(up_threshold); -gov_sys_pol_attr_rw(down_threshold); -gov_sys_pol_attr_rw(ignore_nice_load); -gov_sys_pol_attr_rw(freq_step); -gov_sys_pol_attr_ro(sampling_rate_min); - -static struct attribute *dbs_attributes_gov_sys[] = { - &sampling_rate_min_gov_sys.attr, - &sampling_rate_gov_sys.attr, - &sampling_down_factor_gov_sys.attr, - &up_threshold_gov_sys.attr, - &down_threshold_gov_sys.attr, - &ignore_nice_load_gov_sys.attr, - &freq_step_gov_sys.attr, - NULL -}; - -static struct attribute_group cs_attr_group_gov_sys = { - .attrs = dbs_attributes_gov_sys, - .name = "conservative", -}; - -static struct attribute *dbs_attributes_gov_pol[] = { - &sampling_rate_min_gov_pol.attr, - &sampling_rate_gov_pol.attr, - &sampling_down_factor_gov_pol.attr, - &up_threshold_gov_pol.attr, - &down_threshold_gov_pol.attr, - &ignore_nice_load_gov_pol.attr, - &freq_step_gov_pol.attr, - NULL -}; - -static struct attribute_group cs_attr_group_gov_pol = { - .attrs = dbs_attributes_gov_pol, - .name = "conservative", -}; - -/************************** sysfs end ************************/ - -static int cs_init(struct dbs_data *dbs_data, bool notify) -{ - struct cs_dbs_tuners *tuners; - - tuners = kzalloc(sizeof(*tuners), GFP_KERNEL); - if (!tuners) { - pr_err("%s: kzalloc failed\n", __func__); - return -ENOMEM; - } - - tuners->up_threshold = DEF_FREQUENCY_UP_THRESHOLD; - tuners->down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD; - tuners->sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR; - tuners->ignore_nice_load = 0; - tuners->freq_step = DEF_FREQUENCY_STEP; - - dbs_data->tuners = tuners; - dbs_data->min_sampling_rate = MIN_SAMPLING_RATE_RATIO * - jiffies_to_usecs(10); - - if (notify) - cpufreq_register_notifier(&cs_cpufreq_notifier_block, - CPUFREQ_TRANSITION_NOTIFIER); - - return 0; -} - -static void cs_exit(struct dbs_data *dbs_data, bool notify) -{ - if (notify) - cpufreq_unregister_notifier(&cs_cpufreq_notifier_block, - CPUFREQ_TRANSITION_NOTIFIER); - - kfree(dbs_data->tuners); -} - -define_get_cpu_dbs_routines(cs_cpu_dbs_info); - -static struct common_dbs_data cs_dbs_cdata = { - .governor = GOV_CONSERVATIVE, - .attr_group_gov_sys = &cs_attr_group_gov_sys, - .attr_group_gov_pol = &cs_attr_group_gov_pol, - .get_cpu_cdbs = get_cpu_cdbs, - .get_cpu_dbs_info_s = get_cpu_dbs_info_s, - .gov_dbs_timer = cs_dbs_timer, - .gov_check_cpu = cs_check_cpu, - .init = cs_init, - .exit = cs_exit, - .mutex = __MUTEX_INITIALIZER(cs_dbs_cdata.mutex), -}; - -static int cs_cpufreq_governor_dbs(struct cpufreq_policy *policy, - unsigned int event) -{ - return cpufreq_governor_dbs(policy, &cs_dbs_cdata, event); -} - -static int __init cpufreq_gov_dbs_init(void) -{ - return cpufreq_register_governor(&cpufreq_gov_conservative); -} - -static void __exit cpufreq_gov_dbs_exit(void) -{ - cpufreq_unregister_governor(&cpufreq_gov_conservative); -} - -MODULE_AUTHOR("Alexander Clouter "); -MODULE_DESCRIPTION("'cpufreq_conservative' - A dynamic cpufreq governor for " - "Low Latency Frequency Transition capable processors " - "optimised for use in a battery environment"); -MODULE_LICENSE("GPL"); - -#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE -fs_initcall(cpufreq_gov_dbs_init); -#else -module_init(cpufreq_gov_dbs_init); -#endif -module_exit(cpufreq_gov_dbs_exit); diff --git a/src/4.x/drivers/cpufreq/cpufreq_governor.c b/src/4.x/drivers/cpufreq/cpufreq_governor.c deleted file mode 100644 index d994b0f65..000000000 --- a/src/4.x/drivers/cpufreq/cpufreq_governor.c +++ /dev/null @@ -1,574 +0,0 @@ -/* - * drivers/cpufreq/cpufreq_governor.c - * - * CPUFREQ governors common code - * - * Copyright (C) 2001 Russell King - * (C) 2003 Venkatesh Pallipadi . - * (C) 2003 Jun Nakajima - * (C) 2009 Alexander Clouter - * (c) 2012 Viresh Kumar - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include - -#include "cpufreq_governor.h" - -static struct attribute_group *get_sysfs_attr(struct dbs_data *dbs_data) -{ - if (have_governor_per_policy()) - return dbs_data->cdata->attr_group_gov_pol; - else - return dbs_data->cdata->attr_group_gov_sys; -} - -void dbs_check_cpu(struct dbs_data *dbs_data, int cpu) -{ - struct cpu_dbs_info *cdbs = dbs_data->cdata->get_cpu_cdbs(cpu); - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - struct cpufreq_policy *policy = cdbs->shared->policy; - unsigned int sampling_rate; - unsigned int max_load = 0; - unsigned int ignore_nice; - unsigned int j; - - if (dbs_data->cdata->governor == GOV_ONDEMAND) { - struct od_cpu_dbs_info_s *od_dbs_info = - dbs_data->cdata->get_cpu_dbs_info_s(cpu); - - /* - * Sometimes, the ondemand governor uses an additional - * multiplier to give long delays. So apply this multiplier to - * the 'sampling_rate', so as to keep the wake-up-from-idle - * detection logic a bit conservative. - */ - sampling_rate = od_tuners->sampling_rate; - sampling_rate *= od_dbs_info->rate_mult; - - ignore_nice = od_tuners->ignore_nice_load; - } else { - sampling_rate = cs_tuners->sampling_rate; - ignore_nice = cs_tuners->ignore_nice_load; - } - - /* Get Absolute Load */ - for_each_cpu(j, policy->cpus) { - struct cpu_dbs_info *j_cdbs; - u64 cur_wall_time, cur_idle_time; - unsigned int idle_time, wall_time; - unsigned int load; - int io_busy = 0; - - j_cdbs = dbs_data->cdata->get_cpu_cdbs(j); - - /* - * For the purpose of ondemand, waiting for disk IO is - * an indication that you're performance critical, and - * not that the system is actually idle. So do not add - * the iowait time to the cpu idle time. - */ - if (dbs_data->cdata->governor == GOV_ONDEMAND) - io_busy = od_tuners->io_is_busy; - cur_idle_time = get_cpu_idle_time(j, &cur_wall_time, io_busy); - - wall_time = (unsigned int) - (cur_wall_time - j_cdbs->prev_cpu_wall); - j_cdbs->prev_cpu_wall = cur_wall_time; - - idle_time = (unsigned int) - (cur_idle_time - j_cdbs->prev_cpu_idle); - j_cdbs->prev_cpu_idle = cur_idle_time; - - if (ignore_nice) { - u64 cur_nice; - unsigned long cur_nice_jiffies; - - cur_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE] - - cdbs->prev_cpu_nice; - /* - * Assumption: nice time between sampling periods will - * be less than 2^32 jiffies for 32 bit sys - */ - cur_nice_jiffies = (unsigned long) - cputime64_to_jiffies64(cur_nice); - - cdbs->prev_cpu_nice = - kcpustat_cpu(j).cpustat[CPUTIME_NICE]; - idle_time += jiffies_to_usecs(cur_nice_jiffies); - } - - if (unlikely(!wall_time || wall_time < idle_time)) - continue; - - /* - * If the CPU had gone completely idle, and a task just woke up - * on this CPU now, it would be unfair to calculate 'load' the - * usual way for this elapsed time-window, because it will show - * near-zero load, irrespective of how CPU intensive that task - * actually is. This is undesirable for latency-sensitive bursty - * workloads. - * - * To avoid this, we reuse the 'load' from the previous - * time-window and give this task a chance to start with a - * reasonably high CPU frequency. (However, we shouldn't over-do - * this copy, lest we get stuck at a high load (high frequency) - * for too long, even when the current system load has actually - * dropped down. So we perform the copy only once, upon the - * first wake-up from idle.) - * - * Detecting this situation is easy: the governor's deferrable - * timer would not have fired during CPU-idle periods. Hence - * an unusually large 'wall_time' (as compared to the sampling - * rate) indicates this scenario. - * - * prev_load can be zero in two cases and we must recalculate it - * for both cases: - * - during long idle intervals - * - explicitly set to zero - */ - if (unlikely(wall_time > (2 * sampling_rate) && - j_cdbs->prev_load)) { - load = j_cdbs->prev_load; - - /* - * Perform a destructive copy, to ensure that we copy - * the previous load only once, upon the first wake-up - * from idle. - */ - j_cdbs->prev_load = 0; - } else { - load = 100 * (wall_time - idle_time) / wall_time; - j_cdbs->prev_load = load; - } - - if (load > max_load) - max_load = load; - } - - dbs_data->cdata->gov_check_cpu(cpu, max_load); -} -EXPORT_SYMBOL_GPL(dbs_check_cpu); - -static inline void __gov_queue_work(int cpu, struct dbs_data *dbs_data, - unsigned int delay) -{ - struct cpu_dbs_info *cdbs = dbs_data->cdata->get_cpu_cdbs(cpu); - - mod_delayed_work_on(cpu, system_wq, &cdbs->dwork, delay); -} - -void gov_queue_work(struct dbs_data *dbs_data, struct cpufreq_policy *policy, - unsigned int delay, bool all_cpus) -{ - int i; - - if (!all_cpus) { - /* - * Use raw_smp_processor_id() to avoid preemptible warnings. - * We know that this is only called with all_cpus == false from - * works that have been queued with *_work_on() functions and - * those works are canceled during CPU_DOWN_PREPARE so they - * can't possibly run on any other CPU. - */ - __gov_queue_work(raw_smp_processor_id(), dbs_data, delay); - } else { - for_each_cpu(i, policy->cpus) - __gov_queue_work(i, dbs_data, delay); - } -} -EXPORT_SYMBOL_GPL(gov_queue_work); - -static inline void gov_cancel_work(struct dbs_data *dbs_data, - struct cpufreq_policy *policy) -{ - struct cpu_dbs_info *cdbs; - int i; - - for_each_cpu(i, policy->cpus) { - cdbs = dbs_data->cdata->get_cpu_cdbs(i); - cancel_delayed_work_sync(&cdbs->dwork); - } -} - -/* Will return if we need to evaluate cpu load again or not */ -static bool need_load_eval(struct cpu_common_dbs_info *shared, - unsigned int sampling_rate) -{ - if (policy_is_shared(shared->policy)) { - ktime_t time_now = ktime_get(); - s64 delta_us = ktime_us_delta(time_now, shared->time_stamp); - - /* Do nothing if we recently have sampled */ - if (delta_us < (s64)(sampling_rate / 2)) - return false; - else - shared->time_stamp = time_now; - } - - return true; -} - -static void dbs_timer(struct work_struct *work) -{ - struct cpu_dbs_info *cdbs = container_of(work, struct cpu_dbs_info, - dwork.work); - struct cpu_common_dbs_info *shared = cdbs->shared; - struct cpufreq_policy *policy; - struct dbs_data *dbs_data; - unsigned int sampling_rate, delay; - bool modify_all = true; - - mutex_lock(&shared->timer_mutex); - - policy = shared->policy; - - /* - * Governor might already be disabled and there is no point continuing - * with the work-handler. - */ - if (!policy) - goto unlock; - - dbs_data = policy->governor_data; - - if (dbs_data->cdata->governor == GOV_CONSERVATIVE) { - struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - - sampling_rate = cs_tuners->sampling_rate; - } else { - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - - sampling_rate = od_tuners->sampling_rate; - } - - if (!need_load_eval(cdbs->shared, sampling_rate)) - modify_all = false; - - delay = dbs_data->cdata->gov_dbs_timer(cdbs, dbs_data, modify_all); - gov_queue_work(dbs_data, policy, delay, modify_all); - -unlock: - mutex_unlock(&shared->timer_mutex); -} - -static void set_sampling_rate(struct dbs_data *dbs_data, - unsigned int sampling_rate) -{ - if (dbs_data->cdata->governor == GOV_CONSERVATIVE) { - struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - cs_tuners->sampling_rate = sampling_rate; - } else { - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - od_tuners->sampling_rate = sampling_rate; - } -} - -static int alloc_common_dbs_info(struct cpufreq_policy *policy, - struct common_dbs_data *cdata) -{ - struct cpu_common_dbs_info *shared; - int j; - - /* Allocate memory for the common information for policy->cpus */ - shared = kzalloc(sizeof(*shared), GFP_KERNEL); - if (!shared) - return -ENOMEM; - - /* Set shared for all CPUs, online+offline */ - for_each_cpu(j, policy->related_cpus) - cdata->get_cpu_cdbs(j)->shared = shared; - - return 0; -} - -static void free_common_dbs_info(struct cpufreq_policy *policy, - struct common_dbs_data *cdata) -{ - struct cpu_dbs_info *cdbs = cdata->get_cpu_cdbs(policy->cpu); - struct cpu_common_dbs_info *shared = cdbs->shared; - int j; - - for_each_cpu(j, policy->cpus) - cdata->get_cpu_cdbs(j)->shared = NULL; - - kfree(shared); -} - -static int cpufreq_governor_init(struct cpufreq_policy *policy, - struct dbs_data *dbs_data, - struct common_dbs_data *cdata) -{ - unsigned int latency; - int ret; - - /* State should be equivalent to EXIT */ - if (policy->governor_data) - return -EBUSY; - - if (dbs_data) { - if (WARN_ON(have_governor_per_policy())) - return -EINVAL; - - ret = alloc_common_dbs_info(policy, cdata); - if (ret) - return ret; - - dbs_data->usage_count++; - policy->governor_data = dbs_data; - return 0; - } - - dbs_data = kzalloc(sizeof(*dbs_data), GFP_KERNEL); - if (!dbs_data) - return -ENOMEM; - - ret = alloc_common_dbs_info(policy, cdata); - if (ret) - goto free_dbs_data; - - dbs_data->cdata = cdata; - dbs_data->usage_count = 1; - - ret = cdata->init(dbs_data, !policy->governor->initialized); - if (ret) - goto free_common_dbs_info; - - /* policy latency is in ns. Convert it to us first */ - latency = policy->cpuinfo.transition_latency / 1000; - if (latency == 0) - latency = 1; - - /* Bring kernel and HW constraints together */ - dbs_data->min_sampling_rate = max(dbs_data->min_sampling_rate, - MIN_LATENCY_MULTIPLIER * latency); - set_sampling_rate(dbs_data, max(dbs_data->min_sampling_rate, - latency * LATENCY_MULTIPLIER)); - - if (!have_governor_per_policy()) - cdata->gdbs_data = dbs_data; - - policy->governor_data = dbs_data; - - ret = sysfs_create_group(get_governor_parent_kobj(policy), - get_sysfs_attr(dbs_data)); - if (ret) - goto reset_gdbs_data; - - return 0; - -reset_gdbs_data: - policy->governor_data = NULL; - - if (!have_governor_per_policy()) - cdata->gdbs_data = NULL; - cdata->exit(dbs_data, !policy->governor->initialized); -free_common_dbs_info: - free_common_dbs_info(policy, cdata); -free_dbs_data: - kfree(dbs_data); - return ret; -} - -static int cpufreq_governor_exit(struct cpufreq_policy *policy, - struct dbs_data *dbs_data) -{ - struct common_dbs_data *cdata = dbs_data->cdata; - struct cpu_dbs_info *cdbs = cdata->get_cpu_cdbs(policy->cpu); - - /* State should be equivalent to INIT */ - if (!cdbs->shared || cdbs->shared->policy) - return -EBUSY; - - if (!--dbs_data->usage_count) { - sysfs_remove_group(get_governor_parent_kobj(policy), - get_sysfs_attr(dbs_data)); - - policy->governor_data = NULL; - - if (!have_governor_per_policy()) - cdata->gdbs_data = NULL; - - cdata->exit(dbs_data, policy->governor->initialized == 1); - kfree(dbs_data); - } else { - policy->governor_data = NULL; - } - - free_common_dbs_info(policy, cdata); - return 0; -} - -static int cpufreq_governor_start(struct cpufreq_policy *policy, - struct dbs_data *dbs_data) -{ - struct common_dbs_data *cdata = dbs_data->cdata; - unsigned int sampling_rate, ignore_nice, j, cpu = policy->cpu; - struct cpu_dbs_info *cdbs = cdata->get_cpu_cdbs(cpu); - struct cpu_common_dbs_info *shared = cdbs->shared; - int io_busy = 0; - - if (!policy->cur) - return -EINVAL; - - /* State should be equivalent to INIT */ - if (!shared || shared->policy) - return -EBUSY; - - if (cdata->governor == GOV_CONSERVATIVE) { - struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - - sampling_rate = cs_tuners->sampling_rate; - ignore_nice = cs_tuners->ignore_nice_load; - } else { - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - - sampling_rate = od_tuners->sampling_rate; - ignore_nice = od_tuners->ignore_nice_load; - io_busy = od_tuners->io_is_busy; - } - - shared->policy = policy; - shared->time_stamp = ktime_get(); - mutex_init(&shared->timer_mutex); - - for_each_cpu(j, policy->cpus) { - struct cpu_dbs_info *j_cdbs = cdata->get_cpu_cdbs(j); - unsigned int prev_load; - - j_cdbs->prev_cpu_idle = - get_cpu_idle_time(j, &j_cdbs->prev_cpu_wall, io_busy); - - prev_load = (unsigned int)(j_cdbs->prev_cpu_wall - - j_cdbs->prev_cpu_idle); - j_cdbs->prev_load = 100 * prev_load / - (unsigned int)j_cdbs->prev_cpu_wall; - - if (ignore_nice) - j_cdbs->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; - - INIT_DEFERRABLE_WORK(&j_cdbs->dwork, dbs_timer); - } - - if (cdata->governor == GOV_CONSERVATIVE) { - struct cs_cpu_dbs_info_s *cs_dbs_info = - cdata->get_cpu_dbs_info_s(cpu); - - cs_dbs_info->down_skip = 0; - cs_dbs_info->requested_freq = policy->cur; - } else { - struct od_ops *od_ops = cdata->gov_ops; - struct od_cpu_dbs_info_s *od_dbs_info = cdata->get_cpu_dbs_info_s(cpu); - - od_dbs_info->rate_mult = 1; - od_dbs_info->sample_type = OD_NORMAL_SAMPLE; - od_ops->powersave_bias_init_cpu(cpu); - } - - gov_queue_work(dbs_data, policy, delay_for_sampling_rate(sampling_rate), - true); - return 0; -} - -static int cpufreq_governor_stop(struct cpufreq_policy *policy, - struct dbs_data *dbs_data) -{ - struct cpu_dbs_info *cdbs = dbs_data->cdata->get_cpu_cdbs(policy->cpu); - struct cpu_common_dbs_info *shared = cdbs->shared; - - /* State should be equivalent to START */ - if (!shared || !shared->policy) - return -EBUSY; - - /* - * Work-handler must see this updated, as it should not proceed any - * further after governor is disabled. And so timer_mutex is taken while - * updating this value. - */ - mutex_lock(&shared->timer_mutex); - shared->policy = NULL; - mutex_unlock(&shared->timer_mutex); - - gov_cancel_work(dbs_data, policy); - - mutex_destroy(&shared->timer_mutex); - return 0; -} - -static int cpufreq_governor_limits(struct cpufreq_policy *policy, - struct dbs_data *dbs_data) -{ - struct common_dbs_data *cdata = dbs_data->cdata; - unsigned int cpu = policy->cpu; - struct cpu_dbs_info *cdbs = cdata->get_cpu_cdbs(cpu); - - /* State should be equivalent to START */ - if (!cdbs->shared || !cdbs->shared->policy) - return -EBUSY; - - mutex_lock(&cdbs->shared->timer_mutex); - if (policy->max < cdbs->shared->policy->cur) - __cpufreq_driver_target(cdbs->shared->policy, policy->max, - CPUFREQ_RELATION_H); - else if (policy->min > cdbs->shared->policy->cur) - __cpufreq_driver_target(cdbs->shared->policy, policy->min, - CPUFREQ_RELATION_L); - dbs_check_cpu(dbs_data, cpu); - mutex_unlock(&cdbs->shared->timer_mutex); - - return 0; -} - -int cpufreq_governor_dbs(struct cpufreq_policy *policy, - struct common_dbs_data *cdata, unsigned int event) -{ - struct dbs_data *dbs_data; - int ret; - - /* Lock governor to block concurrent initialization of governor */ - mutex_lock(&cdata->mutex); - - if (have_governor_per_policy()) - dbs_data = policy->governor_data; - else - dbs_data = cdata->gdbs_data; - - if (!dbs_data && (event != CPUFREQ_GOV_POLICY_INIT)) { - ret = -EINVAL; - goto unlock; - } - - switch (event) { - case CPUFREQ_GOV_POLICY_INIT: - ret = cpufreq_governor_init(policy, dbs_data, cdata); - break; - case CPUFREQ_GOV_POLICY_EXIT: - ret = cpufreq_governor_exit(policy, dbs_data); - break; - case CPUFREQ_GOV_START: - ret = cpufreq_governor_start(policy, dbs_data); - break; - case CPUFREQ_GOV_STOP: - ret = cpufreq_governor_stop(policy, dbs_data); - break; - case CPUFREQ_GOV_LIMITS: - ret = cpufreq_governor_limits(policy, dbs_data); - break; - default: - ret = -EINVAL; - } - -unlock: - mutex_unlock(&cdata->mutex); - - return ret; -} -EXPORT_SYMBOL_GPL(cpufreq_governor_dbs); diff --git a/src/4.x/drivers/cpufreq/cpufreq_governor.h b/src/4.x/drivers/cpufreq/cpufreq_governor.h deleted file mode 100644 index f7b340c27..000000000 --- a/src/4.x/drivers/cpufreq/cpufreq_governor.h +++ /dev/null @@ -1,281 +0,0 @@ -/* - * drivers/cpufreq/cpufreq_governor.h - * - * Header file for CPUFreq governors common code - * - * Copyright (C) 2001 Russell King - * (C) 2003 Venkatesh Pallipadi . - * (C) 2003 Jun Nakajima - * (C) 2009 Alexander Clouter - * (c) 2012 Viresh Kumar - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _CPUFREQ_GOVERNOR_H -#define _CPUFREQ_GOVERNOR_H - -#include -#include -#include -#include - -/* - * The polling frequency depends on the capability of the processor. Default - * polling frequency is 1000 times the transition latency of the processor. The - * governor will work on any processor with transition latency <= 10ms, using - * appropriate sampling rate. - * - * For CPUs with transition latency > 10ms (mostly drivers with CPUFREQ_ETERNAL) - * this governor will not work. All times here are in us (micro seconds). - */ -#define MIN_SAMPLING_RATE_RATIO (2) -#define LATENCY_MULTIPLIER (1000) -#define MIN_LATENCY_MULTIPLIER (20) -#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) - -/* Ondemand Sampling types */ -enum {OD_NORMAL_SAMPLE, OD_SUB_SAMPLE}; - -/* - * Macro for creating governors sysfs routines - * - * - gov_sys: One governor instance per whole system - * - gov_pol: One governor instance per policy - */ - -/* Create attributes */ -#define gov_sys_attr_ro(_name) \ -static struct kobj_attribute _name##_gov_sys = \ -__ATTR(_name, 0444, show_##_name##_gov_sys, NULL) - -#define gov_sys_attr_rw(_name) \ -static struct kobj_attribute _name##_gov_sys = \ -__ATTR(_name, 0644, show_##_name##_gov_sys, store_##_name##_gov_sys) - -#define gov_pol_attr_ro(_name) \ -static struct freq_attr _name##_gov_pol = \ -__ATTR(_name, 0444, show_##_name##_gov_pol, NULL) - -#define gov_pol_attr_rw(_name) \ -static struct freq_attr _name##_gov_pol = \ -__ATTR(_name, 0644, show_##_name##_gov_pol, store_##_name##_gov_pol) - -#define gov_sys_pol_attr_rw(_name) \ - gov_sys_attr_rw(_name); \ - gov_pol_attr_rw(_name) - -#define gov_sys_pol_attr_ro(_name) \ - gov_sys_attr_ro(_name); \ - gov_pol_attr_ro(_name) - -/* Create show/store routines */ -#define show_one(_gov, file_name) \ -static ssize_t show_##file_name##_gov_sys \ -(struct kobject *kobj, struct kobj_attribute *attr, char *buf) \ -{ \ - struct _gov##_dbs_tuners *tuners = _gov##_dbs_cdata.gdbs_data->tuners; \ - return sprintf(buf, "%u\n", tuners->file_name); \ -} \ - \ -static ssize_t show_##file_name##_gov_pol \ -(struct cpufreq_policy *policy, char *buf) \ -{ \ - struct dbs_data *dbs_data = policy->governor_data; \ - struct _gov##_dbs_tuners *tuners = dbs_data->tuners; \ - return sprintf(buf, "%u\n", tuners->file_name); \ -} - -#define store_one(_gov, file_name) \ -static ssize_t store_##file_name##_gov_sys \ -(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) \ -{ \ - struct dbs_data *dbs_data = _gov##_dbs_cdata.gdbs_data; \ - return store_##file_name(dbs_data, buf, count); \ -} \ - \ -static ssize_t store_##file_name##_gov_pol \ -(struct cpufreq_policy *policy, const char *buf, size_t count) \ -{ \ - struct dbs_data *dbs_data = policy->governor_data; \ - return store_##file_name(dbs_data, buf, count); \ -} - -#define show_store_one(_gov, file_name) \ -show_one(_gov, file_name); \ -store_one(_gov, file_name) - -/* create helper routines */ -#define define_get_cpu_dbs_routines(_dbs_info) \ -static struct cpu_dbs_info *get_cpu_cdbs(int cpu) \ -{ \ - return &per_cpu(_dbs_info, cpu).cdbs; \ -} \ - \ -static void *get_cpu_dbs_info_s(int cpu) \ -{ \ - return &per_cpu(_dbs_info, cpu); \ -} - -/* - * Abbreviations: - * dbs: used as a shortform for demand based switching It helps to keep variable - * names smaller, simpler - * cdbs: common dbs - * od_*: On-demand governor - * cs_*: Conservative governor - */ - -/* Common to all CPUs of a policy */ -struct cpu_common_dbs_info { - struct cpufreq_policy *policy; - /* - * percpu mutex that serializes governor limit change with dbs_timer - * invocation. We do not want dbs_timer to run when user is changing - * the governor or limits. - */ - struct mutex timer_mutex; - ktime_t time_stamp; -}; - -/* Per cpu structures */ -struct cpu_dbs_info { - u64 prev_cpu_idle; - u64 prev_cpu_wall; - u64 prev_cpu_nice; - /* - * Used to keep track of load in the previous interval. However, when - * explicitly set to zero, it is used as a flag to ensure that we copy - * the previous load to the current interval only once, upon the first - * wake-up from idle. - */ - unsigned int prev_load; - struct delayed_work dwork; - struct cpu_common_dbs_info *shared; -}; - -struct od_cpu_dbs_info_s { - struct cpu_dbs_info cdbs; - struct cpufreq_frequency_table *freq_table; - unsigned int freq_lo; - unsigned int freq_lo_jiffies; - unsigned int freq_hi_jiffies; - unsigned int rate_mult; - unsigned int sample_type:1; -}; - -struct cs_cpu_dbs_info_s { - struct cpu_dbs_info cdbs; - unsigned int down_skip; - unsigned int requested_freq; -}; - -/* Per policy Governors sysfs tunables */ -struct od_dbs_tuners { - unsigned int ignore_nice_load; - unsigned int sampling_rate; - unsigned int sampling_down_factor; - unsigned int up_threshold; - unsigned int powersave_bias; - unsigned int io_is_busy; -}; - -struct cs_dbs_tuners { - unsigned int ignore_nice_load; - unsigned int sampling_rate; - unsigned int sampling_down_factor; - unsigned int up_threshold; - unsigned int down_threshold; - unsigned int freq_step; -}; - -/* Common Governor data across policies */ -struct dbs_data; -struct common_dbs_data { - /* Common across governors */ - #define GOV_ONDEMAND 0 - #define GOV_CONSERVATIVE 1 - int governor; - struct attribute_group *attr_group_gov_sys; /* one governor - system */ - struct attribute_group *attr_group_gov_pol; /* one governor - policy */ - - /* - * Common data for platforms that don't set - * CPUFREQ_HAVE_GOVERNOR_PER_POLICY - */ - struct dbs_data *gdbs_data; - - struct cpu_dbs_info *(*get_cpu_cdbs)(int cpu); - void *(*get_cpu_dbs_info_s)(int cpu); - unsigned int (*gov_dbs_timer)(struct cpu_dbs_info *cdbs, - struct dbs_data *dbs_data, - bool modify_all); - void (*gov_check_cpu)(int cpu, unsigned int load); - int (*init)(struct dbs_data *dbs_data, bool notify); - void (*exit)(struct dbs_data *dbs_data, bool notify); - - /* Governor specific ops, see below */ - void *gov_ops; - - /* - * Protects governor's data (struct dbs_data and struct common_dbs_data) - */ - struct mutex mutex; -}; - -/* Governor Per policy data */ -struct dbs_data { - struct common_dbs_data *cdata; - unsigned int min_sampling_rate; - int usage_count; - void *tuners; -}; - -/* Governor specific ops, will be passed to dbs_data->gov_ops */ -struct od_ops { - void (*powersave_bias_init_cpu)(int cpu); - unsigned int (*powersave_bias_target)(struct cpufreq_policy *policy, - unsigned int freq_next, unsigned int relation); - void (*freq_increase)(struct cpufreq_policy *policy, unsigned int freq); -}; - -static inline int delay_for_sampling_rate(unsigned int sampling_rate) -{ - int delay = usecs_to_jiffies(sampling_rate); - - /* We want all CPUs to do sampling nearly on same jiffy */ - if (num_online_cpus() > 1) - delay -= jiffies % delay; - - return delay; -} - -#define declare_show_sampling_rate_min(_gov) \ -static ssize_t show_sampling_rate_min_gov_sys \ -(struct kobject *kobj, struct kobj_attribute *attr, char *buf) \ -{ \ - struct dbs_data *dbs_data = _gov##_dbs_cdata.gdbs_data; \ - return sprintf(buf, "%u\n", dbs_data->min_sampling_rate); \ -} \ - \ -static ssize_t show_sampling_rate_min_gov_pol \ -(struct cpufreq_policy *policy, char *buf) \ -{ \ - struct dbs_data *dbs_data = policy->governor_data; \ - return sprintf(buf, "%u\n", dbs_data->min_sampling_rate); \ -} - -extern struct mutex cpufreq_governor_lock; - -void dbs_check_cpu(struct dbs_data *dbs_data, int cpu); -int cpufreq_governor_dbs(struct cpufreq_policy *policy, - struct common_dbs_data *cdata, unsigned int event); -void gov_queue_work(struct dbs_data *dbs_data, struct cpufreq_policy *policy, - unsigned int delay, bool all_cpus); -void od_register_powersave_bias_handler(unsigned int (*f) - (struct cpufreq_policy *, unsigned int, unsigned int), - unsigned int powersave_bias); -void od_unregister_powersave_bias_handler(void); -#endif /* _CPUFREQ_GOVERNOR_H */ diff --git a/src/4.x/drivers/cpufreq/cpufreq_ondemand.c b/src/4.x/drivers/cpufreq/cpufreq_ondemand.c deleted file mode 100644 index 03ac6ce54..000000000 --- a/src/4.x/drivers/cpufreq/cpufreq_ondemand.c +++ /dev/null @@ -1,620 +0,0 @@ -/* - * drivers/cpufreq/cpufreq_ondemand.c - * - * Copyright (C) 2001 Russell King - * (C) 2003 Venkatesh Pallipadi . - * Jun Nakajima - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include "cpufreq_governor.h" - -/* On-demand governor macros */ -#define DEF_FREQUENCY_UP_THRESHOLD (80) -#define DEF_SAMPLING_DOWN_FACTOR (1) -#define MAX_SAMPLING_DOWN_FACTOR (100000) -#define MICRO_FREQUENCY_UP_THRESHOLD (95) -#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) -#define MIN_FREQUENCY_UP_THRESHOLD (11) -#define MAX_FREQUENCY_UP_THRESHOLD (100) - -static DEFINE_PER_CPU(struct od_cpu_dbs_info_s, od_cpu_dbs_info); - -static struct od_ops od_ops; - -#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND -static struct cpufreq_governor cpufreq_gov_ondemand; -#endif - -static unsigned int default_powersave_bias; - -static void ondemand_powersave_bias_init_cpu(int cpu) -{ - struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu); - - dbs_info->freq_table = cpufreq_frequency_get_table(cpu); - dbs_info->freq_lo = 0; -} - -/* - * Not all CPUs want IO time to be accounted as busy; this depends on how - * efficient idling at a higher frequency/voltage is. - * Pavel Machek says this is not so for various generations of AMD and old - * Intel systems. - * Mike Chan (android.com) claims this is also not true for ARM. - * Because of this, whitelist specific known (series) of CPUs by default, and - * leave all others up to the user. - */ -static int should_io_be_busy(void) -{ -#if defined(CONFIG_X86) - /* - * For Intel, Core 2 (model 15) and later have an efficient idle. - */ - if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && - boot_cpu_data.x86 == 6 && - boot_cpu_data.x86_model >= 15) - return 1; -#endif - return 0; -} - -/* - * Find right freq to be set now with powersave_bias on. - * Returns the freq_hi to be used right now and will set freq_hi_jiffies, - * freq_lo, and freq_lo_jiffies in percpu area for averaging freqs. - */ -static unsigned int generic_powersave_bias_target(struct cpufreq_policy *policy, - unsigned int freq_next, unsigned int relation) -{ - unsigned int freq_req, freq_reduc, freq_avg; - unsigned int freq_hi, freq_lo; - unsigned int index = 0; - unsigned int jiffies_total, jiffies_hi, jiffies_lo; - struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, - policy->cpu); - struct dbs_data *dbs_data = policy->governor_data; - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - - if (!dbs_info->freq_table) { - dbs_info->freq_lo = 0; - dbs_info->freq_lo_jiffies = 0; - return freq_next; - } - - cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_next, - relation, &index); - freq_req = dbs_info->freq_table[index].frequency; - freq_reduc = freq_req * od_tuners->powersave_bias / 1000; - freq_avg = freq_req - freq_reduc; - - /* Find freq bounds for freq_avg in freq_table */ - index = 0; - cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_avg, - CPUFREQ_RELATION_H, &index); - freq_lo = dbs_info->freq_table[index].frequency; - index = 0; - cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_avg, - CPUFREQ_RELATION_L, &index); - freq_hi = dbs_info->freq_table[index].frequency; - - /* Find out how long we have to be in hi and lo freqs */ - if (freq_hi == freq_lo) { - dbs_info->freq_lo = 0; - dbs_info->freq_lo_jiffies = 0; - return freq_lo; - } - jiffies_total = usecs_to_jiffies(od_tuners->sampling_rate); - jiffies_hi = (freq_avg - freq_lo) * jiffies_total; - jiffies_hi += ((freq_hi - freq_lo) / 2); - jiffies_hi /= (freq_hi - freq_lo); - jiffies_lo = jiffies_total - jiffies_hi; - dbs_info->freq_lo = freq_lo; - dbs_info->freq_lo_jiffies = jiffies_lo; - dbs_info->freq_hi_jiffies = jiffies_hi; - return freq_hi; -} - -static void ondemand_powersave_bias_init(void) -{ - int i; - for_each_online_cpu(i) { - ondemand_powersave_bias_init_cpu(i); - } -} - -static void dbs_freq_increase(struct cpufreq_policy *policy, unsigned int freq) -{ - struct dbs_data *dbs_data = policy->governor_data; - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - - if (od_tuners->powersave_bias) - freq = od_ops.powersave_bias_target(policy, freq, - CPUFREQ_RELATION_H); - else if (policy->cur == policy->max) - return; - - __cpufreq_driver_target(policy, freq, od_tuners->powersave_bias ? - CPUFREQ_RELATION_L : CPUFREQ_RELATION_H); -} - -/* - * Every sampling_rate, we check, if current idle time is less than 20% - * (default), then we try to increase frequency. Else, we adjust the frequency - * proportional to load. - */ -static void od_check_cpu(int cpu, unsigned int load) -{ - struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu); - struct cpufreq_policy *policy = dbs_info->cdbs.shared->policy; - struct dbs_data *dbs_data = policy->governor_data; - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - - dbs_info->freq_lo = 0; - - /* Check for frequency increase */ - if (load > od_tuners->up_threshold) { - /* If switching to max speed, apply sampling_down_factor */ - if (policy->cur < policy->max) - dbs_info->rate_mult = - od_tuners->sampling_down_factor; - dbs_freq_increase(policy, policy->max); - } else { - /* Calculate the next frequency proportional to load */ - unsigned int freq_next, min_f, max_f; - - min_f = policy->cpuinfo.min_freq; - max_f = policy->cpuinfo.max_freq; - freq_next = min_f + load * (max_f - min_f) / 100; - - /* No longer fully busy, reset rate_mult */ - dbs_info->rate_mult = 1; - - if (!od_tuners->powersave_bias) { - __cpufreq_driver_target(policy, freq_next, - CPUFREQ_RELATION_C); - return; - } - - freq_next = od_ops.powersave_bias_target(policy, freq_next, - CPUFREQ_RELATION_L); - __cpufreq_driver_target(policy, freq_next, CPUFREQ_RELATION_C); - } -} - -static unsigned int od_dbs_timer(struct cpu_dbs_info *cdbs, - struct dbs_data *dbs_data, bool modify_all) -{ - struct cpufreq_policy *policy = cdbs->shared->policy; - unsigned int cpu = policy->cpu; - struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, - cpu); - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - int delay = 0, sample_type = dbs_info->sample_type; - - if (!modify_all) - goto max_delay; - - /* Common NORMAL_SAMPLE setup */ - dbs_info->sample_type = OD_NORMAL_SAMPLE; - if (sample_type == OD_SUB_SAMPLE) { - delay = dbs_info->freq_lo_jiffies; - __cpufreq_driver_target(policy, dbs_info->freq_lo, - CPUFREQ_RELATION_H); - } else { - dbs_check_cpu(dbs_data, cpu); - if (dbs_info->freq_lo) { - /* Setup timer for SUB_SAMPLE */ - dbs_info->sample_type = OD_SUB_SAMPLE; - delay = dbs_info->freq_hi_jiffies; - } - } - -max_delay: - if (!delay) - delay = delay_for_sampling_rate(od_tuners->sampling_rate - * dbs_info->rate_mult); - - return delay; -} - -/************************** sysfs interface ************************/ -static struct common_dbs_data od_dbs_cdata; - -/** - * update_sampling_rate - update sampling rate effective immediately if needed. - * @new_rate: new sampling rate - * - * If new rate is smaller than the old, simply updating - * dbs_tuners_int.sampling_rate might not be appropriate. For example, if the - * original sampling_rate was 1 second and the requested new sampling rate is 10 - * ms because the user needs immediate reaction from ondemand governor, but not - * sure if higher frequency will be required or not, then, the governor may - * change the sampling rate too late; up to 1 second later. Thus, if we are - * reducing the sampling rate, we need to make the new value effective - * immediately. - */ -static void update_sampling_rate(struct dbs_data *dbs_data, - unsigned int new_rate) -{ - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - int cpu; - - od_tuners->sampling_rate = new_rate = max(new_rate, - dbs_data->min_sampling_rate); - - for_each_online_cpu(cpu) { - struct cpufreq_policy *policy; - struct od_cpu_dbs_info_s *dbs_info; - unsigned long next_sampling, appointed_at; - - policy = cpufreq_cpu_get(cpu); - if (!policy) - continue; - if (policy->governor != &cpufreq_gov_ondemand) { - cpufreq_cpu_put(policy); - continue; - } - dbs_info = &per_cpu(od_cpu_dbs_info, cpu); - cpufreq_cpu_put(policy); - - if (!delayed_work_pending(&dbs_info->cdbs.dwork)) - continue; - - next_sampling = jiffies + usecs_to_jiffies(new_rate); - appointed_at = dbs_info->cdbs.dwork.timer.expires; - - if (time_before(next_sampling, appointed_at)) { - cancel_delayed_work_sync(&dbs_info->cdbs.dwork); - - gov_queue_work(dbs_data, policy, - usecs_to_jiffies(new_rate), true); - - } - } -} - -static ssize_t store_sampling_rate(struct dbs_data *dbs_data, const char *buf, - size_t count) -{ - unsigned int input; - int ret; - ret = sscanf(buf, "%u", &input); - if (ret != 1) - return -EINVAL; - - update_sampling_rate(dbs_data, input); - return count; -} - -static ssize_t store_io_is_busy(struct dbs_data *dbs_data, const char *buf, - size_t count) -{ - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - unsigned int input; - int ret; - unsigned int j; - - ret = sscanf(buf, "%u", &input); - if (ret != 1) - return -EINVAL; - od_tuners->io_is_busy = !!input; - - /* we need to re-evaluate prev_cpu_idle */ - for_each_online_cpu(j) { - struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, - j); - dbs_info->cdbs.prev_cpu_idle = get_cpu_idle_time(j, - &dbs_info->cdbs.prev_cpu_wall, od_tuners->io_is_busy); - } - return count; -} - -static ssize_t store_up_threshold(struct dbs_data *dbs_data, const char *buf, - size_t count) -{ - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - unsigned int input; - int ret; - ret = sscanf(buf, "%u", &input); - - if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || - input < MIN_FREQUENCY_UP_THRESHOLD) { - return -EINVAL; - } - - od_tuners->up_threshold = input; - return count; -} - -static ssize_t store_sampling_down_factor(struct dbs_data *dbs_data, - const char *buf, size_t count) -{ - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - unsigned int input, j; - int ret; - ret = sscanf(buf, "%u", &input); - - if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1) - return -EINVAL; - od_tuners->sampling_down_factor = input; - - /* Reset down sampling multiplier in case it was active */ - for_each_online_cpu(j) { - struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, - j); - dbs_info->rate_mult = 1; - } - return count; -} - -static ssize_t store_ignore_nice_load(struct dbs_data *dbs_data, - const char *buf, size_t count) -{ - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - unsigned int input; - int ret; - - unsigned int j; - - ret = sscanf(buf, "%u", &input); - if (ret != 1) - return -EINVAL; - - if (input > 1) - input = 1; - - if (input == od_tuners->ignore_nice_load) { /* nothing to do */ - return count; - } - od_tuners->ignore_nice_load = input; - - /* we need to re-evaluate prev_cpu_idle */ - for_each_online_cpu(j) { - struct od_cpu_dbs_info_s *dbs_info; - dbs_info = &per_cpu(od_cpu_dbs_info, j); - dbs_info->cdbs.prev_cpu_idle = get_cpu_idle_time(j, - &dbs_info->cdbs.prev_cpu_wall, od_tuners->io_is_busy); - if (od_tuners->ignore_nice_load) - dbs_info->cdbs.prev_cpu_nice = - kcpustat_cpu(j).cpustat[CPUTIME_NICE]; - - } - return count; -} - -static ssize_t store_powersave_bias(struct dbs_data *dbs_data, const char *buf, - size_t count) -{ - struct od_dbs_tuners *od_tuners = dbs_data->tuners; - unsigned int input; - int ret; - ret = sscanf(buf, "%u", &input); - - if (ret != 1) - return -EINVAL; - - if (input > 1000) - input = 1000; - - od_tuners->powersave_bias = input; - ondemand_powersave_bias_init(); - return count; -} - -show_store_one(od, sampling_rate); -show_store_one(od, io_is_busy); -show_store_one(od, up_threshold); -show_store_one(od, sampling_down_factor); -show_store_one(od, ignore_nice_load); -show_store_one(od, powersave_bias); -declare_show_sampling_rate_min(od); - -gov_sys_pol_attr_rw(sampling_rate); -gov_sys_pol_attr_rw(io_is_busy); -gov_sys_pol_attr_rw(up_threshold); -gov_sys_pol_attr_rw(sampling_down_factor); -gov_sys_pol_attr_rw(ignore_nice_load); -gov_sys_pol_attr_rw(powersave_bias); -gov_sys_pol_attr_ro(sampling_rate_min); - -static struct attribute *dbs_attributes_gov_sys[] = { - &sampling_rate_min_gov_sys.attr, - &sampling_rate_gov_sys.attr, - &up_threshold_gov_sys.attr, - &sampling_down_factor_gov_sys.attr, - &ignore_nice_load_gov_sys.attr, - &powersave_bias_gov_sys.attr, - &io_is_busy_gov_sys.attr, - NULL -}; - -static struct attribute_group od_attr_group_gov_sys = { - .attrs = dbs_attributes_gov_sys, - .name = "ondemand", -}; - -static struct attribute *dbs_attributes_gov_pol[] = { - &sampling_rate_min_gov_pol.attr, - &sampling_rate_gov_pol.attr, - &up_threshold_gov_pol.attr, - &sampling_down_factor_gov_pol.attr, - &ignore_nice_load_gov_pol.attr, - &powersave_bias_gov_pol.attr, - &io_is_busy_gov_pol.attr, - NULL -}; - -static struct attribute_group od_attr_group_gov_pol = { - .attrs = dbs_attributes_gov_pol, - .name = "ondemand", -}; - -/************************** sysfs end ************************/ - -static int od_init(struct dbs_data *dbs_data, bool notify) -{ - struct od_dbs_tuners *tuners; - u64 idle_time; - int cpu; - - tuners = kzalloc(sizeof(*tuners), GFP_KERNEL); - if (!tuners) { - pr_err("%s: kzalloc failed\n", __func__); - return -ENOMEM; - } - - cpu = get_cpu(); - idle_time = get_cpu_idle_time_us(cpu, NULL); - put_cpu(); - if (idle_time != -1ULL) { - /* Idle micro accounting is supported. Use finer thresholds */ - tuners->up_threshold = MICRO_FREQUENCY_UP_THRESHOLD; - /* - * In nohz/micro accounting case we set the minimum frequency - * not depending on HZ, but fixed (very low). The deferred - * timer might skip some samples if idle/sleeping as needed. - */ - dbs_data->min_sampling_rate = MICRO_FREQUENCY_MIN_SAMPLE_RATE; - } else { - tuners->up_threshold = DEF_FREQUENCY_UP_THRESHOLD; - - /* For correct statistics, we need 10 ticks for each measure */ - dbs_data->min_sampling_rate = MIN_SAMPLING_RATE_RATIO * - jiffies_to_usecs(10); - } - - tuners->sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR; - tuners->ignore_nice_load = 0; - tuners->powersave_bias = default_powersave_bias; - tuners->io_is_busy = should_io_be_busy(); - - dbs_data->tuners = tuners; - return 0; -} - -static void od_exit(struct dbs_data *dbs_data, bool notify) -{ - kfree(dbs_data->tuners); -} - -define_get_cpu_dbs_routines(od_cpu_dbs_info); - -static struct od_ops od_ops = { - .powersave_bias_init_cpu = ondemand_powersave_bias_init_cpu, - .powersave_bias_target = generic_powersave_bias_target, - .freq_increase = dbs_freq_increase, -}; - -static struct common_dbs_data od_dbs_cdata = { - .governor = GOV_ONDEMAND, - .attr_group_gov_sys = &od_attr_group_gov_sys, - .attr_group_gov_pol = &od_attr_group_gov_pol, - .get_cpu_cdbs = get_cpu_cdbs, - .get_cpu_dbs_info_s = get_cpu_dbs_info_s, - .gov_dbs_timer = od_dbs_timer, - .gov_check_cpu = od_check_cpu, - .gov_ops = &od_ops, - .init = od_init, - .exit = od_exit, - .mutex = __MUTEX_INITIALIZER(od_dbs_cdata.mutex), -}; - -static void od_set_powersave_bias(unsigned int powersave_bias) -{ - struct cpufreq_policy *policy; - struct dbs_data *dbs_data; - struct od_dbs_tuners *od_tuners; - unsigned int cpu; - cpumask_t done; - - default_powersave_bias = powersave_bias; - cpumask_clear(&done); - - get_online_cpus(); - for_each_online_cpu(cpu) { - struct cpu_common_dbs_info *shared; - - if (cpumask_test_cpu(cpu, &done)) - continue; - - shared = per_cpu(od_cpu_dbs_info, cpu).cdbs.shared; - if (!shared) - continue; - - policy = shared->policy; - cpumask_or(&done, &done, policy->cpus); - - if (policy->governor != &cpufreq_gov_ondemand) - continue; - - dbs_data = policy->governor_data; - od_tuners = dbs_data->tuners; - od_tuners->powersave_bias = default_powersave_bias; - } - put_online_cpus(); -} - -void od_register_powersave_bias_handler(unsigned int (*f) - (struct cpufreq_policy *, unsigned int, unsigned int), - unsigned int powersave_bias) -{ - od_ops.powersave_bias_target = f; - od_set_powersave_bias(powersave_bias); -} -EXPORT_SYMBOL_GPL(od_register_powersave_bias_handler); - -void od_unregister_powersave_bias_handler(void) -{ - od_ops.powersave_bias_target = generic_powersave_bias_target; - od_set_powersave_bias(0); -} -EXPORT_SYMBOL_GPL(od_unregister_powersave_bias_handler); - -static int od_cpufreq_governor_dbs(struct cpufreq_policy *policy, - unsigned int event) -{ - return cpufreq_governor_dbs(policy, &od_dbs_cdata, event); -} - -#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND -static -#endif -struct cpufreq_governor cpufreq_gov_ondemand = { - .name = "ondemand", - .governor = od_cpufreq_governor_dbs, - .max_transition_latency = TRANSITION_LATENCY_LIMIT, - .owner = THIS_MODULE, -}; - -static int __init cpufreq_gov_dbs_init(void) -{ - return cpufreq_register_governor(&cpufreq_gov_ondemand); -} - -static void __exit cpufreq_gov_dbs_exit(void) -{ - cpufreq_unregister_governor(&cpufreq_gov_ondemand); -} - -MODULE_AUTHOR("Venkatesh Pallipadi "); -MODULE_AUTHOR("Alexey Starikovskiy "); -MODULE_DESCRIPTION("'cpufreq_ondemand' - A dynamic cpufreq governor for " - "Low Latency Frequency Transition capable processors"); -MODULE_LICENSE("GPL"); - -#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND -fs_initcall(cpufreq_gov_dbs_init); -#else -module_init(cpufreq_gov_dbs_init); -#endif -module_exit(cpufreq_gov_dbs_exit); diff --git a/src/4.x/drivers/gpu/Makefile b/src/4.x/drivers/gpu/Makefile deleted file mode 100644 index 91b92cb2d..000000000 --- a/src/4.x/drivers/gpu/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# drm/tegra depends on host1x, so if both drivers are built-in care must be -# taken to initialize them in the correct order. Link order is the only way -# to ensure this currently. -#obj-$(CONFIG_TEGRA_HOST1X) += host1x/ -obj-y += drm/ -# vga/ -#obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ diff --git a/src/4.x/drivers/gpu/drm/Makefile b/src/4.x/drivers/gpu/drm/Makefile deleted file mode 100644 index 760df6d62..000000000 --- a/src/4.x/drivers/gpu/drm/Makefile +++ /dev/null @@ -1,77 +0,0 @@ -# -# Makefile for the drm device driver. This driver provides support for the -# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. - -drm-y := drm_auth.o drm_bufs.o drm_cache.o \ - drm_context.o drm_dma.o \ - drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \ - drm_lock.o drm_memory.o drm_drv.o drm_vm.o \ - drm_scatter.o drm_pci.o \ - drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \ - drm_crtc.o drm_modes.o drm_edid.o \ - drm_info.o drm_debugfs.o drm_encoder_slave.o \ - drm_trace_points.o drm_global.o drm_prime.o \ - drm_rect.o drm_vma_manager.o drm_flip_work.o \ - drm_modeset_lock.o drm_atomic.o drm_bridge.o - -drm-$(CONFIG_COMPAT) += drm_ioc32.o -drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o -drm-$(CONFIG_PCI) += ati_pcigart.o -drm-$(CONFIG_DRM_PANEL) += drm_panel.o -drm-$(CONFIG_OF) += drm_of.o -drm-$(CONFIG_AGP) += drm_agpsupport.o - -drm-y += $(drm-m) - -drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ - drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o -drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o -drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o -drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o - -obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o - -CFLAGS_drm_trace_points.o := -I$(src) - -obj-$(CONFIG_DRM) += drm.o -#obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o -#obj-$(CONFIG_DRM_TTM) += ttm/ -#obj-$(CONFIG_DRM_TDFX) += tdfx/ -#obj-$(CONFIG_DRM_R128) += r128/ -#obj-$(CONFIG_HSA_AMD) += amd/amdkfd/ -#obj-$(CONFIG_DRM_RADEON)+= radeon/ -#obj-$(CONFIG_DRM_AMDGPU)+= amd/amdgpu/ -#obj-$(CONFIG_DRM_MGA) += mga/ -#obj-$(CONFIG_DRM_I810) += i810/ -#obj-$(CONFIG_DRM_I915) += i915/ -#obj-$(CONFIG_DRM_MGAG200) += mgag200/ -#obj-$(CONFIG_DRM_VC4) += vc4/ -#obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/ -#obj-$(CONFIG_DRM_SIS) += sis/ -#obj-$(CONFIG_DRM_SAVAGE)+= savage/ -#obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/ -#obj-$(CONFIG_DRM_VIA) +=via/ -#obj-$(CONFIG_DRM_VGEM) += vgem/ -#obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ -#obj-$(CONFIG_DRM_EXYNOS) +=exynos/ -#obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/ -#obj-$(CONFIG_DRM_GMA500) += gma500/ -#obj-$(CONFIG_DRM_UDL) += udl/ -#obj-$(CONFIG_DRM_AST) += ast/ -#obj-$(CONFIG_DRM_ARMADA) += armada/ -#obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/ -#obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ -#obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ -#obj-$(CONFIG_DRM_OMAP) += omapdrm/ -#obj-y += tilcdc/ -#obj-$(CONFIG_DRM_QXL) += qxl/ -#obj-$(CONFIG_DRM_BOCHS) += bochs/ -#obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/ -#obj-$(CONFIG_DRM_MSM) += msm/ -#obj-$(CONFIG_DRM_TEGRA) += tegra/ -#obj-$(CONFIG_DRM_STI) += sti/ -#obj-$(CONFIG_DRM_IMX) += imx/ -#obj-y += i2c/ -#obj-y += panel/ -#obj-y += bridge/ -#obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ diff --git a/src/4.x/drivers/gpu/drm/drm_agpsupport.c b/src/4.x/drivers/gpu/drm/drm_agpsupport.c deleted file mode 100644 index a10ea6aec..000000000 --- a/src/4.x/drivers/gpu/drm/drm_agpsupport.c +++ /dev/null @@ -1,502 +0,0 @@ -/** - * \file drm_agpsupport.c - * DRM support for AGP/GART backend - * - * \author Rickard E. (Rik) Faith - * \author Gareth Hughes - */ - -/* - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include "drm_legacy.h" - -#include - -/** - * Get AGP information. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a (output) drm_agp_info structure. - * \return zero on success or a negative number on failure. - * - * Verifies the AGP device has been initialized and acquired and fills in the - * drm_agp_info structure with the information in drm_agp_head::agp_info. - */ -int drm_agp_info(struct drm_device *dev, struct drm_agp_info *info) -{ - struct agp_kern_info *kern; - - if (!dev->agp || !dev->agp->acquired) - return -EINVAL; - - kern = &dev->agp->agp_info; - info->agp_version_major = kern->version.major; - info->agp_version_minor = kern->version.minor; - info->mode = kern->mode; - info->aperture_base = kern->aper_base; - info->aperture_size = kern->aper_size * 1024 * 1024; - info->memory_allowed = kern->max_memory << PAGE_SHIFT; - info->memory_used = kern->current_memory << PAGE_SHIFT; - info->id_vendor = kern->device->vendor; - info->id_device = kern->device->device; - - return 0; -} - -EXPORT_SYMBOL(drm_agp_info); - -int drm_agp_info_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_agp_info *info = data; - int err; - - err = drm_agp_info(dev, info); - if (err) - return err; - - return 0; -} - -/** - * Acquire the AGP device. - * - * \param dev DRM device that is to acquire AGP. - * \return zero on success or a negative number on failure. - * - * Verifies the AGP device hasn't been acquired before and calls - * \c agp_backend_acquire. - */ -int drm_agp_acquire(struct drm_device * dev) -{ - if (!dev->agp) - return -ENODEV; - if (dev->agp->acquired) - return -EBUSY; - if (!(dev->agp->bridge = agp_backend_acquire(dev->pdev))) - return -ENODEV; - dev->agp->acquired = 1; - return 0; -} - -EXPORT_SYMBOL(drm_agp_acquire); - -/** - * Acquire the AGP device (ioctl). - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument. - * \return zero on success or a negative number on failure. - * - * Verifies the AGP device hasn't been acquired before and calls - * \c agp_backend_acquire. - */ -int drm_agp_acquire_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - return drm_agp_acquire((struct drm_device *) file_priv->minor->dev); -} - -/** - * Release the AGP device. - * - * \param dev DRM device that is to release AGP. - * \return zero on success or a negative number on failure. - * - * Verifies the AGP device has been acquired and calls \c agp_backend_release. - */ -int drm_agp_release(struct drm_device * dev) -{ - if (!dev->agp || !dev->agp->acquired) - return -EINVAL; - agp_backend_release(dev->agp->bridge); - dev->agp->acquired = 0; - return 0; -} -EXPORT_SYMBOL(drm_agp_release); - -int drm_agp_release_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - return drm_agp_release(dev); -} - -/** - * Enable the AGP bus. - * - * \param dev DRM device that has previously acquired AGP. - * \param mode Requested AGP mode. - * \return zero on success or a negative number on failure. - * - * Verifies the AGP device has been acquired but not enabled, and calls - * \c agp_enable. - */ -int drm_agp_enable(struct drm_device * dev, struct drm_agp_mode mode) -{ - if (!dev->agp || !dev->agp->acquired) - return -EINVAL; - - dev->agp->mode = mode.mode; - agp_enable(dev->agp->bridge, mode.mode); - dev->agp->enabled = 1; - return 0; -} - -EXPORT_SYMBOL(drm_agp_enable); - -int drm_agp_enable_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_agp_mode *mode = data; - - return drm_agp_enable(dev, *mode); -} - -/** - * Allocate AGP memory. - * - * \param inode device inode. - * \param file_priv file private pointer. - * \param cmd command. - * \param arg pointer to a drm_agp_buffer structure. - * \return zero on success or a negative number on failure. - * - * Verifies the AGP device is present and has been acquired, allocates the - * memory via agp_allocate_memory() and creates a drm_agp_mem entry for it. - */ -int drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request) -{ - struct drm_agp_mem *entry; - struct agp_memory *memory; - unsigned long pages; - u32 type; - - if (!dev->agp || !dev->agp->acquired) - return -EINVAL; - if (!(entry = kzalloc(sizeof(*entry), GFP_KERNEL))) - return -ENOMEM; - - pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE; - type = (u32) request->type; - if (!(memory = agp_allocate_memory(dev->agp->bridge, pages, type))) { - kfree(entry); - return -ENOMEM; - } - - entry->handle = (unsigned long)memory->key + 1; - entry->memory = memory; - entry->bound = 0; - entry->pages = pages; - list_add(&entry->head, &dev->agp->memory); - - request->handle = entry->handle; - request->physical = memory->physical; - - return 0; -} -EXPORT_SYMBOL(drm_agp_alloc); - - -int drm_agp_alloc_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_agp_buffer *request = data; - - return drm_agp_alloc(dev, request); -} - -/** - * Search for the AGP memory entry associated with a handle. - * - * \param dev DRM device structure. - * \param handle AGP memory handle. - * \return pointer to the drm_agp_mem structure associated with \p handle. - * - * Walks through drm_agp_head::memory until finding a matching handle. - */ -static struct drm_agp_mem *drm_agp_lookup_entry(struct drm_device * dev, - unsigned long handle) -{ - struct drm_agp_mem *entry; - - list_for_each_entry(entry, &dev->agp->memory, head) { - if (entry->handle == handle) - return entry; - } - return NULL; -} - -/** - * Unbind AGP memory from the GATT (ioctl). - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a drm_agp_binding structure. - * \return zero on success or a negative number on failure. - * - * Verifies the AGP device is present and acquired, looks-up the AGP memory - * entry and passes it to the unbind_agp() function. - */ -int drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request) -{ - struct drm_agp_mem *entry; - int ret; - - if (!dev->agp || !dev->agp->acquired) - return -EINVAL; - if (!(entry = drm_agp_lookup_entry(dev, request->handle))) - return -EINVAL; - if (!entry->bound) - return -EINVAL; - ret = drm_unbind_agp(entry->memory); - if (ret == 0) - entry->bound = 0; - return ret; -} -EXPORT_SYMBOL(drm_agp_unbind); - - -int drm_agp_unbind_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_agp_binding *request = data; - - return drm_agp_unbind(dev, request); -} - -/** - * Bind AGP memory into the GATT (ioctl) - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a drm_agp_binding structure. - * \return zero on success or a negative number on failure. - * - * Verifies the AGP device is present and has been acquired and that no memory - * is currently bound into the GATT. Looks-up the AGP memory entry and passes - * it to bind_agp() function. - */ -int drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request) -{ - struct drm_agp_mem *entry; - int retcode; - int page; - - if (!dev->agp || !dev->agp->acquired) - return -EINVAL; - if (!(entry = drm_agp_lookup_entry(dev, request->handle))) - return -EINVAL; - if (entry->bound) - return -EINVAL; - page = (request->offset + PAGE_SIZE - 1) / PAGE_SIZE; - if ((retcode = drm_bind_agp(entry->memory, page))) - return retcode; - entry->bound = dev->agp->base + (page << PAGE_SHIFT); - DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n", - dev->agp->base, entry->bound); - return 0; -} -EXPORT_SYMBOL(drm_agp_bind); - - -int drm_agp_bind_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_agp_binding *request = data; - - return drm_agp_bind(dev, request); -} - -/** - * Free AGP memory (ioctl). - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a drm_agp_buffer structure. - * \return zero on success or a negative number on failure. - * - * Verifies the AGP device is present and has been acquired and looks up the - * AGP memory entry. If the memory it's currently bound, unbind it via - * unbind_agp(). Frees it via free_agp() as well as the entry itself - * and unlinks from the doubly linked list it's inserted in. - */ -int drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request) -{ - struct drm_agp_mem *entry; - - if (!dev->agp || !dev->agp->acquired) - return -EINVAL; - if (!(entry = drm_agp_lookup_entry(dev, request->handle))) - return -EINVAL; - if (entry->bound) - drm_unbind_agp(entry->memory); - - list_del(&entry->head); - - drm_free_agp(entry->memory, entry->pages); - kfree(entry); - return 0; -} -EXPORT_SYMBOL(drm_agp_free); - - - -int drm_agp_free_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_agp_buffer *request = data; - - return drm_agp_free(dev, request); -} - -/** - * Initialize the AGP resources. - * - * \return pointer to a drm_agp_head structure. - * - * Gets the drm_agp_t structure which is made available by the agpgart module - * via the inter_module_* functions. Creates and initializes a drm_agp_head - * structure. - * - * Note that final cleanup of the kmalloced structure is directly done in - * drm_pci_agp_destroy. - */ -struct drm_agp_head *drm_agp_init(struct drm_device *dev) -{ - struct drm_agp_head *head = NULL; - - if (!(head = kzalloc(sizeof(*head), GFP_KERNEL))) - return NULL; - head->bridge = agp_find_bridge(dev->pdev); - if (!head->bridge) { - if (!(head->bridge = agp_backend_acquire(dev->pdev))) { - kfree(head); - return NULL; - } - agp_copy_info(head->bridge, &head->agp_info); - agp_backend_release(head->bridge); - } else { - agp_copy_info(head->bridge, &head->agp_info); - } - if (head->agp_info.chipset == NOT_SUPPORTED) { - kfree(head); - return NULL; - } - INIT_LIST_HEAD(&head->memory); - head->cant_use_aperture = head->agp_info.cant_use_aperture; - head->page_mask = head->agp_info.page_mask; - head->base = head->agp_info.aper_base; - return head; -} - -/** - * drm_agp_clear - Clear AGP resource list - * @dev: DRM device - * - * Iterate over all AGP resources and remove them. But keep the AGP head - * intact so it can still be used. It is safe to call this if AGP is disabled or - * was already removed. - * - * If DRIVER_MODESET is active, nothing is done to protect the modesetting - * resources from getting destroyed. Drivers are responsible of cleaning them up - * during device shutdown. - */ -void drm_agp_clear(struct drm_device *dev) -{ - struct drm_agp_mem *entry, *tempe; - - if (!dev->agp) - return; - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return; - - list_for_each_entry_safe(entry, tempe, &dev->agp->memory, head) { - if (entry->bound) - drm_unbind_agp(entry->memory); - drm_free_agp(entry->memory, entry->pages); - kfree(entry); - } - INIT_LIST_HEAD(&dev->agp->memory); - - if (dev->agp->acquired) - drm_agp_release(dev); - - dev->agp->acquired = 0; - dev->agp->enabled = 0; -} - -/** - * Binds a collection of pages into AGP memory at the given offset, returning - * the AGP memory structure containing them. - * - * No reference is held on the pages during this time -- it is up to the - * caller to handle that. - */ -struct agp_memory * -drm_agp_bind_pages(struct drm_device *dev, - struct page **pages, - unsigned long num_pages, - uint32_t gtt_offset, - u32 type) -{ - struct agp_memory *mem; - int ret, i; - - DRM_DEBUG("\n"); - - mem = agp_allocate_memory(dev->agp->bridge, num_pages, - type); - if (mem == NULL) { - DRM_ERROR("Failed to allocate memory for %ld pages\n", - num_pages); - return NULL; - } - - for (i = 0; i < num_pages; i++) - mem->pages[i] = pages[i]; - mem->page_count = num_pages; - - mem->is_flushed = true; - ret = agp_bind_memory(mem, gtt_offset / PAGE_SIZE); - if (ret != 0) { - DRM_ERROR("Failed to bind AGP memory: %d\n", ret); - agp_free_memory(mem); - return NULL; - } - - return mem; -} -EXPORT_SYMBOL(drm_agp_bind_pages); diff --git a/src/4.x/drivers/gpu/drm/drm_atomic.c b/src/4.x/drivers/gpu/drm/drm_atomic.c deleted file mode 100644 index 355ad1b97..000000000 --- a/src/4.x/drivers/gpu/drm/drm_atomic.c +++ /dev/null @@ -1,1656 +0,0 @@ -/* - * Copyright (C) 2014 Red Hat - * Copyright (C) 2014 Intel Corp. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rob Clark - * Daniel Vetter - */ - - -#include -#include -#include - -/** - * drm_atomic_state_default_release - - * release memory initialized by drm_atomic_state_init - * @state: atomic state - * - * Free all the memory allocated by drm_atomic_state_init. - * This is useful for drivers that subclass the atomic state. - */ -void drm_atomic_state_default_release(struct drm_atomic_state *state) -{ - kfree(state->connectors); - kfree(state->connector_states); - kfree(state->crtcs); - kfree(state->crtc_states); - kfree(state->planes); - kfree(state->plane_states); -} -EXPORT_SYMBOL(drm_atomic_state_default_release); - -/** - * drm_atomic_state_init - init new atomic state - * @dev: DRM device - * @state: atomic state - * - * Default implementation for filling in a new atomic state. - * This is useful for drivers that subclass the atomic state. - */ -int -drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) -{ - /* TODO legacy paths should maybe do a better job about - * setting this appropriately? - */ - state->allow_modeset = true; - - state->num_connector = ACCESS_ONCE(dev->mode_config.num_connector); - - state->crtcs = kcalloc(dev->mode_config.num_crtc, - sizeof(*state->crtcs), GFP_KERNEL); - if (!state->crtcs) - goto fail; - state->crtc_states = kcalloc(dev->mode_config.num_crtc, - sizeof(*state->crtc_states), GFP_KERNEL); - if (!state->crtc_states) - goto fail; - state->planes = kcalloc(dev->mode_config.num_total_plane, - sizeof(*state->planes), GFP_KERNEL); - if (!state->planes) - goto fail; - state->plane_states = kcalloc(dev->mode_config.num_total_plane, - sizeof(*state->plane_states), GFP_KERNEL); - if (!state->plane_states) - goto fail; - state->connectors = kcalloc(state->num_connector, - sizeof(*state->connectors), - GFP_KERNEL); - if (!state->connectors) - goto fail; - state->connector_states = kcalloc(state->num_connector, - sizeof(*state->connector_states), - GFP_KERNEL); - if (!state->connector_states) - goto fail; - - state->dev = dev; - - DRM_DEBUG_ATOMIC("Allocated atomic state %p\n", state); - - return 0; -fail: - drm_atomic_state_default_release(state); - return -ENOMEM; -} -EXPORT_SYMBOL(drm_atomic_state_init); - -/** - * drm_atomic_state_alloc - allocate atomic state - * @dev: DRM device - * - * This allocates an empty atomic state to track updates. - */ -struct drm_atomic_state * -drm_atomic_state_alloc(struct drm_device *dev) -{ - struct drm_mode_config *config = &dev->mode_config; - struct drm_atomic_state *state; - - if (!config->funcs->atomic_state_alloc) { - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) - return NULL; - if (drm_atomic_state_init(dev, state) < 0) { - kfree(state); - return NULL; - } - return state; - } - - return config->funcs->atomic_state_alloc(dev); -} -EXPORT_SYMBOL(drm_atomic_state_alloc); - -/** - * drm_atomic_state_default_clear - clear base atomic state - * @state: atomic state - * - * Default implementation for clearing atomic state. - * This is useful for drivers that subclass the atomic state. - */ -void drm_atomic_state_default_clear(struct drm_atomic_state *state) -{ - struct drm_device *dev = state->dev; - struct drm_mode_config *config = &dev->mode_config; - int i; - - DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state); - - for (i = 0; i < state->num_connector; i++) { - struct drm_connector *connector = state->connectors[i]; - - if (!connector || !connector->funcs) - continue; - - /* - * FIXME: Async commits can race with connector unplugging and - * there's currently nothing that prevents cleanup up state for - * deleted connectors. As long as the callback doesn't look at - * the connector we'll be fine though, so make sure that's the - * case by setting all connector pointers to NULL. - */ - state->connector_states[i]->connector = NULL; - connector->funcs->atomic_destroy_state(NULL, - state->connector_states[i]); - state->connectors[i] = NULL; - state->connector_states[i] = NULL; - } - - for (i = 0; i < config->num_crtc; i++) { - struct drm_crtc *crtc = state->crtcs[i]; - - if (!crtc) - continue; - - crtc->funcs->atomic_destroy_state(crtc, - state->crtc_states[i]); - state->crtcs[i] = NULL; - state->crtc_states[i] = NULL; - } - - for (i = 0; i < config->num_total_plane; i++) { - struct drm_plane *plane = state->planes[i]; - - if (!plane) - continue; - - plane->funcs->atomic_destroy_state(plane, - state->plane_states[i]); - state->planes[i] = NULL; - state->plane_states[i] = NULL; - } -} -EXPORT_SYMBOL(drm_atomic_state_default_clear); - -/** - * drm_atomic_state_clear - clear state object - * @state: atomic state - * - * When the w/w mutex algorithm detects a deadlock we need to back off and drop - * all locks. So someone else could sneak in and change the current modeset - * configuration. Which means that all the state assembled in @state is no - * longer an atomic update to the current state, but to some arbitrary earlier - * state. Which could break assumptions the driver's ->atomic_check likely - * relies on. - * - * Hence we must clear all cached state and completely start over, using this - * function. - */ -void drm_atomic_state_clear(struct drm_atomic_state *state) -{ - struct drm_device *dev = state->dev; - struct drm_mode_config *config = &dev->mode_config; - - if (config->funcs->atomic_state_clear) - config->funcs->atomic_state_clear(state); - else - drm_atomic_state_default_clear(state); -} -EXPORT_SYMBOL(drm_atomic_state_clear); - -/** - * drm_atomic_state_free - free all memory for an atomic state - * @state: atomic state to deallocate - * - * This frees all memory associated with an atomic state, including all the - * per-object state for planes, crtcs and connectors. - */ -void drm_atomic_state_free(struct drm_atomic_state *state) -{ - struct drm_device *dev; - struct drm_mode_config *config; - - if (!state) - return; - - dev = state->dev; - config = &dev->mode_config; - - drm_atomic_state_clear(state); - - DRM_DEBUG_ATOMIC("Freeing atomic state %p\n", state); - - if (config->funcs->atomic_state_free) { - config->funcs->atomic_state_free(state); - } else { - drm_atomic_state_default_release(state); - kfree(state); - } -} -EXPORT_SYMBOL(drm_atomic_state_free); - -/** - * drm_atomic_get_crtc_state - get crtc state - * @state: global atomic state object - * @crtc: crtc to get state object for - * - * This function returns the crtc state for the given crtc, allocating it if - * needed. It will also grab the relevant crtc lock to make sure that the state - * is consistent. - * - * Returns: - * - * Either the allocated state or the error code encoded into the pointer. When - * the error is EDEADLK then the w/w mutex code has detected a deadlock and the - * entire atomic sequence must be restarted. All other errors are fatal. - */ -struct drm_crtc_state * -drm_atomic_get_crtc_state(struct drm_atomic_state *state, - struct drm_crtc *crtc) -{ - int ret, index = drm_crtc_index(crtc); - struct drm_crtc_state *crtc_state; - - crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); - if (crtc_state) - return crtc_state; - - ret = drm_modeset_lock(&crtc->mutex, state->acquire_ctx); - if (ret) - return ERR_PTR(ret); - - crtc_state = crtc->funcs->atomic_duplicate_state(crtc); - if (!crtc_state) - return ERR_PTR(-ENOMEM); - - state->crtc_states[index] = crtc_state; - state->crtcs[index] = crtc; - crtc_state->state = state; - - DRM_DEBUG_ATOMIC("Added [CRTC:%d] %p state to %p\n", - crtc->base.id, crtc_state, state); - - return crtc_state; -} -EXPORT_SYMBOL(drm_atomic_get_crtc_state); - -/** - * drm_atomic_set_mode_for_crtc - set mode for CRTC - * @state: the CRTC whose incoming state to update - * @mode: kernel-internal mode to use for the CRTC, or NULL to disable - * - * Set a mode (originating from the kernel) on the desired CRTC state. Does - * not change any other state properties, including enable, active, or - * mode_changed. - * - * RETURNS: - * Zero on success, error code on failure. Cannot return -EDEADLK. - */ -int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, - struct drm_display_mode *mode) -{ - struct drm_mode_modeinfo umode; - - /* Early return for no change. */ - if (mode && memcmp(&state->mode, mode, sizeof(*mode)) == 0) - return 0; - - if (state->mode_blob) - drm_property_unreference_blob(state->mode_blob); - state->mode_blob = NULL; - - if (mode) { - drm_mode_convert_to_umode(&umode, mode); - state->mode_blob = - drm_property_create_blob(state->crtc->dev, - sizeof(umode), - &umode); - if (IS_ERR(state->mode_blob)) - return PTR_ERR(state->mode_blob); - - drm_mode_copy(&state->mode, mode); - state->enable = true; - DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n", - mode->name, state); - } else { - memset(&state->mode, 0, sizeof(state->mode)); - state->enable = false; - DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n", - state); - } - - return 0; -} -EXPORT_SYMBOL(drm_atomic_set_mode_for_crtc); - -/** - * drm_atomic_set_mode_prop_for_crtc - set mode for CRTC - * @state: the CRTC whose incoming state to update - * @blob: pointer to blob property to use for mode - * - * Set a mode (originating from a blob property) on the desired CRTC state. - * This function will take a reference on the blob property for the CRTC state, - * and release the reference held on the state's existing mode property, if any - * was set. - * - * RETURNS: - * Zero on success, error code on failure. Cannot return -EDEADLK. - */ -int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state, - struct drm_property_blob *blob) -{ - if (blob == state->mode_blob) - return 0; - - if (state->mode_blob) - drm_property_unreference_blob(state->mode_blob); - state->mode_blob = NULL; - - memset(&state->mode, 0, sizeof(state->mode)); - - if (blob) { - if (blob->length != sizeof(struct drm_mode_modeinfo) || - drm_mode_convert_umode(&state->mode, - (const struct drm_mode_modeinfo *) - blob->data)) - return -EINVAL; - - state->mode_blob = drm_property_reference_blob(blob); - state->enable = true; - DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n", - state->mode.name, state); - } else { - state->enable = false; - DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n", - state); - } - - return 0; -} -EXPORT_SYMBOL(drm_atomic_set_mode_prop_for_crtc); - -/** - * drm_atomic_crtc_set_property - set property on CRTC - * @crtc: the drm CRTC to set a property on - * @state: the state object to update with the new property value - * @property: the property to set - * @val: the new property value - * - * Use this instead of calling crtc->atomic_set_property directly. - * This function handles generic/core properties and calls out to - * driver's ->atomic_set_property() for driver properties. To ensure - * consistent behavior you must call this function rather than the - * driver hook directly. - * - * RETURNS: - * Zero on success, error code on failure - */ -int drm_atomic_crtc_set_property(struct drm_crtc *crtc, - struct drm_crtc_state *state, struct drm_property *property, - uint64_t val) -{ - struct drm_device *dev = crtc->dev; - struct drm_mode_config *config = &dev->mode_config; - int ret; - - if (property == config->prop_active) - state->active = val; - else if (property == config->prop_mode_id) { - struct drm_property_blob *mode = - drm_property_lookup_blob(dev, val); - ret = drm_atomic_set_mode_prop_for_crtc(state, mode); - if (mode) - drm_property_unreference_blob(mode); - return ret; - } - else if (crtc->funcs->atomic_set_property) - return crtc->funcs->atomic_set_property(crtc, state, property, val); - else - return -EINVAL; - - return 0; -} -EXPORT_SYMBOL(drm_atomic_crtc_set_property); - -/* - * This function handles generic/core properties and calls out to - * driver's ->atomic_get_property() for driver properties. To ensure - * consistent behavior you must call this function rather than the - * driver hook directly. - */ -static int -drm_atomic_crtc_get_property(struct drm_crtc *crtc, - const struct drm_crtc_state *state, - struct drm_property *property, uint64_t *val) -{ - struct drm_device *dev = crtc->dev; - struct drm_mode_config *config = &dev->mode_config; - - if (property == config->prop_active) - *val = state->active; - else if (property == config->prop_mode_id) - *val = (state->mode_blob) ? state->mode_blob->base.id : 0; - else if (crtc->funcs->atomic_get_property) - return crtc->funcs->atomic_get_property(crtc, state, property, val); - else - return -EINVAL; - - return 0; -} - -/** - * drm_atomic_crtc_check - check crtc state - * @crtc: crtc to check - * @state: crtc state to check - * - * Provides core sanity checks for crtc state. - * - * RETURNS: - * Zero on success, error code on failure - */ -static int drm_atomic_crtc_check(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - /* NOTE: we explicitly don't enforce constraints such as primary - * layer covering entire screen, since that is something we want - * to allow (on hw that supports it). For hw that does not, it - * should be checked in driver's crtc->atomic_check() vfunc. - * - * TODO: Add generic modeset state checks once we support those. - */ - - if (state->active && !state->enable) { - DRM_DEBUG_ATOMIC("[CRTC:%d] active without enabled\n", - crtc->base.id); - return -EINVAL; - } - - /* The state->enable vs. state->mode_blob checks can be WARN_ON, - * as this is a kernel-internal detail that userspace should never - * be able to trigger. */ - if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) && - WARN_ON(state->enable && !state->mode_blob)) { - DRM_DEBUG_ATOMIC("[CRTC:%d] enabled without mode blob\n", - crtc->base.id); - return -EINVAL; - } - - if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) && - WARN_ON(!state->enable && state->mode_blob)) { - DRM_DEBUG_ATOMIC("[CRTC:%d] disabled with mode blob\n", - crtc->base.id); - return -EINVAL; - } - - return 0; -} - -/** - * drm_atomic_get_plane_state - get plane state - * @state: global atomic state object - * @plane: plane to get state object for - * - * This function returns the plane state for the given plane, allocating it if - * needed. It will also grab the relevant plane lock to make sure that the state - * is consistent. - * - * Returns: - * - * Either the allocated state or the error code encoded into the pointer. When - * the error is EDEADLK then the w/w mutex code has detected a deadlock and the - * entire atomic sequence must be restarted. All other errors are fatal. - */ -struct drm_plane_state * -drm_atomic_get_plane_state(struct drm_atomic_state *state, - struct drm_plane *plane) -{ - int ret, index = drm_plane_index(plane); - struct drm_plane_state *plane_state; - - plane_state = drm_atomic_get_existing_plane_state(state, plane); - if (plane_state) - return plane_state; - - ret = drm_modeset_lock(&plane->mutex, state->acquire_ctx); - if (ret) - return ERR_PTR(ret); - - plane_state = plane->funcs->atomic_duplicate_state(plane); - if (!plane_state) - return ERR_PTR(-ENOMEM); - - state->plane_states[index] = plane_state; - state->planes[index] = plane; - plane_state->state = state; - - DRM_DEBUG_ATOMIC("Added [PLANE:%d] %p state to %p\n", - plane->base.id, plane_state, state); - - if (plane_state->crtc) { - struct drm_crtc_state *crtc_state; - - crtc_state = drm_atomic_get_crtc_state(state, - plane_state->crtc); - if (IS_ERR(crtc_state)) - return ERR_CAST(crtc_state); - } - - return plane_state; -} -EXPORT_SYMBOL(drm_atomic_get_plane_state); - -/** - * drm_atomic_plane_set_property - set property on plane - * @plane: the drm plane to set a property on - * @state: the state object to update with the new property value - * @property: the property to set - * @val: the new property value - * - * Use this instead of calling plane->atomic_set_property directly. - * This function handles generic/core properties and calls out to - * driver's ->atomic_set_property() for driver properties. To ensure - * consistent behavior you must call this function rather than the - * driver hook directly. - * - * RETURNS: - * Zero on success, error code on failure - */ -int drm_atomic_plane_set_property(struct drm_plane *plane, - struct drm_plane_state *state, struct drm_property *property, - uint64_t val) -{ - struct drm_device *dev = plane->dev; - struct drm_mode_config *config = &dev->mode_config; - - if (property == config->prop_fb_id) { - struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, val); - drm_atomic_set_fb_for_plane(state, fb); - if (fb) - drm_framebuffer_unreference(fb); - } else if (property == config->prop_crtc_id) { - struct drm_crtc *crtc = drm_crtc_find(dev, val); - return drm_atomic_set_crtc_for_plane(state, crtc); - } else if (property == config->prop_crtc_x) { - state->crtc_x = U642I64(val); - } else if (property == config->prop_crtc_y) { - state->crtc_y = U642I64(val); - } else if (property == config->prop_crtc_w) { - state->crtc_w = val; - } else if (property == config->prop_crtc_h) { - state->crtc_h = val; - } else if (property == config->prop_src_x) { - state->src_x = val; - } else if (property == config->prop_src_y) { - state->src_y = val; - } else if (property == config->prop_src_w) { - state->src_w = val; - } else if (property == config->prop_src_h) { - state->src_h = val; - } else if (property == config->rotation_property) { - state->rotation = val; - } else if (plane->funcs->atomic_set_property) { - return plane->funcs->atomic_set_property(plane, state, - property, val); - } else { - return -EINVAL; - } - - return 0; -} -EXPORT_SYMBOL(drm_atomic_plane_set_property); - -/* - * This function handles generic/core properties and calls out to - * driver's ->atomic_get_property() for driver properties. To ensure - * consistent behavior you must call this function rather than the - * driver hook directly. - */ -static int -drm_atomic_plane_get_property(struct drm_plane *plane, - const struct drm_plane_state *state, - struct drm_property *property, uint64_t *val) -{ - struct drm_device *dev = plane->dev; - struct drm_mode_config *config = &dev->mode_config; - - if (property == config->prop_fb_id) { - *val = (state->fb) ? state->fb->base.id : 0; - } else if (property == config->prop_crtc_id) { - *val = (state->crtc) ? state->crtc->base.id : 0; - } else if (property == config->prop_crtc_x) { - *val = I642U64(state->crtc_x); - } else if (property == config->prop_crtc_y) { - *val = I642U64(state->crtc_y); - } else if (property == config->prop_crtc_w) { - *val = state->crtc_w; - } else if (property == config->prop_crtc_h) { - *val = state->crtc_h; - } else if (property == config->prop_src_x) { - *val = state->src_x; - } else if (property == config->prop_src_y) { - *val = state->src_y; - } else if (property == config->prop_src_w) { - *val = state->src_w; - } else if (property == config->prop_src_h) { - *val = state->src_h; - } else if (property == config->rotation_property) { - *val = state->rotation; - } else if (plane->funcs->atomic_get_property) { - return plane->funcs->atomic_get_property(plane, state, property, val); - } else { - return -EINVAL; - } - - return 0; -} - -static bool -plane_switching_crtc(struct drm_atomic_state *state, - struct drm_plane *plane, - struct drm_plane_state *plane_state) -{ - if (!plane->state->crtc || !plane_state->crtc) - return false; - - if (plane->state->crtc == plane_state->crtc) - return false; - - /* This could be refined, but currently there's no helper or driver code - * to implement direct switching of active planes nor userspace to take - * advantage of more direct plane switching without the intermediate - * full OFF state. - */ - return true; -} - -/** - * drm_atomic_plane_check - check plane state - * @plane: plane to check - * @state: plane state to check - * - * Provides core sanity checks for plane state. - * - * RETURNS: - * Zero on success, error code on failure - */ -static int drm_atomic_plane_check(struct drm_plane *plane, - struct drm_plane_state *state) -{ - unsigned int fb_width, fb_height; - int ret; - - /* either *both* CRTC and FB must be set, or neither */ - if (WARN_ON(state->crtc && !state->fb)) { - DRM_DEBUG_ATOMIC("CRTC set but no FB\n"); - return -EINVAL; - } else if (WARN_ON(state->fb && !state->crtc)) { - DRM_DEBUG_ATOMIC("FB set but no CRTC\n"); - return -EINVAL; - } - - /* if disabled, we don't care about the rest of the state: */ - if (!state->crtc) - return 0; - - /* Check whether this plane is usable on this CRTC */ - if (!(plane->possible_crtcs & drm_crtc_mask(state->crtc))) { - DRM_DEBUG_ATOMIC("Invalid crtc for plane\n"); - return -EINVAL; - } - - /* Check whether this plane supports the fb pixel format. */ - ret = drm_plane_check_pixel_format(plane, state->fb->pixel_format); - if (ret) { - DRM_DEBUG_ATOMIC("Invalid pixel format %s\n", - drm_get_format_name(state->fb->pixel_format)); - return ret; - } - - /* Give drivers some help against integer overflows */ - if (state->crtc_w > INT_MAX || - state->crtc_x > INT_MAX - (int32_t) state->crtc_w || - state->crtc_h > INT_MAX || - state->crtc_y > INT_MAX - (int32_t) state->crtc_h) { - DRM_DEBUG_ATOMIC("Invalid CRTC coordinates %ux%u+%d+%d\n", - state->crtc_w, state->crtc_h, - state->crtc_x, state->crtc_y); - return -ERANGE; - } - - fb_width = state->fb->width << 16; - fb_height = state->fb->height << 16; - - /* Make sure source coordinates are inside the fb. */ - if (state->src_w > fb_width || - state->src_x > fb_width - state->src_w || - state->src_h > fb_height || - state->src_y > fb_height - state->src_h) { - DRM_DEBUG_ATOMIC("Invalid source coordinates " - "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", - state->src_w >> 16, ((state->src_w & 0xffff) * 15625) >> 10, - state->src_h >> 16, ((state->src_h & 0xffff) * 15625) >> 10, - state->src_x >> 16, ((state->src_x & 0xffff) * 15625) >> 10, - state->src_y >> 16, ((state->src_y & 0xffff) * 15625) >> 10); - return -ENOSPC; - } - - if (plane_switching_crtc(state->state, plane, state)) { - DRM_DEBUG_ATOMIC("[PLANE:%d] switching CRTC directly\n", - plane->base.id); - return -EINVAL; - } - - return 0; -} - -/** - * drm_atomic_get_connector_state - get connector state - * @state: global atomic state object - * @connector: connector to get state object for - * - * This function returns the connector state for the given connector, - * allocating it if needed. It will also grab the relevant connector lock to - * make sure that the state is consistent. - * - * Returns: - * - * Either the allocated state or the error code encoded into the pointer. When - * the error is EDEADLK then the w/w mutex code has detected a deadlock and the - * entire atomic sequence must be restarted. All other errors are fatal. - */ -struct drm_connector_state * -drm_atomic_get_connector_state(struct drm_atomic_state *state, - struct drm_connector *connector) -{ - int ret, index; - struct drm_mode_config *config = &connector->dev->mode_config; - struct drm_connector_state *connector_state; - - ret = drm_modeset_lock(&config->connection_mutex, state->acquire_ctx); - if (ret) - return ERR_PTR(ret); - - index = drm_connector_index(connector); - - /* - * Construction of atomic state updates can race with a connector - * hot-add which might overflow. In this case flip the table and just - * restart the entire ioctl - no one is fast enough to livelock a cpu - * with physical hotplug events anyway. - * - * Note that we only grab the indexes once we have the right lock to - * prevent hotplug/unplugging of connectors. So removal is no problem, - * at most the array is a bit too large. - */ - if (index >= state->num_connector) { - DRM_DEBUG_ATOMIC("Hot-added connector would overflow state array, restarting\n"); - return ERR_PTR(-EAGAIN); - } - - if (state->connector_states[index]) - return state->connector_states[index]; - - connector_state = connector->funcs->atomic_duplicate_state(connector); - if (!connector_state) - return ERR_PTR(-ENOMEM); - - state->connector_states[index] = connector_state; - state->connectors[index] = connector; - connector_state->state = state; - - DRM_DEBUG_ATOMIC("Added [CONNECTOR:%d] %p state to %p\n", - connector->base.id, connector_state, state); - - if (connector_state->crtc) { - struct drm_crtc_state *crtc_state; - - crtc_state = drm_atomic_get_crtc_state(state, - connector_state->crtc); - if (IS_ERR(crtc_state)) - return ERR_CAST(crtc_state); - } - - return connector_state; -} -EXPORT_SYMBOL(drm_atomic_get_connector_state); - -/** - * drm_atomic_connector_set_property - set property on connector. - * @connector: the drm connector to set a property on - * @state: the state object to update with the new property value - * @property: the property to set - * @val: the new property value - * - * Use this instead of calling connector->atomic_set_property directly. - * This function handles generic/core properties and calls out to - * driver's ->atomic_set_property() for driver properties. To ensure - * consistent behavior you must call this function rather than the - * driver hook directly. - * - * RETURNS: - * Zero on success, error code on failure - */ -int drm_atomic_connector_set_property(struct drm_connector *connector, - struct drm_connector_state *state, struct drm_property *property, - uint64_t val) -{ - struct drm_device *dev = connector->dev; - struct drm_mode_config *config = &dev->mode_config; - - if (property == config->prop_crtc_id) { - struct drm_crtc *crtc = drm_crtc_find(dev, val); - return drm_atomic_set_crtc_for_connector(state, crtc); - } else if (property == config->dpms_property) { - /* setting DPMS property requires special handling, which - * is done in legacy setprop path for us. Disallow (for - * now?) atomic writes to DPMS property: - */ - return -EINVAL; - } else if (connector->funcs->atomic_set_property) { - return connector->funcs->atomic_set_property(connector, - state, property, val); - } else { - return -EINVAL; - } -} -EXPORT_SYMBOL(drm_atomic_connector_set_property); - -/* - * This function handles generic/core properties and calls out to - * driver's ->atomic_get_property() for driver properties. To ensure - * consistent behavior you must call this function rather than the - * driver hook directly. - */ -static int -drm_atomic_connector_get_property(struct drm_connector *connector, - const struct drm_connector_state *state, - struct drm_property *property, uint64_t *val) -{ - struct drm_device *dev = connector->dev; - struct drm_mode_config *config = &dev->mode_config; - - if (property == config->prop_crtc_id) { - *val = (state->crtc) ? state->crtc->base.id : 0; - } else if (property == config->dpms_property) { - *val = connector->dpms; - } else if (connector->funcs->atomic_get_property) { - return connector->funcs->atomic_get_property(connector, - state, property, val); - } else { - return -EINVAL; - } - - return 0; -} - -int drm_atomic_get_property(struct drm_mode_object *obj, - struct drm_property *property, uint64_t *val) -{ - struct drm_device *dev = property->dev; - int ret; - - switch (obj->type) { - case DRM_MODE_OBJECT_CONNECTOR: { - struct drm_connector *connector = obj_to_connector(obj); - WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); - ret = drm_atomic_connector_get_property(connector, - connector->state, property, val); - break; - } - case DRM_MODE_OBJECT_CRTC: { - struct drm_crtc *crtc = obj_to_crtc(obj); - WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); - ret = drm_atomic_crtc_get_property(crtc, - crtc->state, property, val); - break; - } - case DRM_MODE_OBJECT_PLANE: { - struct drm_plane *plane = obj_to_plane(obj); - WARN_ON(!drm_modeset_is_locked(&plane->mutex)); - ret = drm_atomic_plane_get_property(plane, - plane->state, property, val); - break; - } - default: - ret = -EINVAL; - break; - } - - return ret; -} - -/** - * drm_atomic_set_crtc_for_plane - set crtc for plane - * @plane_state: the plane whose incoming state to update - * @crtc: crtc to use for the plane - * - * Changing the assigned crtc for a plane requires us to grab the lock and state - * for the new crtc, as needed. This function takes care of all these details - * besides updating the pointer in the state object itself. - * - * Returns: - * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK - * then the w/w mutex code has detected a deadlock and the entire atomic - * sequence must be restarted. All other errors are fatal. - */ -int -drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, - struct drm_crtc *crtc) -{ - struct drm_plane *plane = plane_state->plane; - struct drm_crtc_state *crtc_state; - /* Nothing to do for same crtc*/ - if (plane_state->crtc == crtc) - return 0; - if (plane_state->crtc) { - crtc_state = drm_atomic_get_crtc_state(plane_state->state, - plane_state->crtc); - if (WARN_ON(IS_ERR(crtc_state))) - return PTR_ERR(crtc_state); - - crtc_state->plane_mask &= ~(1 << drm_plane_index(plane)); - } - - plane_state->crtc = crtc; - - if (crtc) { - crtc_state = drm_atomic_get_crtc_state(plane_state->state, - crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); - crtc_state->plane_mask |= (1 << drm_plane_index(plane)); - } - - if (crtc) - DRM_DEBUG_ATOMIC("Link plane state %p to [CRTC:%d]\n", - plane_state, crtc->base.id); - else - DRM_DEBUG_ATOMIC("Link plane state %p to [NOCRTC]\n", - plane_state); - - return 0; -} -EXPORT_SYMBOL(drm_atomic_set_crtc_for_plane); - -/** - * drm_atomic_set_fb_for_plane - set framebuffer for plane - * @plane_state: atomic state object for the plane - * @fb: fb to use for the plane - * - * Changing the assigned framebuffer for a plane requires us to grab a reference - * to the new fb and drop the reference to the old fb, if there is one. This - * function takes care of all these details besides updating the pointer in the - * state object itself. - */ -void -drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state, - struct drm_framebuffer *fb) -{ - if (plane_state->fb) - drm_framebuffer_unreference(plane_state->fb); - if (fb) - drm_framebuffer_reference(fb); - plane_state->fb = fb; - - if (fb) - DRM_DEBUG_ATOMIC("Set [FB:%d] for plane state %p\n", - fb->base.id, plane_state); - else - DRM_DEBUG_ATOMIC("Set [NOFB] for plane state %p\n", - plane_state); -} -EXPORT_SYMBOL(drm_atomic_set_fb_for_plane); - -/** - * drm_atomic_set_crtc_for_connector - set crtc for connector - * @conn_state: atomic state object for the connector - * @crtc: crtc to use for the connector - * - * Changing the assigned crtc for a connector requires us to grab the lock and - * state for the new crtc, as needed. This function takes care of all these - * details besides updating the pointer in the state object itself. - * - * Returns: - * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK - * then the w/w mutex code has detected a deadlock and the entire atomic - * sequence must be restarted. All other errors are fatal. - */ -int -drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, - struct drm_crtc *crtc) -{ - struct drm_crtc_state *crtc_state; - - if (crtc) { - crtc_state = drm_atomic_get_crtc_state(conn_state->state, crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); - } - - conn_state->crtc = crtc; - - if (crtc) - DRM_DEBUG_ATOMIC("Link connector state %p to [CRTC:%d]\n", - conn_state, crtc->base.id); - else - DRM_DEBUG_ATOMIC("Link connector state %p to [NOCRTC]\n", - conn_state); - - return 0; -} -EXPORT_SYMBOL(drm_atomic_set_crtc_for_connector); - -/** - * drm_atomic_add_affected_connectors - add connectors for crtc - * @state: atomic state - * @crtc: DRM crtc - * - * This function walks the current configuration and adds all connectors - * currently using @crtc to the atomic configuration @state. Note that this - * function must acquire the connection mutex. This can potentially cause - * unneeded seralization if the update is just for the planes on one crtc. Hence - * drivers and helpers should only call this when really needed (e.g. when a - * full modeset needs to happen due to some change). - * - * Returns: - * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK - * then the w/w mutex code has detected a deadlock and the entire atomic - * sequence must be restarted. All other errors are fatal. - */ -int -drm_atomic_add_affected_connectors(struct drm_atomic_state *state, - struct drm_crtc *crtc) -{ - struct drm_mode_config *config = &state->dev->mode_config; - struct drm_connector *connector; - struct drm_connector_state *conn_state; - int ret; - - ret = drm_modeset_lock(&config->connection_mutex, state->acquire_ctx); - if (ret) - return ret; - - DRM_DEBUG_ATOMIC("Adding all current connectors for [CRTC:%d] to %p\n", - crtc->base.id, state); - - /* - * Changed connectors are already in @state, so only need to look at the - * current configuration. - */ - drm_for_each_connector(connector, state->dev) { - if (connector->state->crtc != crtc) - continue; - - conn_state = drm_atomic_get_connector_state(state, connector); - if (IS_ERR(conn_state)) - return PTR_ERR(conn_state); - } - - return 0; -} -EXPORT_SYMBOL(drm_atomic_add_affected_connectors); - -/** - * drm_atomic_add_affected_planes - add planes for crtc - * @state: atomic state - * @crtc: DRM crtc - * - * This function walks the current configuration and adds all planes - * currently used by @crtc to the atomic configuration @state. This is useful - * when an atomic commit also needs to check all currently enabled plane on - * @crtc, e.g. when changing the mode. It's also useful when re-enabling a CRTC - * to avoid special code to force-enable all planes. - * - * Since acquiring a plane state will always also acquire the w/w mutex of the - * current CRTC for that plane (if there is any) adding all the plane states for - * a CRTC will not reduce parallism of atomic updates. - * - * Returns: - * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK - * then the w/w mutex code has detected a deadlock and the entire atomic - * sequence must be restarted. All other errors are fatal. - */ -int -drm_atomic_add_affected_planes(struct drm_atomic_state *state, - struct drm_crtc *crtc) -{ - struct drm_plane *plane; - - WARN_ON(!drm_atomic_get_existing_crtc_state(state, crtc)); - - drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) { - struct drm_plane_state *plane_state = - drm_atomic_get_plane_state(state, plane); - - if (IS_ERR(plane_state)) - return PTR_ERR(plane_state); - } - return 0; -} -EXPORT_SYMBOL(drm_atomic_add_affected_planes); - -/** - * drm_atomic_connectors_for_crtc - count number of connected outputs - * @state: atomic state - * @crtc: DRM crtc - * - * This function counts all connectors which will be connected to @crtc - * according to @state. Useful to recompute the enable state for @crtc. - */ -int -drm_atomic_connectors_for_crtc(struct drm_atomic_state *state, - struct drm_crtc *crtc) -{ - struct drm_connector *connector; - struct drm_connector_state *conn_state; - - int i, num_connected_connectors = 0; - - for_each_connector_in_state(state, connector, conn_state, i) { - if (conn_state->crtc == crtc) - num_connected_connectors++; - } - - DRM_DEBUG_ATOMIC("State %p has %i connectors for [CRTC:%d]\n", - state, num_connected_connectors, crtc->base.id); - - return num_connected_connectors; -} -EXPORT_SYMBOL(drm_atomic_connectors_for_crtc); - -/** - * drm_atomic_legacy_backoff - locking backoff for legacy ioctls - * @state: atomic state - * - * This function should be used by legacy entry points which don't understand - * -EDEADLK semantics. For simplicity this one will grab all modeset locks after - * the slowpath completed. - */ -void drm_atomic_legacy_backoff(struct drm_atomic_state *state) -{ - int ret; - -retry: - drm_modeset_backoff(state->acquire_ctx); - - ret = drm_modeset_lock(&state->dev->mode_config.connection_mutex, - state->acquire_ctx); - if (ret) - goto retry; - ret = drm_modeset_lock_all_crtcs(state->dev, - state->acquire_ctx); - if (ret) - goto retry; -} -EXPORT_SYMBOL(drm_atomic_legacy_backoff); - -/** - * drm_atomic_check_only - check whether a given config would work - * @state: atomic configuration to check - * - * Note that this function can return -EDEADLK if the driver needed to acquire - * more locks but encountered a deadlock. The caller must then do the usual w/w - * backoff dance and restart. All other errors are fatal. - * - * Returns: - * 0 on success, negative error code on failure. - */ -int drm_atomic_check_only(struct drm_atomic_state *state) -{ - struct drm_device *dev = state->dev; - struct drm_mode_config *config = &dev->mode_config; - struct drm_plane *plane; - struct drm_plane_state *plane_state; - struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; - int i, ret = 0; - - DRM_DEBUG_ATOMIC("checking %p\n", state); - - for_each_plane_in_state(state, plane, plane_state, i) { - ret = drm_atomic_plane_check(plane, plane_state); - if (ret) { - DRM_DEBUG_ATOMIC("[PLANE:%d] atomic core check failed\n", - plane->base.id); - return ret; - } - } - - for_each_crtc_in_state(state, crtc, crtc_state, i) { - ret = drm_atomic_crtc_check(crtc, crtc_state); - if (ret) { - DRM_DEBUG_ATOMIC("[CRTC:%d] atomic core check failed\n", - crtc->base.id); - return ret; - } - } - - if (config->funcs->atomic_check) - ret = config->funcs->atomic_check(state->dev, state); - - if (ret) - return ret; - - if (!state->allow_modeset) { - for_each_crtc_in_state(state, crtc, crtc_state, i) { - if (drm_atomic_crtc_needs_modeset(crtc_state)) { - DRM_DEBUG_ATOMIC("[CRTC:%d] requires full modeset\n", - crtc->base.id); - return -EINVAL; - } - } - } - - return 0; -} -EXPORT_SYMBOL(drm_atomic_check_only); - -/** - * drm_atomic_commit - commit configuration atomically - * @state: atomic configuration to check - * - * Note that this function can return -EDEADLK if the driver needed to acquire - * more locks but encountered a deadlock. The caller must then do the usual w/w - * backoff dance and restart. All other errors are fatal. - * - * Also note that on successful execution ownership of @state is transferred - * from the caller of this function to the function itself. The caller must not - * free or in any other way access @state. If the function fails then the caller - * must clean up @state itself. - * - * Returns: - * 0 on success, negative error code on failure. - */ -int drm_atomic_commit(struct drm_atomic_state *state) -{ - struct drm_mode_config *config = &state->dev->mode_config; - int ret; - - ret = drm_atomic_check_only(state); - if (ret) - return ret; - - DRM_DEBUG_ATOMIC("commiting %p\n", state); - - return config->funcs->atomic_commit(state->dev, state, false); -} -EXPORT_SYMBOL(drm_atomic_commit); - -/** - * drm_atomic_async_commit - atomic&async configuration commit - * @state: atomic configuration to check - * - * Note that this function can return -EDEADLK if the driver needed to acquire - * more locks but encountered a deadlock. The caller must then do the usual w/w - * backoff dance and restart. All other errors are fatal. - * - * Also note that on successful execution ownership of @state is transferred - * from the caller of this function to the function itself. The caller must not - * free or in any other way access @state. If the function fails then the caller - * must clean up @state itself. - * - * Returns: - * 0 on success, negative error code on failure. - */ -int drm_atomic_async_commit(struct drm_atomic_state *state) -{ - struct drm_mode_config *config = &state->dev->mode_config; - int ret; - - ret = drm_atomic_check_only(state); - if (ret) - return ret; - - DRM_DEBUG_ATOMIC("commiting %p asynchronously\n", state); - - return config->funcs->atomic_commit(state->dev, state, true); -} -EXPORT_SYMBOL(drm_atomic_async_commit); - -/* - * The big monstor ioctl - */ - -static struct drm_pending_vblank_event *create_vblank_event( - struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data) -{ - struct drm_pending_vblank_event *e = NULL; - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - if (file_priv->event_space < sizeof e->event) { - spin_unlock_irqrestore(&dev->event_lock, flags); - goto out; - } - file_priv->event_space -= sizeof e->event; - spin_unlock_irqrestore(&dev->event_lock, flags); - - e = kzalloc(sizeof *e, GFP_KERNEL); - if (e == NULL) { - spin_lock_irqsave(&dev->event_lock, flags); - file_priv->event_space += sizeof e->event; - spin_unlock_irqrestore(&dev->event_lock, flags); - goto out; - } - - e->event.base.type = DRM_EVENT_FLIP_COMPLETE; - e->event.base.length = sizeof e->event; - e->event.user_data = user_data; - e->base.event = &e->event.base; - e->base.file_priv = file_priv; - e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; - -out: - return e; -} - -static void destroy_vblank_event(struct drm_device *dev, - struct drm_file *file_priv, struct drm_pending_vblank_event *e) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - file_priv->event_space += sizeof e->event; - spin_unlock_irqrestore(&dev->event_lock, flags); - kfree(e); -} - -static int atomic_set_prop(struct drm_atomic_state *state, - struct drm_mode_object *obj, struct drm_property *prop, - uint64_t prop_value) -{ - struct drm_mode_object *ref; - int ret; - - if (!drm_property_change_valid_get(prop, prop_value, &ref)) - return -EINVAL; - - switch (obj->type) { - case DRM_MODE_OBJECT_CONNECTOR: { - struct drm_connector *connector = obj_to_connector(obj); - struct drm_connector_state *connector_state; - - connector_state = drm_atomic_get_connector_state(state, connector); - if (IS_ERR(connector_state)) { - ret = PTR_ERR(connector_state); - break; - } - - ret = drm_atomic_connector_set_property(connector, - connector_state, prop, prop_value); - break; - } - case DRM_MODE_OBJECT_CRTC: { - struct drm_crtc *crtc = obj_to_crtc(obj); - struct drm_crtc_state *crtc_state; - - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); - break; - } - - ret = drm_atomic_crtc_set_property(crtc, - crtc_state, prop, prop_value); - break; - } - case DRM_MODE_OBJECT_PLANE: { - struct drm_plane *plane = obj_to_plane(obj); - struct drm_plane_state *plane_state; - - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - break; - } - - ret = drm_atomic_plane_set_property(plane, - plane_state, prop, prop_value); - break; - } - default: - ret = -EINVAL; - break; - } - - drm_property_change_valid_put(prop, ref); - return ret; -} - -/** - * drm_atomic_update_old_fb -- Unset old_fb pointers and set plane->fb pointers. - * - * @dev: drm device to check. - * @plane_mask: plane mask for planes that were updated. - * @ret: return value, can be -EDEADLK for a retry. - * - * Before doing an update plane->old_fb is set to plane->fb, - * but before dropping the locks old_fb needs to be set to NULL - * and plane->fb updated. This is a common operation for each - * atomic update, so this call is split off as a helper. - */ -void drm_atomic_clean_old_fb(struct drm_device *dev, - unsigned plane_mask, - int ret) -{ - struct drm_plane *plane; - - /* if succeeded, fixup legacy plane crtc/fb ptrs before dropping - * locks (ie. while it is still safe to deref plane->state). We - * need to do this here because the driver entry points cannot - * distinguish between legacy and atomic ioctls. - */ - drm_for_each_plane_mask(plane, dev, plane_mask) { - if (ret == 0) { - struct drm_framebuffer *new_fb = plane->state->fb; - if (new_fb) - drm_framebuffer_reference(new_fb); - plane->fb = new_fb; - plane->crtc = plane->state->crtc; - - if (plane->old_fb) - drm_framebuffer_unreference(plane->old_fb); - } - plane->old_fb = NULL; - } -} -EXPORT_SYMBOL(drm_atomic_clean_old_fb); - -int drm_mode_atomic_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_atomic *arg = data; - uint32_t __user *objs_ptr = (uint32_t __user *)(unsigned long)(arg->objs_ptr); - uint32_t __user *count_props_ptr = (uint32_t __user *)(unsigned long)(arg->count_props_ptr); - uint32_t __user *props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); - uint64_t __user *prop_values_ptr = (uint64_t __user *)(unsigned long)(arg->prop_values_ptr); - unsigned int copied_objs, copied_props; - struct drm_atomic_state *state; - struct drm_modeset_acquire_ctx ctx; - struct drm_plane *plane; - struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; - unsigned plane_mask; - int ret = 0; - unsigned int i, j; - - /* disallow for drivers not supporting atomic: */ - if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) - return -EINVAL; - - /* disallow for userspace that has not enabled atomic cap (even - * though this may be a bit overkill, since legacy userspace - * wouldn't know how to call this ioctl) - */ - if (!file_priv->atomic) - return -EINVAL; - - if (arg->flags & ~DRM_MODE_ATOMIC_FLAGS) - return -EINVAL; - - if (arg->reserved) - return -EINVAL; - - if ((arg->flags & DRM_MODE_PAGE_FLIP_ASYNC) && - !dev->mode_config.async_page_flip) - return -EINVAL; - - /* can't test and expect an event at the same time. */ - if ((arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) && - (arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) - return -EINVAL; - - drm_modeset_acquire_init(&ctx, 0); - - state = drm_atomic_state_alloc(dev); - if (!state) - return -ENOMEM; - - state->acquire_ctx = &ctx; - state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET); - -retry: - plane_mask = 0; - copied_objs = 0; - copied_props = 0; - - for (i = 0; i < arg->count_objs; i++) { - uint32_t obj_id, count_props; - struct drm_mode_object *obj; - - if (get_user(obj_id, objs_ptr + copied_objs)) { - ret = -EFAULT; - goto out; - } - - obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY); - if (!obj || !obj->properties) { - ret = -ENOENT; - goto out; - } - - if (get_user(count_props, count_props_ptr + copied_objs)) { - ret = -EFAULT; - goto out; - } - - copied_objs++; - - for (j = 0; j < count_props; j++) { - uint32_t prop_id; - uint64_t prop_value; - struct drm_property *prop; - - if (get_user(prop_id, props_ptr + copied_props)) { - ret = -EFAULT; - goto out; - } - - prop = drm_property_find(dev, prop_id); - if (!prop) { - ret = -ENOENT; - goto out; - } - - if (copy_from_user(&prop_value, - prop_values_ptr + copied_props, - sizeof(prop_value))) { - ret = -EFAULT; - goto out; - } - - ret = atomic_set_prop(state, obj, prop, prop_value); - if (ret) - goto out; - - copied_props++; - } - - if (obj->type == DRM_MODE_OBJECT_PLANE && count_props && - !(arg->flags & DRM_MODE_ATOMIC_TEST_ONLY)) { - plane = obj_to_plane(obj); - plane_mask |= (1 << drm_plane_index(plane)); - plane->old_fb = plane->fb; - } - } - - if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) { - for_each_crtc_in_state(state, crtc, crtc_state, i) { - struct drm_pending_vblank_event *e; - - e = create_vblank_event(dev, file_priv, arg->user_data); - if (!e) { - ret = -ENOMEM; - goto out; - } - - crtc_state->event = e; - } - } - - if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) { - /* - * Unlike commit, check_only does not clean up state. - * Below we call drm_atomic_state_free for it. - */ - ret = drm_atomic_check_only(state); - } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) { - ret = drm_atomic_async_commit(state); - } else { - ret = drm_atomic_commit(state); - } - -out: - drm_atomic_clean_old_fb(dev, plane_mask, ret); - - if (ret && arg->flags & DRM_MODE_PAGE_FLIP_EVENT) { - /* - * TEST_ONLY and PAGE_FLIP_EVENT are mutually exclusive, - * if they weren't, this code should be called on success - * for TEST_ONLY too. - */ - - for_each_crtc_in_state(state, crtc, crtc_state, i) { - if (!crtc_state->event) - continue; - - destroy_vblank_event(dev, file_priv, - crtc_state->event); - } - } - - if (ret == -EDEADLK) { - drm_atomic_state_clear(state); - drm_modeset_backoff(&ctx); - goto retry; - } - - if (ret || arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) - drm_atomic_state_free(state); - - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - - return ret; -} diff --git a/src/4.x/drivers/gpu/drm/drm_atomic_helper.c b/src/4.x/drivers/gpu/drm/drm_atomic_helper.c deleted file mode 100644 index ea443fafb..000000000 --- a/src/4.x/drivers/gpu/drm/drm_atomic_helper.c +++ /dev/null @@ -1,2536 +0,0 @@ -/* - * Copyright (C) 2014 Red Hat - * Copyright (C) 2014 Intel Corp. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rob Clark - * Daniel Vetter - */ - -#include -#include -#include -#include -#include -#include - -/** - * DOC: overview - * - * This helper library provides implementations of check and commit functions on - * top of the CRTC modeset helper callbacks and the plane helper callbacks. It - * also provides convenience implementations for the atomic state handling - * callbacks for drivers which don't need to subclass the drm core structures to - * add their own additional internal state. - * - * This library also provides default implementations for the check callback in - * drm_atomic_helper_check() and for the commit callback with - * drm_atomic_helper_commit(). But the individual stages and callbacks are - * exposed to allow drivers to mix and match and e.g. use the plane helpers only - * together with a driver private modeset implementation. - * - * This library also provides implementations for all the legacy driver - * interfaces on top of the atomic interface. See drm_atomic_helper_set_config(), - * drm_atomic_helper_disable_plane(), drm_atomic_helper_disable_plane() and the - * various functions to implement set_property callbacks. New drivers must not - * implement these functions themselves but must use the provided helpers. - */ -static void -drm_atomic_helper_plane_changed(struct drm_atomic_state *state, - struct drm_plane_state *plane_state, - struct drm_plane *plane) -{ - struct drm_crtc_state *crtc_state; - - if (plane->state->crtc) { - crtc_state = state->crtc_states[drm_crtc_index(plane->state->crtc)]; - - if (WARN_ON(!crtc_state)) - return; - - crtc_state->planes_changed = true; - } - - if (plane_state->crtc) { - crtc_state = - state->crtc_states[drm_crtc_index(plane_state->crtc)]; - - if (WARN_ON(!crtc_state)) - return; - - crtc_state->planes_changed = true; - } -} - -static struct drm_crtc * -get_current_crtc_for_encoder(struct drm_device *dev, - struct drm_encoder *encoder) -{ - struct drm_mode_config *config = &dev->mode_config; - struct drm_connector *connector; - - WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); - - drm_for_each_connector(connector, dev) { - if (connector->state->best_encoder != encoder) - continue; - - return connector->state->crtc; - } - - return NULL; -} - -static int -steal_encoder(struct drm_atomic_state *state, - struct drm_encoder *encoder, - struct drm_crtc *encoder_crtc) -{ - struct drm_mode_config *config = &state->dev->mode_config; - struct drm_crtc_state *crtc_state; - struct drm_connector *connector; - struct drm_connector_state *connector_state; - - /* - * We can only steal an encoder coming from a connector, which means we - * must already hold the connection_mutex. - */ - WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); - - DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d], stealing it\n", - encoder->base.id, encoder->name, - encoder_crtc->base.id); - - crtc_state = drm_atomic_get_crtc_state(state, encoder_crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); - - crtc_state->connectors_changed = true; - - list_for_each_entry(connector, &config->connector_list, head) { - if (connector->state->best_encoder != encoder) - continue; - - DRM_DEBUG_ATOMIC("Stealing encoder from [CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); - - connector_state = drm_atomic_get_connector_state(state, - connector); - if (IS_ERR(connector_state)) - return PTR_ERR(connector_state); - - connector_state->best_encoder = NULL; - } - - return 0; -} - -static int -update_connector_routing(struct drm_atomic_state *state, int conn_idx) -{ - const struct drm_connector_helper_funcs *funcs; - struct drm_encoder *new_encoder; - struct drm_crtc *encoder_crtc; - struct drm_connector *connector; - struct drm_connector_state *connector_state; - struct drm_crtc_state *crtc_state; - int idx, ret; - - connector = state->connectors[conn_idx]; - connector_state = state->connector_states[conn_idx]; - - if (!connector) - return 0; - - DRM_DEBUG_ATOMIC("Updating routing for [CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); - - if (connector->state->crtc != connector_state->crtc) { - if (connector->state->crtc) { - idx = drm_crtc_index(connector->state->crtc); - - crtc_state = state->crtc_states[idx]; - crtc_state->connectors_changed = true; - } - - if (connector_state->crtc) { - idx = drm_crtc_index(connector_state->crtc); - - crtc_state = state->crtc_states[idx]; - crtc_state->connectors_changed = true; - } - } - - if (!connector_state->crtc) { - DRM_DEBUG_ATOMIC("Disabling [CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); - - connector_state->best_encoder = NULL; - - return 0; - } - - funcs = connector->helper_private; - - if (funcs->atomic_best_encoder) - new_encoder = funcs->atomic_best_encoder(connector, - connector_state); - else - new_encoder = funcs->best_encoder(connector); - - if (!new_encoder) { - DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); - return -EINVAL; - } - - if (!drm_encoder_crtc_ok(new_encoder, connector_state->crtc)) { - DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] incompatible with [CRTC:%d]\n", - new_encoder->base.id, - new_encoder->name, - connector_state->crtc->base.id); - return -EINVAL; - } - - if (new_encoder == connector_state->best_encoder) { - DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d]\n", - connector->base.id, - connector->name, - new_encoder->base.id, - new_encoder->name, - connector_state->crtc->base.id); - - return 0; - } - - encoder_crtc = get_current_crtc_for_encoder(state->dev, - new_encoder); - - if (encoder_crtc) { - ret = steal_encoder(state, new_encoder, encoder_crtc); - if (ret) { - DRM_DEBUG_ATOMIC("Encoder stealing failed for [CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); - return ret; - } - } - - if (WARN_ON(!connector_state->crtc)) - return -EINVAL; - - connector_state->best_encoder = new_encoder; - idx = drm_crtc_index(connector_state->crtc); - - crtc_state = state->crtc_states[idx]; - crtc_state->connectors_changed = true; - - DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d]\n", - connector->base.id, - connector->name, - new_encoder->base.id, - new_encoder->name, - connector_state->crtc->base.id); - - return 0; -} - -static int -mode_fixup(struct drm_atomic_state *state) -{ - struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; - struct drm_connector *connector; - struct drm_connector_state *conn_state; - int i; - int ret; - - for_each_crtc_in_state(state, crtc, crtc_state, i) { - if (!crtc_state->mode_changed && - !crtc_state->connectors_changed) - continue; - - drm_mode_copy(&crtc_state->adjusted_mode, &crtc_state->mode); - } - - for_each_connector_in_state(state, connector, conn_state, i) { - const struct drm_encoder_helper_funcs *funcs; - struct drm_encoder *encoder; - - WARN_ON(!!conn_state->best_encoder != !!conn_state->crtc); - - if (!conn_state->crtc || !conn_state->best_encoder) - continue; - - crtc_state = - state->crtc_states[drm_crtc_index(conn_state->crtc)]; - - /* - * Each encoder has at most one connector (since we always steal - * it away), so we won't call ->mode_fixup twice. - */ - encoder = conn_state->best_encoder; - funcs = encoder->helper_private; - if (!funcs) - continue; - - ret = drm_bridge_mode_fixup(encoder->bridge, &crtc_state->mode, - &crtc_state->adjusted_mode); - if (!ret) { - DRM_DEBUG_ATOMIC("Bridge fixup failed\n"); - return -EINVAL; - } - - if (funcs->atomic_check) { - ret = funcs->atomic_check(encoder, crtc_state, - conn_state); - if (ret) { - DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] check failed\n", - encoder->base.id, encoder->name); - return ret; - } - } else if (funcs->mode_fixup) { - ret = funcs->mode_fixup(encoder, &crtc_state->mode, - &crtc_state->adjusted_mode); - if (!ret) { - DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] fixup failed\n", - encoder->base.id, encoder->name); - return -EINVAL; - } - } - } - - for_each_crtc_in_state(state, crtc, crtc_state, i) { - const struct drm_crtc_helper_funcs *funcs; - - if (!crtc_state->mode_changed && - !crtc_state->connectors_changed) - continue; - - funcs = crtc->helper_private; - if (!funcs->mode_fixup) - continue; - - ret = funcs->mode_fixup(crtc, &crtc_state->mode, - &crtc_state->adjusted_mode); - if (!ret) { - DRM_DEBUG_ATOMIC("[CRTC:%d] fixup failed\n", - crtc->base.id); - return -EINVAL; - } - } - - return 0; -} - -/** - * drm_atomic_helper_check_modeset - validate state object for modeset changes - * @dev: DRM device - * @state: the driver state object - * - * Check the state object to see if the requested state is physically possible. - * This does all the crtc and connector related computations for an atomic - * update and adds any additional connectors needed for full modesets and calls - * down into ->mode_fixup functions of the driver backend. - * - * crtc_state->mode_changed is set when the input mode is changed. - * crtc_state->connectors_changed is set when a connector is added or - * removed from the crtc. - * crtc_state->active_changed is set when crtc_state->active changes, - * which is used for dpms. - * - * IMPORTANT: - * - * Drivers which update ->mode_changed (e.g. in their ->atomic_check hooks if a - * plane update can't be done without a full modeset) _must_ call this function - * afterwards after that change. It is permitted to call this function multiple - * times for the same update, e.g. when the ->atomic_check functions depend upon - * the adjusted dotclock for fifo space allocation and watermark computation. - * - * RETURNS - * Zero for success or -errno - */ -int -drm_atomic_helper_check_modeset(struct drm_device *dev, - struct drm_atomic_state *state) -{ - struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; - struct drm_connector *connector; - struct drm_connector_state *connector_state; - int i, ret; - - for_each_crtc_in_state(state, crtc, crtc_state, i) { - if (!drm_mode_equal(&crtc->state->mode, &crtc_state->mode)) { - DRM_DEBUG_ATOMIC("[CRTC:%d] mode changed\n", - crtc->base.id); - crtc_state->mode_changed = true; - } - - if (crtc->state->enable != crtc_state->enable) { - DRM_DEBUG_ATOMIC("[CRTC:%d] enable changed\n", - crtc->base.id); - - /* - * For clarity this assignment is done here, but - * enable == 0 is only true when there are no - * connectors and a NULL mode. - * - * The other way around is true as well. enable != 0 - * iff connectors are attached and a mode is set. - */ - crtc_state->mode_changed = true; - crtc_state->connectors_changed = true; - } - } - - for_each_connector_in_state(state, connector, connector_state, i) { - /* - * This only sets crtc->mode_changed for routing changes, - * drivers must set crtc->mode_changed themselves when connector - * properties need to be updated. - */ - ret = update_connector_routing(state, i); - if (ret) - return ret; - } - - /* - * After all the routing has been prepared we need to add in any - * connector which is itself unchanged, but who's crtc changes it's - * configuration. This must be done before calling mode_fixup in case a - * crtc only changed its mode but has the same set of connectors. - */ - for_each_crtc_in_state(state, crtc, crtc_state, i) { - int num_connectors; - - /* - * We must set ->active_changed after walking connectors for - * otherwise an update that only changes active would result in - * a full modeset because update_connector_routing force that. - */ - if (crtc->state->active != crtc_state->active) { - DRM_DEBUG_ATOMIC("[CRTC:%d] active changed\n", - crtc->base.id); - crtc_state->active_changed = true; - } - - if (!drm_atomic_crtc_needs_modeset(crtc_state)) - continue; - - DRM_DEBUG_ATOMIC("[CRTC:%d] needs all connectors, enable: %c, active: %c\n", - crtc->base.id, - crtc_state->enable ? 'y' : 'n', - crtc_state->active ? 'y' : 'n'); - - ret = drm_atomic_add_affected_connectors(state, crtc); - if (ret != 0) - return ret; - - ret = drm_atomic_add_affected_planes(state, crtc); - if (ret != 0) - return ret; - - num_connectors = drm_atomic_connectors_for_crtc(state, - crtc); - - if (crtc_state->enable != !!num_connectors) { - DRM_DEBUG_ATOMIC("[CRTC:%d] enabled/connectors mismatch\n", - crtc->base.id); - - return -EINVAL; - } - } - - return mode_fixup(state); -} -EXPORT_SYMBOL(drm_atomic_helper_check_modeset); - -/** - * drm_atomic_helper_check_planes - validate state object for planes changes - * @dev: DRM device - * @state: the driver state object - * - * Check the state object to see if the requested state is physically possible. - * This does all the plane update related checks using by calling into the - * ->atomic_check hooks provided by the driver. - * - * It also sets crtc_state->planes_changed to indicate that a crtc has - * updated planes. - * - * RETURNS - * Zero for success or -errno - */ -int -drm_atomic_helper_check_planes(struct drm_device *dev, - struct drm_atomic_state *state) -{ - struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; - struct drm_plane *plane; - struct drm_plane_state *plane_state; - int i, ret = 0; - - for_each_plane_in_state(state, plane, plane_state, i) { - const struct drm_plane_helper_funcs *funcs; - - funcs = plane->helper_private; - - drm_atomic_helper_plane_changed(state, plane_state, plane); - - if (!funcs || !funcs->atomic_check) - continue; - - ret = funcs->atomic_check(plane, plane_state); - if (ret) { - DRM_DEBUG_ATOMIC("[PLANE:%d] atomic driver check failed\n", - plane->base.id); - return ret; - } - } - - for_each_crtc_in_state(state, crtc, crtc_state, i) { - const struct drm_crtc_helper_funcs *funcs; - - funcs = crtc->helper_private; - - if (!funcs || !funcs->atomic_check) - continue; - - ret = funcs->atomic_check(crtc, state->crtc_states[i]); - if (ret) { - DRM_DEBUG_ATOMIC("[CRTC:%d] atomic driver check failed\n", - crtc->base.id); - return ret; - } - } - - return ret; -} -EXPORT_SYMBOL(drm_atomic_helper_check_planes); - -/** - * drm_atomic_helper_check - validate state object - * @dev: DRM device - * @state: the driver state object - * - * Check the state object to see if the requested state is physically possible. - * Only crtcs and planes have check callbacks, so for any additional (global) - * checking that a driver needs it can simply wrap that around this function. - * Drivers without such needs can directly use this as their ->atomic_check() - * callback. - * - * This just wraps the two parts of the state checking for planes and modeset - * state in the default order: First it calls drm_atomic_helper_check_modeset() - * and then drm_atomic_helper_check_planes(). The assumption is that the - * ->atomic_check functions depend upon an updated adjusted_mode.clock to - * e.g. properly compute watermarks. - * - * RETURNS - * Zero for success or -errno - */ -int drm_atomic_helper_check(struct drm_device *dev, - struct drm_atomic_state *state) -{ - int ret; - - ret = drm_atomic_helper_check_modeset(dev, state); - if (ret) - return ret; - - ret = drm_atomic_helper_check_planes(dev, state); - if (ret) - return ret; - - return ret; -} -EXPORT_SYMBOL(drm_atomic_helper_check); - -static void -disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) -{ - struct drm_connector *connector; - struct drm_connector_state *old_conn_state; - struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; - int i; - - for_each_connector_in_state(old_state, connector, old_conn_state, i) { - const struct drm_encoder_helper_funcs *funcs; - struct drm_encoder *encoder; - struct drm_crtc_state *old_crtc_state; - - /* Shut down everything that's in the changeset and currently - * still on. So need to check the old, saved state. */ - if (!old_conn_state->crtc) - continue; - - old_crtc_state = old_state->crtc_states[drm_crtc_index(old_conn_state->crtc)]; - - if (!old_crtc_state->active || - !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state)) - continue; - - encoder = old_conn_state->best_encoder; - - /* We shouldn't get this far if we didn't previously have - * an encoder.. but WARN_ON() rather than explode. - */ - if (WARN_ON(!encoder)) - continue; - - funcs = encoder->helper_private; - - DRM_DEBUG_ATOMIC("disabling [ENCODER:%d:%s]\n", - encoder->base.id, encoder->name); - - /* - * Each encoder has at most one connector (since we always steal - * it away), so we won't call disable hooks twice. - */ - drm_bridge_disable(encoder->bridge); - - /* Right function depends upon target state. */ - if (connector->state->crtc && funcs->prepare) - funcs->prepare(encoder); - else if (funcs->disable) - funcs->disable(encoder); - else - funcs->dpms(encoder, DRM_MODE_DPMS_OFF); - - drm_bridge_post_disable(encoder->bridge); - } - - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - const struct drm_crtc_helper_funcs *funcs; - - /* Shut down everything that needs a full modeset. */ - if (!drm_atomic_crtc_needs_modeset(crtc->state)) - continue; - - if (!old_crtc_state->active) - continue; - - funcs = crtc->helper_private; - - DRM_DEBUG_ATOMIC("disabling [CRTC:%d]\n", - crtc->base.id); - - - /* Right function depends upon target state. */ - if (crtc->state->enable && funcs->prepare) - funcs->prepare(crtc); - else if (funcs->disable) - funcs->disable(crtc); - else - funcs->dpms(crtc, DRM_MODE_DPMS_OFF); - } -} - -/** - * drm_atomic_helper_update_legacy_modeset_state - update legacy modeset state - * @dev: DRM device - * @old_state: atomic state object with old state structures - * - * This function updates all the various legacy modeset state pointers in - * connectors, encoders and crtcs. It also updates the timestamping constants - * used for precise vblank timestamps by calling - * drm_calc_timestamping_constants(). - * - * Drivers can use this for building their own atomic commit if they don't have - * a pure helper-based modeset implementation. - */ -void -drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, - struct drm_atomic_state *old_state) -{ - struct drm_connector *connector; - struct drm_connector_state *old_conn_state; - struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; - int i; - - /* clear out existing links and update dpms */ - for_each_connector_in_state(old_state, connector, old_conn_state, i) { - if (connector->encoder) { - WARN_ON(!connector->encoder->crtc); - - connector->encoder->crtc = NULL; - connector->encoder = NULL; - } - - crtc = connector->state->crtc; - if ((!crtc && old_conn_state->crtc) || - (crtc && drm_atomic_crtc_needs_modeset(crtc->state))) { - struct drm_property *dpms_prop = - dev->mode_config.dpms_property; - int mode = DRM_MODE_DPMS_OFF; - - if (crtc && crtc->state->active) - mode = DRM_MODE_DPMS_ON; - - connector->dpms = mode; - drm_object_property_set_value(&connector->base, - dpms_prop, mode); - } - } - - /* set new links */ - for_each_connector_in_state(old_state, connector, old_conn_state, i) { - if (!connector->state->crtc) - continue; - - if (WARN_ON(!connector->state->best_encoder)) - continue; - - connector->encoder = connector->state->best_encoder; - connector->encoder->crtc = connector->state->crtc; - } - - /* set legacy state in the crtc structure */ - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - struct drm_plane *primary = crtc->primary; - - crtc->mode = crtc->state->mode; - crtc->enabled = crtc->state->enable; - - if (drm_atomic_get_existing_plane_state(old_state, primary) && - primary->state->crtc == crtc) { - crtc->x = primary->state->src_x >> 16; - crtc->y = primary->state->src_y >> 16; - } - - if (crtc->state->enable) - drm_calc_timestamping_constants(crtc, - &crtc->state->adjusted_mode); - } -} -EXPORT_SYMBOL(drm_atomic_helper_update_legacy_modeset_state); - -static void -crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) -{ - struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; - struct drm_connector *connector; - struct drm_connector_state *old_conn_state; - int i; - - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - const struct drm_crtc_helper_funcs *funcs; - - if (!crtc->state->mode_changed) - continue; - - funcs = crtc->helper_private; - - if (crtc->state->enable && funcs->mode_set_nofb) { - DRM_DEBUG_ATOMIC("modeset on [CRTC:%d]\n", - crtc->base.id); - - funcs->mode_set_nofb(crtc); - } - } - - for_each_connector_in_state(old_state, connector, old_conn_state, i) { - const struct drm_encoder_helper_funcs *funcs; - struct drm_crtc_state *new_crtc_state; - struct drm_encoder *encoder; - struct drm_display_mode *mode, *adjusted_mode; - - if (!connector->state->best_encoder) - continue; - - encoder = connector->state->best_encoder; - funcs = encoder->helper_private; - new_crtc_state = connector->state->crtc->state; - mode = &new_crtc_state->mode; - adjusted_mode = &new_crtc_state->adjusted_mode; - - if (!new_crtc_state->mode_changed) - continue; - - DRM_DEBUG_ATOMIC("modeset on [ENCODER:%d:%s]\n", - encoder->base.id, encoder->name); - - /* - * Each encoder has at most one connector (since we always steal - * it away), so we won't call mode_set hooks twice. - */ - if (funcs->mode_set) - funcs->mode_set(encoder, mode, adjusted_mode); - - drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode); - } -} - -/** - * drm_atomic_helper_commit_modeset_disables - modeset commit to disable outputs - * @dev: DRM device - * @old_state: atomic state object with old state structures - * - * This function shuts down all the outputs that need to be shut down and - * prepares them (if required) with the new mode. - * - * For compatibility with legacy crtc helpers this should be called before - * drm_atomic_helper_commit_planes(), which is what the default commit function - * does. But drivers with different needs can group the modeset commits together - * and do the plane commits at the end. This is useful for drivers doing runtime - * PM since planes updates then only happen when the CRTC is actually enabled. - */ -void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev, - struct drm_atomic_state *old_state) -{ - disable_outputs(dev, old_state); - - drm_atomic_helper_update_legacy_modeset_state(dev, old_state); - - crtc_set_mode(dev, old_state); -} -EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_disables); - -/** - * drm_atomic_helper_commit_modeset_enables - modeset commit to enable outputs - * @dev: DRM device - * @old_state: atomic state object with old state structures - * - * This function enables all the outputs with the new configuration which had to - * be turned off for the update. - * - * For compatibility with legacy crtc helpers this should be called after - * drm_atomic_helper_commit_planes(), which is what the default commit function - * does. But drivers with different needs can group the modeset commits together - * and do the plane commits at the end. This is useful for drivers doing runtime - * PM since planes updates then only happen when the CRTC is actually enabled. - */ -void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, - struct drm_atomic_state *old_state) -{ - struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; - struct drm_connector *connector; - struct drm_connector_state *old_conn_state; - int i; - - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - const struct drm_crtc_helper_funcs *funcs; - - /* Need to filter out CRTCs where only planes change. */ - if (!drm_atomic_crtc_needs_modeset(crtc->state)) - continue; - - if (!crtc->state->active) - continue; - - funcs = crtc->helper_private; - - if (crtc->state->enable) { - DRM_DEBUG_ATOMIC("enabling [CRTC:%d]\n", - crtc->base.id); - - if (funcs->enable) - funcs->enable(crtc); - else - funcs->commit(crtc); - } - } - - for_each_connector_in_state(old_state, connector, old_conn_state, i) { - const struct drm_encoder_helper_funcs *funcs; - struct drm_encoder *encoder; - - if (!connector->state->best_encoder) - continue; - - if (!connector->state->crtc->state->active || - !drm_atomic_crtc_needs_modeset(connector->state->crtc->state)) - continue; - - encoder = connector->state->best_encoder; - funcs = encoder->helper_private; - - DRM_DEBUG_ATOMIC("enabling [ENCODER:%d:%s]\n", - encoder->base.id, encoder->name); - - /* - * Each encoder has at most one connector (since we always steal - * it away), so we won't call enable hooks twice. - */ - drm_bridge_pre_enable(encoder->bridge); - - if (funcs->enable) - funcs->enable(encoder); - else - funcs->commit(encoder); - - drm_bridge_enable(encoder->bridge); - } -} -EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables); - -static void wait_for_fences(struct drm_device *dev, - struct drm_atomic_state *state) -{ - struct drm_plane *plane; - struct drm_plane_state *plane_state; - int i; - - for_each_plane_in_state(state, plane, plane_state, i) { - if (!plane->state->fence) - continue; - - WARN_ON(!plane->state->fb); - - fence_wait(plane->state->fence, false); - fence_put(plane->state->fence); - plane->state->fence = NULL; - } -} - -static bool framebuffer_changed(struct drm_device *dev, - struct drm_atomic_state *old_state, - struct drm_crtc *crtc) -{ - struct drm_plane *plane; - struct drm_plane_state *old_plane_state; - int i; - - for_each_plane_in_state(old_state, plane, old_plane_state, i) { - if (plane->state->crtc != crtc && - old_plane_state->crtc != crtc) - continue; - - if (plane->state->fb != old_plane_state->fb) - return true; - } - - return false; -} - -/** - * drm_atomic_helper_wait_for_vblanks - wait for vblank on crtcs - * @dev: DRM device - * @old_state: atomic state object with old state structures - * - * Helper to, after atomic commit, wait for vblanks on all effected - * crtcs (ie. before cleaning up old framebuffers using - * drm_atomic_helper_cleanup_planes()). It will only wait on crtcs where the - * framebuffers have actually changed to optimize for the legacy cursor and - * plane update use-case. - */ -void -drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, - struct drm_atomic_state *old_state) -{ - struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; - int i, ret; - - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - /* No one cares about the old state, so abuse it for tracking - * and store whether we hold a vblank reference (and should do a - * vblank wait) in the ->enable boolean. */ - old_crtc_state->enable = false; - - if (!crtc->state->enable) - continue; - - /* Legacy cursor ioctls are completely unsynced, and userspace - * relies on that (by doing tons of cursor updates). */ - if (old_state->legacy_cursor_update) - continue; - - if (!framebuffer_changed(dev, old_state, crtc)) - continue; - - ret = drm_crtc_vblank_get(crtc); - if (ret != 0) - continue; - - old_crtc_state->enable = true; - old_crtc_state->last_vblank_count = drm_crtc_vblank_count(crtc); - } - - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - if (!old_crtc_state->enable) - continue; - - ret = wait_event_timeout(dev->vblank[i].queue, - old_crtc_state->last_vblank_count != - drm_crtc_vblank_count(crtc), - msecs_to_jiffies(50)); - - drm_crtc_vblank_put(crtc); - } -} -EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks); - -/** - * drm_atomic_helper_commit - commit validated state object - * @dev: DRM device - * @state: the driver state object - * @async: asynchronous commit - * - * This function commits a with drm_atomic_helper_check() pre-validated state - * object. This can still fail when e.g. the framebuffer reservation fails. For - * now this doesn't implement asynchronous commits. - * - * Note that right now this function does not support async commits, and hence - * driver writers must implement their own version for now. Also note that the - * default ordering of how the various stages are called is to match the legacy - * modeset helper library closest. One peculiarity of that is that it doesn't - * mesh well with runtime PM at all. - * - * For drivers supporting runtime PM the recommended sequence is - * - * drm_atomic_helper_commit_modeset_disables(dev, state); - * - * drm_atomic_helper_commit_modeset_enables(dev, state); - * - * drm_atomic_helper_commit_planes(dev, state, true); - * - * See the kerneldoc entries for these three functions for more details. - * - * RETURNS - * Zero for success or -errno. - */ -int drm_atomic_helper_commit(struct drm_device *dev, - struct drm_atomic_state *state, - bool async) -{ - int ret; - - if (async) - return -EBUSY; - - ret = drm_atomic_helper_prepare_planes(dev, state); - if (ret) - return ret; - - /* - * This is the point of no return - everything below never fails except - * when the hw goes bonghits. Which means we can commit the new state on - * the software side now. - */ - - drm_atomic_helper_swap_state(dev, state); - - /* - * Everything below can be run asynchronously without the need to grab - * any modeset locks at all under one condition: It must be guaranteed - * that the asynchronous work has either been cancelled (if the driver - * supports it, which at least requires that the framebuffers get - * cleaned up with drm_atomic_helper_cleanup_planes()) or completed - * before the new state gets committed on the software side with - * drm_atomic_helper_swap_state(). - * - * This scheme allows new atomic state updates to be prepared and - * checked in parallel to the asynchronous completion of the previous - * update. Which is important since compositors need to figure out the - * composition of the next frame right after having submitted the - * current layout. - */ - - wait_for_fences(dev, state); - - drm_atomic_helper_commit_modeset_disables(dev, state); - - drm_atomic_helper_commit_planes(dev, state, false); - - drm_atomic_helper_commit_modeset_enables(dev, state); - - drm_atomic_helper_wait_for_vblanks(dev, state); - - drm_atomic_helper_cleanup_planes(dev, state); - - drm_atomic_state_free(state); - - return 0; -} -EXPORT_SYMBOL(drm_atomic_helper_commit); - -/** - * DOC: implementing async commit - * - * For now the atomic helpers don't support async commit directly. If there is - * real need it could be added though, using the dma-buf fence infrastructure - * for generic synchronization with outstanding rendering. - * - * For now drivers have to implement async commit themselves, with the following - * sequence being the recommended one: - * - * 1. Run drm_atomic_helper_prepare_planes() first. This is the only function - * which commit needs to call which can fail, so we want to run it first and - * synchronously. - * - * 2. Synchronize with any outstanding asynchronous commit worker threads which - * might be affected the new state update. This can be done by either cancelling - * or flushing the work items, depending upon whether the driver can deal with - * cancelled updates. Note that it is important to ensure that the framebuffer - * cleanup is still done when cancelling. - * - * For sufficient parallelism it is recommended to have a work item per crtc - * (for updates which don't touch global state) and a global one. Then we only - * need to synchronize with the crtc work items for changed crtcs and the global - * work item, which allows nice concurrent updates on disjoint sets of crtcs. - * - * 3. The software state is updated synchronously with - * drm_atomic_helper_swap_state(). Doing this under the protection of all modeset - * locks means concurrent callers never see inconsistent state. And doing this - * while it's guaranteed that no relevant async worker runs means that async - * workers do not need grab any locks. Actually they must not grab locks, for - * otherwise the work flushing will deadlock. - * - * 4. Schedule a work item to do all subsequent steps, using the split-out - * commit helpers: a) pre-plane commit b) plane commit c) post-plane commit and - * then cleaning up the framebuffers after the old framebuffer is no longer - * being displayed. - */ - -/** - * drm_atomic_helper_prepare_planes - prepare plane resources before commit - * @dev: DRM device - * @state: atomic state object with new state structures - * - * This function prepares plane state, specifically framebuffers, for the new - * configuration. If any failure is encountered this function will call - * ->cleanup_fb on any already successfully prepared framebuffer. - * - * Returns: - * 0 on success, negative error code on failure. - */ -int drm_atomic_helper_prepare_planes(struct drm_device *dev, - struct drm_atomic_state *state) -{ - int nplanes = dev->mode_config.num_total_plane; - int ret, i; - - for (i = 0; i < nplanes; i++) { - const struct drm_plane_helper_funcs *funcs; - struct drm_plane *plane = state->planes[i]; - struct drm_plane_state *plane_state = state->plane_states[i]; - - if (!plane) - continue; - - funcs = plane->helper_private; - - if (funcs->prepare_fb) { - ret = funcs->prepare_fb(plane, plane_state); - if (ret) - goto fail; - } - } - - return 0; - -fail: - for (i--; i >= 0; i--) { - const struct drm_plane_helper_funcs *funcs; - struct drm_plane *plane = state->planes[i]; - struct drm_plane_state *plane_state = state->plane_states[i]; - - if (!plane) - continue; - - funcs = plane->helper_private; - - if (funcs->cleanup_fb) - funcs->cleanup_fb(plane, plane_state); - - } - - return ret; -} -EXPORT_SYMBOL(drm_atomic_helper_prepare_planes); - -bool plane_crtc_active(struct drm_plane_state *state) -{ - return state->crtc && state->crtc->state->active; -} - -/** - * drm_atomic_helper_commit_planes - commit plane state - * @dev: DRM device - * @old_state: atomic state object with old state structures - * @active_only: Only commit on active CRTC if set - * - * This function commits the new plane state using the plane and atomic helper - * functions for planes and crtcs. It assumes that the atomic state has already - * been pushed into the relevant object state pointers, since this step can no - * longer fail. - * - * It still requires the global state object @old_state to know which planes and - * crtcs need to be updated though. - * - * Note that this function does all plane updates across all CRTCs in one step. - * If the hardware can't support this approach look at - * drm_atomic_helper_commit_planes_on_crtc() instead. - * - * Plane parameters can be updated by applications while the associated CRTC is - * disabled. The DRM/KMS core will store the parameters in the plane state, - * which will be available to the driver when the CRTC is turned on. As a result - * most drivers don't need to be immediately notified of plane updates for a - * disabled CRTC. - * - * Unless otherwise needed, drivers are advised to set the @active_only - * parameters to true in order not to receive plane update notifications related - * to a disabled CRTC. This avoids the need to manually ignore plane updates in - * driver code when the driver and/or hardware can't or just don't need to deal - * with updates on disabled CRTCs, for example when supporting runtime PM. - * - * The drm_atomic_helper_commit() default implementation only sets @active_only - * to false to most closely match the behaviour of the legacy helpers. This should - * not be copied blindly by drivers. - */ -void drm_atomic_helper_commit_planes(struct drm_device *dev, - struct drm_atomic_state *old_state, - bool active_only) -{ - struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; - struct drm_plane *plane; - struct drm_plane_state *old_plane_state; - int i; - - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - const struct drm_crtc_helper_funcs *funcs; - - funcs = crtc->helper_private; - - if (!funcs || !funcs->atomic_begin) - continue; - - if (active_only && !crtc->state->active) - continue; - - funcs->atomic_begin(crtc, old_crtc_state); - } - - for_each_plane_in_state(old_state, plane, old_plane_state, i) { - const struct drm_plane_helper_funcs *funcs; - bool disabling; - - funcs = plane->helper_private; - - if (!funcs) - continue; - - disabling = drm_atomic_plane_disabling(plane, old_plane_state); - - if (active_only) { - /* - * Skip planes related to inactive CRTCs. If the plane - * is enabled use the state of the current CRTC. If the - * plane is being disabled use the state of the old - * CRTC to avoid skipping planes being disabled on an - * active CRTC. - */ - if (!disabling && !plane_crtc_active(plane->state)) - continue; - if (disabling && !plane_crtc_active(old_plane_state)) - continue; - } - - /* - * Special-case disabling the plane if drivers support it. - */ - if (disabling && funcs->atomic_disable) - funcs->atomic_disable(plane, old_plane_state); - else if (plane->state->crtc || disabling) - funcs->atomic_update(plane, old_plane_state); - } - - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - const struct drm_crtc_helper_funcs *funcs; - - funcs = crtc->helper_private; - - if (!funcs || !funcs->atomic_flush) - continue; - - if (active_only && !crtc->state->active) - continue; - - funcs->atomic_flush(crtc, old_crtc_state); - } -} -EXPORT_SYMBOL(drm_atomic_helper_commit_planes); - -/** - * drm_atomic_helper_commit_planes_on_crtc - commit plane state for a crtc - * @old_crtc_state: atomic state object with the old crtc state - * - * This function commits the new plane state using the plane and atomic helper - * functions for planes on the specific crtc. It assumes that the atomic state - * has already been pushed into the relevant object state pointers, since this - * step can no longer fail. - * - * This function is useful when plane updates should be done crtc-by-crtc - * instead of one global step like drm_atomic_helper_commit_planes() does. - * - * This function can only be savely used when planes are not allowed to move - * between different CRTCs because this function doesn't handle inter-CRTC - * depencies. Callers need to ensure that either no such depencies exist, - * resolve them through ordering of commit calls or through some other means. - */ -void -drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state) -{ - const struct drm_crtc_helper_funcs *crtc_funcs; - struct drm_crtc *crtc = old_crtc_state->crtc; - struct drm_atomic_state *old_state = old_crtc_state->state; - struct drm_plane *plane; - unsigned plane_mask; - - plane_mask = old_crtc_state->plane_mask; - plane_mask |= crtc->state->plane_mask; - - crtc_funcs = crtc->helper_private; - if (crtc_funcs && crtc_funcs->atomic_begin) - crtc_funcs->atomic_begin(crtc, old_crtc_state); - - drm_for_each_plane_mask(plane, crtc->dev, plane_mask) { - struct drm_plane_state *old_plane_state = - drm_atomic_get_existing_plane_state(old_state, plane); - const struct drm_plane_helper_funcs *plane_funcs; - - plane_funcs = plane->helper_private; - - if (!old_plane_state || !plane_funcs) - continue; - - WARN_ON(plane->state->crtc && plane->state->crtc != crtc); - - if (drm_atomic_plane_disabling(plane, old_plane_state) && - plane_funcs->atomic_disable) - plane_funcs->atomic_disable(plane, old_plane_state); - else if (plane->state->crtc || - drm_atomic_plane_disabling(plane, old_plane_state)) - plane_funcs->atomic_update(plane, old_plane_state); - } - - if (crtc_funcs && crtc_funcs->atomic_flush) - crtc_funcs->atomic_flush(crtc, old_crtc_state); -} -EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc); - -/** - * drm_atomic_helper_cleanup_planes - cleanup plane resources after commit - * @dev: DRM device - * @old_state: atomic state object with old state structures - * - * This function cleans up plane state, specifically framebuffers, from the old - * configuration. Hence the old configuration must be perserved in @old_state to - * be able to call this function. - * - * This function must also be called on the new state when the atomic update - * fails at any point after calling drm_atomic_helper_prepare_planes(). - */ -void drm_atomic_helper_cleanup_planes(struct drm_device *dev, - struct drm_atomic_state *old_state) -{ - struct drm_plane *plane; - struct drm_plane_state *plane_state; - int i; - - for_each_plane_in_state(old_state, plane, plane_state, i) { - const struct drm_plane_helper_funcs *funcs; - - funcs = plane->helper_private; - - if (funcs->cleanup_fb) - funcs->cleanup_fb(plane, plane_state); - } -} -EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes); - -/** - * drm_atomic_helper_swap_state - store atomic state into current sw state - * @dev: DRM device - * @state: atomic state - * - * This function stores the atomic state into the current state pointers in all - * driver objects. It should be called after all failing steps have been done - * and succeeded, but before the actual hardware state is committed. - * - * For cleanup and error recovery the current state for all changed objects will - * be swaped into @state. - * - * With that sequence it fits perfectly into the plane prepare/cleanup sequence: - * - * 1. Call drm_atomic_helper_prepare_planes() with the staged atomic state. - * - * 2. Do any other steps that might fail. - * - * 3. Put the staged state into the current state pointers with this function. - * - * 4. Actually commit the hardware state. - * - * 5. Call drm_atomic_helper_cleanup_planes() with @state, which since step 3 - * contains the old state. Also do any other cleanup required with that state. - */ -void drm_atomic_helper_swap_state(struct drm_device *dev, - struct drm_atomic_state *state) -{ - int i; - - for (i = 0; i < dev->mode_config.num_connector; i++) { - struct drm_connector *connector = state->connectors[i]; - - if (!connector) - continue; - - connector->state->state = state; - swap(state->connector_states[i], connector->state); - connector->state->state = NULL; - } - - for (i = 0; i < dev->mode_config.num_crtc; i++) { - struct drm_crtc *crtc = state->crtcs[i]; - - if (!crtc) - continue; - - crtc->state->state = state; - swap(state->crtc_states[i], crtc->state); - crtc->state->state = NULL; - } - - for (i = 0; i < dev->mode_config.num_total_plane; i++) { - struct drm_plane *plane = state->planes[i]; - - if (!plane) - continue; - - plane->state->state = state; - swap(state->plane_states[i], plane->state); - plane->state->state = NULL; - } -} -EXPORT_SYMBOL(drm_atomic_helper_swap_state); - -/** - * drm_atomic_helper_update_plane - Helper for primary plane update using atomic - * @plane: plane object to update - * @crtc: owning CRTC of owning plane - * @fb: framebuffer to flip onto plane - * @crtc_x: x offset of primary plane on crtc - * @crtc_y: y offset of primary plane on crtc - * @crtc_w: width of primary plane rectangle on crtc - * @crtc_h: height of primary plane rectangle on crtc - * @src_x: x offset of @fb for panning - * @src_y: y offset of @fb for panning - * @src_w: width of source rectangle in @fb - * @src_h: height of source rectangle in @fb - * - * Provides a default plane update handler using the atomic driver interface. - * - * RETURNS: - * Zero on success, error code on failure - */ -int drm_atomic_helper_update_plane(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) -{ - struct drm_atomic_state *state; - struct drm_plane_state *plane_state; - int ret = 0; - - state = drm_atomic_state_alloc(plane->dev); - if (!state) - return -ENOMEM; - - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); -retry: - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - goto fail; - } - - ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); - if (ret != 0) - goto fail; - drm_atomic_set_fb_for_plane(plane_state, fb); - plane_state->crtc_x = crtc_x; - plane_state->crtc_y = crtc_y; - plane_state->crtc_h = crtc_h; - plane_state->crtc_w = crtc_w; - plane_state->src_x = src_x; - plane_state->src_y = src_y; - plane_state->src_h = src_h; - plane_state->src_w = src_w; - - if (plane == crtc->cursor) - state->legacy_cursor_update = true; - - ret = drm_atomic_commit(state); - if (ret != 0) - goto fail; - - /* Driver takes ownership of state on successful commit. */ - return 0; -fail: - if (ret == -EDEADLK) - goto backoff; - - drm_atomic_state_free(state); - - return ret; -backoff: - drm_atomic_state_clear(state); - drm_atomic_legacy_backoff(state); - - /* - * Someone might have exchanged the framebuffer while we dropped locks - * in the backoff code. We need to fix up the fb refcount tracking the - * core does for us. - */ - plane->old_fb = plane->fb; - - goto retry; -} -EXPORT_SYMBOL(drm_atomic_helper_update_plane); - -/** - * drm_atomic_helper_disable_plane - Helper for primary plane disable using * atomic - * @plane: plane to disable - * - * Provides a default plane disable handler using the atomic driver interface. - * - * RETURNS: - * Zero on success, error code on failure - */ -int drm_atomic_helper_disable_plane(struct drm_plane *plane) -{ - struct drm_atomic_state *state; - struct drm_plane_state *plane_state; - int ret = 0; - - /* - * FIXME: Without plane->crtc set we can't get at the implicit legacy - * acquire context. The real fix will be to wire the acquire ctx through - * everywhere we need it, but meanwhile prevent chaos by just skipping - * this noop. The critical case is the cursor ioctls which a) only grab - * crtc/cursor-plane locks (so we need the crtc to get at the right - * acquire context) and b) can try to disable the plane multiple times. - */ - if (!plane->crtc) - return 0; - - state = drm_atomic_state_alloc(plane->dev); - if (!state) - return -ENOMEM; - - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(plane->crtc); -retry: - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - goto fail; - } - - if (plane_state->crtc && (plane == plane->crtc->cursor)) - plane_state->state->legacy_cursor_update = true; - - ret = __drm_atomic_helper_disable_plane(plane, plane_state); - if (ret != 0) - goto fail; - - ret = drm_atomic_commit(state); - if (ret != 0) - goto fail; - - /* Driver takes ownership of state on successful commit. */ - return 0; -fail: - if (ret == -EDEADLK) - goto backoff; - - drm_atomic_state_free(state); - - return ret; -backoff: - drm_atomic_state_clear(state); - drm_atomic_legacy_backoff(state); - - /* - * Someone might have exchanged the framebuffer while we dropped locks - * in the backoff code. We need to fix up the fb refcount tracking the - * core does for us. - */ - plane->old_fb = plane->fb; - - goto retry; -} -EXPORT_SYMBOL(drm_atomic_helper_disable_plane); - -/* just used from fb-helper and atomic-helper: */ -int __drm_atomic_helper_disable_plane(struct drm_plane *plane, - struct drm_plane_state *plane_state) -{ - int ret; - - ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); - if (ret != 0) - return ret; - - drm_atomic_set_fb_for_plane(plane_state, NULL); - plane_state->crtc_x = 0; - plane_state->crtc_y = 0; - plane_state->crtc_h = 0; - plane_state->crtc_w = 0; - plane_state->src_x = 0; - plane_state->src_y = 0; - plane_state->src_h = 0; - plane_state->src_w = 0; - - return 0; -} - -static int update_output_state(struct drm_atomic_state *state, - struct drm_mode_set *set) -{ - struct drm_device *dev = set->crtc->dev; - struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; - struct drm_connector *connector; - struct drm_connector_state *conn_state; - int ret, i, j; - - ret = drm_modeset_lock(&dev->mode_config.connection_mutex, - state->acquire_ctx); - if (ret) - return ret; - - /* First grab all affected connector/crtc states. */ - for (i = 0; i < set->num_connectors; i++) { - conn_state = drm_atomic_get_connector_state(state, - set->connectors[i]); - if (IS_ERR(conn_state)) - return PTR_ERR(conn_state); - } - - for_each_crtc_in_state(state, crtc, crtc_state, i) { - ret = drm_atomic_add_affected_connectors(state, crtc); - if (ret) - return ret; - } - - /* Then recompute connector->crtc links and crtc enabling state. */ - for_each_connector_in_state(state, connector, conn_state, i) { - if (conn_state->crtc == set->crtc) { - ret = drm_atomic_set_crtc_for_connector(conn_state, - NULL); - if (ret) - return ret; - } - - for (j = 0; j < set->num_connectors; j++) { - if (set->connectors[j] == connector) { - ret = drm_atomic_set_crtc_for_connector(conn_state, - set->crtc); - if (ret) - return ret; - break; - } - } - } - - for_each_crtc_in_state(state, crtc, crtc_state, i) { - /* Don't update ->enable for the CRTC in the set_config request, - * since a mismatch would indicate a bug in the upper layers. - * The actual modeset code later on will catch any - * inconsistencies here. */ - if (crtc == set->crtc) - continue; - - if (!drm_atomic_connectors_for_crtc(state, crtc)) { - ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, - NULL); - if (ret < 0) - return ret; - - crtc_state->active = false; - } - } - - return 0; -} - -/** - * drm_atomic_helper_set_config - set a new config from userspace - * @set: mode set configuration - * - * Provides a default crtc set_config handler using the atomic driver interface. - * - * Returns: - * Returns 0 on success, negative errno numbers on failure. - */ -int drm_atomic_helper_set_config(struct drm_mode_set *set) -{ - struct drm_atomic_state *state; - struct drm_crtc *crtc = set->crtc; - int ret = 0; - - state = drm_atomic_state_alloc(crtc->dev); - if (!state) - return -ENOMEM; - - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); -retry: - ret = __drm_atomic_helper_set_config(set, state); - if (ret != 0) - goto fail; - - ret = drm_atomic_commit(state); - if (ret != 0) - goto fail; - - /* Driver takes ownership of state on successful commit. */ - return 0; -fail: - if (ret == -EDEADLK) - goto backoff; - - drm_atomic_state_free(state); - - return ret; -backoff: - drm_atomic_state_clear(state); - drm_atomic_legacy_backoff(state); - - /* - * Someone might have exchanged the framebuffer while we dropped locks - * in the backoff code. We need to fix up the fb refcount tracking the - * core does for us. - */ - crtc->primary->old_fb = crtc->primary->fb; - - goto retry; -} -EXPORT_SYMBOL(drm_atomic_helper_set_config); - -/* just used from fb-helper and atomic-helper: */ -int __drm_atomic_helper_set_config(struct drm_mode_set *set, - struct drm_atomic_state *state) -{ - struct drm_crtc_state *crtc_state; - struct drm_plane_state *primary_state; - struct drm_crtc *crtc = set->crtc; - int hdisplay, vdisplay; - int ret; - - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); - - primary_state = drm_atomic_get_plane_state(state, crtc->primary); - if (IS_ERR(primary_state)) - return PTR_ERR(primary_state); - - if (!set->mode) { - WARN_ON(set->fb); - WARN_ON(set->num_connectors); - - ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL); - if (ret != 0) - return ret; - - crtc_state->active = false; - - ret = drm_atomic_set_crtc_for_plane(primary_state, NULL); - if (ret != 0) - return ret; - - drm_atomic_set_fb_for_plane(primary_state, NULL); - - goto commit; - } - - WARN_ON(!set->fb); - WARN_ON(!set->num_connectors); - - ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode); - if (ret != 0) - return ret; - - crtc_state->active = true; - - ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); - if (ret != 0) - return ret; - - drm_crtc_get_hv_timing(set->mode, &hdisplay, &vdisplay); - - drm_atomic_set_fb_for_plane(primary_state, set->fb); - primary_state->crtc_x = 0; - primary_state->crtc_y = 0; - primary_state->crtc_h = vdisplay; - primary_state->crtc_w = hdisplay; - primary_state->src_x = set->x << 16; - primary_state->src_y = set->y << 16; - if (primary_state->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) { - primary_state->src_h = hdisplay << 16; - primary_state->src_w = vdisplay << 16; - } else { - primary_state->src_h = vdisplay << 16; - primary_state->src_w = hdisplay << 16; - } - -commit: - ret = update_output_state(state, set); - if (ret) - return ret; - - return 0; -} - -/** - * drm_atomic_helper_crtc_set_property - helper for crtc properties - * @crtc: DRM crtc - * @property: DRM property - * @val: value of property - * - * Provides a default crtc set_property handler using the atomic driver - * interface. - * - * RETURNS: - * Zero on success, error code on failure - */ -int -drm_atomic_helper_crtc_set_property(struct drm_crtc *crtc, - struct drm_property *property, - uint64_t val) -{ - struct drm_atomic_state *state; - struct drm_crtc_state *crtc_state; - int ret = 0; - - state = drm_atomic_state_alloc(crtc->dev); - if (!state) - return -ENOMEM; - - /* ->set_property is always called with all locks held. */ - state->acquire_ctx = crtc->dev->mode_config.acquire_ctx; -retry: - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); - goto fail; - } - - ret = drm_atomic_crtc_set_property(crtc, crtc_state, - property, val); - if (ret) - goto fail; - - ret = drm_atomic_commit(state); - if (ret != 0) - goto fail; - - /* Driver takes ownership of state on successful commit. */ - return 0; -fail: - if (ret == -EDEADLK) - goto backoff; - - drm_atomic_state_free(state); - - return ret; -backoff: - drm_atomic_state_clear(state); - drm_atomic_legacy_backoff(state); - - goto retry; -} -EXPORT_SYMBOL(drm_atomic_helper_crtc_set_property); - -/** - * drm_atomic_helper_plane_set_property - helper for plane properties - * @plane: DRM plane - * @property: DRM property - * @val: value of property - * - * Provides a default plane set_property handler using the atomic driver - * interface. - * - * RETURNS: - * Zero on success, error code on failure - */ -int -drm_atomic_helper_plane_set_property(struct drm_plane *plane, - struct drm_property *property, - uint64_t val) -{ - struct drm_atomic_state *state; - struct drm_plane_state *plane_state; - int ret = 0; - - state = drm_atomic_state_alloc(plane->dev); - if (!state) - return -ENOMEM; - - /* ->set_property is always called with all locks held. */ - state->acquire_ctx = plane->dev->mode_config.acquire_ctx; -retry: - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - goto fail; - } - - ret = drm_atomic_plane_set_property(plane, plane_state, - property, val); - if (ret) - goto fail; - - ret = drm_atomic_commit(state); - if (ret != 0) - goto fail; - - /* Driver takes ownership of state on successful commit. */ - return 0; -fail: - if (ret == -EDEADLK) - goto backoff; - - drm_atomic_state_free(state); - - return ret; -backoff: - drm_atomic_state_clear(state); - drm_atomic_legacy_backoff(state); - - goto retry; -} -EXPORT_SYMBOL(drm_atomic_helper_plane_set_property); - -/** - * drm_atomic_helper_connector_set_property - helper for connector properties - * @connector: DRM connector - * @property: DRM property - * @val: value of property - * - * Provides a default connector set_property handler using the atomic driver - * interface. - * - * RETURNS: - * Zero on success, error code on failure - */ -int -drm_atomic_helper_connector_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t val) -{ - struct drm_atomic_state *state; - struct drm_connector_state *connector_state; - int ret = 0; - - state = drm_atomic_state_alloc(connector->dev); - if (!state) - return -ENOMEM; - - /* ->set_property is always called with all locks held. */ - state->acquire_ctx = connector->dev->mode_config.acquire_ctx; -retry: - connector_state = drm_atomic_get_connector_state(state, connector); - if (IS_ERR(connector_state)) { - ret = PTR_ERR(connector_state); - goto fail; - } - - ret = drm_atomic_connector_set_property(connector, connector_state, - property, val); - if (ret) - goto fail; - - ret = drm_atomic_commit(state); - if (ret != 0) - goto fail; - - /* Driver takes ownership of state on successful commit. */ - return 0; -fail: - if (ret == -EDEADLK) - goto backoff; - - drm_atomic_state_free(state); - - return ret; -backoff: - drm_atomic_state_clear(state); - drm_atomic_legacy_backoff(state); - - goto retry; -} -EXPORT_SYMBOL(drm_atomic_helper_connector_set_property); - -/** - * drm_atomic_helper_page_flip - execute a legacy page flip - * @crtc: DRM crtc - * @fb: DRM framebuffer - * @event: optional DRM event to signal upon completion - * @flags: flip flags for non-vblank sync'ed updates - * - * Provides a default page flip implementation using the atomic driver interface. - * - * Note that for now so called async page flips (i.e. updates which are not - * synchronized to vblank) are not supported, since the atomic interfaces have - * no provisions for this yet. - * - * Returns: - * Returns 0 on success, negative errno numbers on failure. - */ -int drm_atomic_helper_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t flags) -{ - struct drm_plane *plane = crtc->primary; - struct drm_atomic_state *state; - struct drm_plane_state *plane_state; - struct drm_crtc_state *crtc_state; - int ret = 0; - - if (flags & DRM_MODE_PAGE_FLIP_ASYNC) - return -EINVAL; - - state = drm_atomic_state_alloc(plane->dev); - if (!state) - return -ENOMEM; - - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); -retry: - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); - goto fail; - } - crtc_state->event = event; - - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - goto fail; - } - - ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); - if (ret != 0) - goto fail; - drm_atomic_set_fb_for_plane(plane_state, fb); - - ret = drm_atomic_async_commit(state); - if (ret != 0) - goto fail; - - /* Driver takes ownership of state on successful async commit. */ - return 0; -fail: - if (ret == -EDEADLK) - goto backoff; - - drm_atomic_state_free(state); - - return ret; -backoff: - drm_atomic_state_clear(state); - drm_atomic_legacy_backoff(state); - - /* - * Someone might have exchanged the framebuffer while we dropped locks - * in the backoff code. We need to fix up the fb refcount tracking the - * core does for us. - */ - plane->old_fb = plane->fb; - - goto retry; -} -EXPORT_SYMBOL(drm_atomic_helper_page_flip); - -/** - * drm_atomic_helper_connector_dpms() - connector dpms helper implementation - * @connector: affected connector - * @mode: DPMS mode - * - * This is the main helper function provided by the atomic helper framework for - * implementing the legacy DPMS connector interface. It computes the new desired - * ->active state for the corresponding CRTC (if the connector is enabled) and - * updates it. - * - * Returns: - * Returns 0 on success, negative errno numbers on failure. - */ -int drm_atomic_helper_connector_dpms(struct drm_connector *connector, - int mode) -{ - struct drm_mode_config *config = &connector->dev->mode_config; - struct drm_atomic_state *state; - struct drm_crtc_state *crtc_state; - struct drm_crtc *crtc; - struct drm_connector *tmp_connector; - int ret; - bool active = false; - int old_mode = connector->dpms; - - if (mode != DRM_MODE_DPMS_ON) - mode = DRM_MODE_DPMS_OFF; - - connector->dpms = mode; - crtc = connector->state->crtc; - - if (!crtc) - return 0; - - state = drm_atomic_state_alloc(connector->dev); - if (!state) - return -ENOMEM; - - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); -retry: - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); - goto fail; - } - - WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); - - drm_for_each_connector(tmp_connector, connector->dev) { - if (tmp_connector->state->crtc != crtc) - continue; - - if (tmp_connector->dpms == DRM_MODE_DPMS_ON) { - active = true; - break; - } - } - crtc_state->active = active; - - ret = drm_atomic_commit(state); - if (ret != 0) - goto fail; - - /* Driver takes ownership of state on successful commit. */ - return 0; -fail: - if (ret == -EDEADLK) - goto backoff; - - connector->dpms = old_mode; - drm_atomic_state_free(state); - - return ret; -backoff: - drm_atomic_state_clear(state); - drm_atomic_legacy_backoff(state); - - goto retry; -} -EXPORT_SYMBOL(drm_atomic_helper_connector_dpms); - -/** - * DOC: atomic state reset and initialization - * - * Both the drm core and the atomic helpers assume that there is always the full - * and correct atomic software state for all connectors, CRTCs and planes - * available. Which is a bit a problem on driver load and also after system - * suspend. One way to solve this is to have a hardware state read-out - * infrastructure which reconstructs the full software state (e.g. the i915 - * driver). - * - * The simpler solution is to just reset the software state to everything off, - * which is easiest to do by calling drm_mode_config_reset(). To facilitate this - * the atomic helpers provide default reset implementations for all hooks. - */ - -/** - * drm_atomic_helper_crtc_reset - default ->reset hook for CRTCs - * @crtc: drm CRTC - * - * Resets the atomic state for @crtc by freeing the state pointer (which might - * be NULL, e.g. at driver load time) and allocating a new empty state object. - */ -void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) -{ - if (crtc->state && crtc->state->mode_blob) - drm_property_unreference_blob(crtc->state->mode_blob); - kfree(crtc->state); - crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); - - if (crtc->state) - crtc->state->crtc = crtc; -} -EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); - -/** - * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state - * @crtc: CRTC object - * @state: atomic CRTC state - * - * Copies atomic state from a CRTC's current state and resets inferred values. - * This is useful for drivers that subclass the CRTC state. - */ -void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - memcpy(state, crtc->state, sizeof(*state)); - - if (state->mode_blob) - drm_property_reference_blob(state->mode_blob); - state->mode_changed = false; - state->active_changed = false; - state->planes_changed = false; - state->connectors_changed = false; - state->event = NULL; -} -EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); - -/** - * drm_atomic_helper_crtc_duplicate_state - default state duplicate hook - * @crtc: drm CRTC - * - * Default CRTC state duplicate hook for drivers which don't have their own - * subclassed CRTC state structure. - */ -struct drm_crtc_state * -drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc) -{ - struct drm_crtc_state *state; - - if (WARN_ON(!crtc->state)) - return NULL; - - state = kmalloc(sizeof(*state), GFP_KERNEL); - if (state) - __drm_atomic_helper_crtc_duplicate_state(crtc, state); - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); - -/** - * __drm_atomic_helper_crtc_destroy_state - release CRTC state - * @crtc: CRTC object - * @state: CRTC state object to release - * - * Releases all resources stored in the CRTC state without actually freeing - * the memory of the CRTC state. This is useful for drivers that subclass the - * CRTC state. - */ -void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - if (state->mode_blob) - drm_property_unreference_blob(state->mode_blob); -} -EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); - -/** - * drm_atomic_helper_crtc_destroy_state - default state destroy hook - * @crtc: drm CRTC - * @state: CRTC state object to release - * - * Default CRTC state destroy hook for drivers which don't have their own - * subclassed CRTC state structure. - */ -void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - __drm_atomic_helper_crtc_destroy_state(crtc, state); - kfree(state); -} -EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); - -/** - * drm_atomic_helper_plane_reset - default ->reset hook for planes - * @plane: drm plane - * - * Resets the atomic state for @plane by freeing the state pointer (which might - * be NULL, e.g. at driver load time) and allocating a new empty state object. - */ -void drm_atomic_helper_plane_reset(struct drm_plane *plane) -{ - if (plane->state && plane->state->fb) - drm_framebuffer_unreference(plane->state->fb); - - kfree(plane->state); - plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); - - if (plane->state) - plane->state->plane = plane; -} -EXPORT_SYMBOL(drm_atomic_helper_plane_reset); - -/** - * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state - * @plane: plane object - * @state: atomic plane state - * - * Copies atomic state from a plane's current state. This is useful for - * drivers that subclass the plane state. - */ -void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, - struct drm_plane_state *state) -{ - memcpy(state, plane->state, sizeof(*state)); - - if (state->fb) - drm_framebuffer_reference(state->fb); -} -EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state); - -/** - * drm_atomic_helper_plane_duplicate_state - default state duplicate hook - * @plane: drm plane - * - * Default plane state duplicate hook for drivers which don't have their own - * subclassed plane state structure. - */ -struct drm_plane_state * -drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane) -{ - struct drm_plane_state *state; - - if (WARN_ON(!plane->state)) - return NULL; - - state = kmalloc(sizeof(*state), GFP_KERNEL); - if (state) - __drm_atomic_helper_plane_duplicate_state(plane, state); - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state); - -/** - * __drm_atomic_helper_plane_destroy_state - release plane state - * @plane: plane object - * @state: plane state object to release - * - * Releases all resources stored in the plane state without actually freeing - * the memory of the plane state. This is useful for drivers that subclass the - * plane state. - */ -void __drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, - struct drm_plane_state *state) -{ - if (state->fb) - drm_framebuffer_unreference(state->fb); -} -EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state); - -/** - * drm_atomic_helper_plane_destroy_state - default state destroy hook - * @plane: drm plane - * @state: plane state object to release - * - * Default plane state destroy hook for drivers which don't have their own - * subclassed plane state structure. - */ -void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, - struct drm_plane_state *state) -{ - __drm_atomic_helper_plane_destroy_state(plane, state); - kfree(state); -} -EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); - -/** - * drm_atomic_helper_connector_reset - default ->reset hook for connectors - * @connector: drm connector - * - * Resets the atomic state for @connector by freeing the state pointer (which - * might be NULL, e.g. at driver load time) and allocating a new empty state - * object. - */ -void drm_atomic_helper_connector_reset(struct drm_connector *connector) -{ - kfree(connector->state); - connector->state = kzalloc(sizeof(*connector->state), GFP_KERNEL); - - if (connector->state) - connector->state->connector = connector; -} -EXPORT_SYMBOL(drm_atomic_helper_connector_reset); - -/** - * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state - * @connector: connector object - * @state: atomic connector state - * - * Copies atomic state from a connector's current state. This is useful for - * drivers that subclass the connector state. - */ -void -__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, - struct drm_connector_state *state) -{ - memcpy(state, connector->state, sizeof(*state)); -} -EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state); - -/** - * drm_atomic_helper_connector_duplicate_state - default state duplicate hook - * @connector: drm connector - * - * Default connector state duplicate hook for drivers which don't have their own - * subclassed connector state structure. - */ -struct drm_connector_state * -drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector) -{ - struct drm_connector_state *state; - - if (WARN_ON(!connector->state)) - return NULL; - - state = kmalloc(sizeof(*state), GFP_KERNEL); - if (state) - __drm_atomic_helper_connector_duplicate_state(connector, state); - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); - -/** - * drm_atomic_helper_duplicate_state - duplicate an atomic state object - * @dev: DRM device - * @ctx: lock acquisition context - * - * Makes a copy of the current atomic state by looping over all objects and - * duplicating their respective states. - * - * Note that this treats atomic state as persistent between save and restore. - * Drivers must make sure that this is possible and won't result in confusion - * or erroneous behaviour. - * - * Note that if callers haven't already acquired all modeset locks this might - * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). - * - * Returns: - * A pointer to the copy of the atomic state object on success or an - * ERR_PTR()-encoded error code on failure. - */ -struct drm_atomic_state * -drm_atomic_helper_duplicate_state(struct drm_device *dev, - struct drm_modeset_acquire_ctx *ctx) -{ - struct drm_atomic_state *state; - struct drm_connector *conn; - struct drm_plane *plane; - struct drm_crtc *crtc; - int err = 0; - - state = drm_atomic_state_alloc(dev); - if (!state) - return ERR_PTR(-ENOMEM); - - state->acquire_ctx = ctx; - - drm_for_each_crtc(crtc, dev) { - struct drm_crtc_state *crtc_state; - - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - err = PTR_ERR(crtc_state); - goto free; - } - } - - drm_for_each_plane(plane, dev) { - struct drm_plane_state *plane_state; - - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - err = PTR_ERR(plane_state); - goto free; - } - } - - drm_for_each_connector(conn, dev) { - struct drm_connector_state *conn_state; - - conn_state = drm_atomic_get_connector_state(state, conn); - if (IS_ERR(conn_state)) { - err = PTR_ERR(conn_state); - goto free; - } - } - - /* clear the acquire context so that it isn't accidentally reused */ - state->acquire_ctx = NULL; - -free: - if (err < 0) { - drm_atomic_state_free(state); - state = ERR_PTR(err); - } - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_duplicate_state); - -/** - * __drm_atomic_helper_connector_destroy_state - release connector state - * @connector: connector object - * @state: connector state object to release - * - * Releases all resources stored in the connector state without actually - * freeing the memory of the connector state. This is useful for drivers that - * subclass the connector state. - */ -void -__drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, - struct drm_connector_state *state) -{ - /* - * This is currently a placeholder so that drivers that subclass the - * state will automatically do the right thing if code is ever added - * to this function. - */ -} -EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state); - -/** - * drm_atomic_helper_connector_destroy_state - default state destroy hook - * @connector: drm connector - * @state: connector state object to release - * - * Default connector state destroy hook for drivers which don't have their own - * subclassed connector state structure. - */ -void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, - struct drm_connector_state *state) -{ - __drm_atomic_helper_connector_destroy_state(connector, state); - kfree(state); -} -EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); diff --git a/src/4.x/drivers/gpu/drm/drm_auth.c b/src/4.x/drivers/gpu/drm/drm_auth.c deleted file mode 100644 index 50d0baa06..000000000 --- a/src/4.x/drivers/gpu/drm/drm_auth.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Author Rickard E. (Rik) Faith - * Author Gareth Hughes - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include "drm_internal.h" - -/** - * drm_getmagic - Get unique magic of a client - * @dev: DRM device to operate on - * @data: ioctl data containing the drm_auth object - * @file_priv: DRM file that performs the operation - * - * This looks up the unique magic of the passed client and returns it. If the - * client did not have a magic assigned, yet, a new one is registered. The magic - * is stored in the passed drm_auth object. - * - * Returns: 0 on success, negative error code on failure. - */ -int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_auth *auth = data; - int ret = 0; - - mutex_lock(&dev->struct_mutex); - if (!file_priv->magic) { - ret = idr_alloc(&file_priv->master->magic_map, file_priv, - 1, 0, GFP_KERNEL); - if (ret >= 0) - file_priv->magic = ret; - } - auth->magic = file_priv->magic; - mutex_unlock(&dev->struct_mutex); - - DRM_DEBUG("%u\n", auth->magic); - - return ret < 0 ? ret : 0; -} - -/** - * drm_authmagic - Authenticate client with a magic - * @dev: DRM device to operate on - * @data: ioctl data containing the drm_auth object - * @file_priv: DRM file that performs the operation - * - * This looks up a DRM client by the passed magic and authenticates it. - * - * Returns: 0 on success, negative error code on failure. - */ -int drm_authmagic(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_auth *auth = data; - struct drm_file *file; - - DRM_DEBUG("%u\n", auth->magic); - - mutex_lock(&dev->struct_mutex); - file = idr_find(&file_priv->master->magic_map, auth->magic); - if (file) { - file->authenticated = 1; - idr_replace(&file_priv->master->magic_map, NULL, auth->magic); - } - mutex_unlock(&dev->struct_mutex); - - return file ? 0 : -EINVAL; -} diff --git a/src/4.x/drivers/gpu/drm/drm_bridge.c b/src/4.x/drivers/gpu/drm/drm_bridge.c deleted file mode 100644 index 6b8f7211e..000000000 --- a/src/4.x/drivers/gpu/drm/drm_bridge.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (c) 2014 Samsung Electronics Co., Ltd - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include - -#include - -#include "drm/drmP.h" - -/** - * DOC: overview - * - * drm_bridge represents a device that hangs on to an encoder. These are handy - * when a regular drm_encoder entity isn't enough to represent the entire - * encoder chain. - * - * A bridge is always associated to a single drm_encoder at a time, but can be - * either connected to it directly, or through an intermediate bridge: - * - * encoder ---> bridge B ---> bridge A - * - * Here, the output of the encoder feeds to bridge B, and that furthers feeds to - * bridge A. - * - * The driver using the bridge is responsible to make the associations between - * the encoder and bridges. Once these links are made, the bridges will - * participate along with encoder functions to perform mode_set/enable/disable - * through the ops provided in drm_bridge_funcs. - * - * drm_bridge, like drm_panel, aren't drm_mode_object entities like planes, - * crtcs, encoders or connectors. They just provide additional hooks to get the - * desired output at the end of the encoder chain. - */ - -static DEFINE_MUTEX(bridge_lock); -static LIST_HEAD(bridge_list); - -/** - * drm_bridge_add - add the given bridge to the global bridge list - * - * @bridge: bridge control structure - * - * RETURNS: - * Unconditionally returns Zero. - */ -int drm_bridge_add(struct drm_bridge *bridge) -{ - mutex_lock(&bridge_lock); - list_add_tail(&bridge->list, &bridge_list); - mutex_unlock(&bridge_lock); - - return 0; -} -EXPORT_SYMBOL(drm_bridge_add); - -/** - * drm_bridge_remove - remove the given bridge from the global bridge list - * - * @bridge: bridge control structure - */ -void drm_bridge_remove(struct drm_bridge *bridge) -{ - mutex_lock(&bridge_lock); - list_del_init(&bridge->list); - mutex_unlock(&bridge_lock); -} -EXPORT_SYMBOL(drm_bridge_remove); - -/** - * drm_bridge_attach - associate given bridge to our DRM device - * - * @dev: DRM device - * @bridge: bridge control structure - * - * called by a kms driver to link one of our encoder/bridge to the given - * bridge. - * - * Note that setting up links between the bridge and our encoder/bridge - * objects needs to be handled by the kms driver itself - * - * RETURNS: - * Zero on success, error code on failure - */ -int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge) -{ - if (!dev || !bridge) - return -EINVAL; - - if (bridge->dev) - return -EBUSY; - - bridge->dev = dev; - - if (bridge->funcs->attach) - return bridge->funcs->attach(bridge); - - return 0; -} -EXPORT_SYMBOL(drm_bridge_attach); - -/** - * DOC: bridge callbacks - * - * The drm_bridge_funcs ops are populated by the bridge driver. The drm - * internals(atomic and crtc helpers) use the helpers defined in drm_bridge.c - * These helpers call a specific drm_bridge_funcs op for all the bridges - * during encoder configuration. - * - * When creating a bridge driver, one can implement drm_bridge_funcs op with - * the help of these rough rules: - * - * pre_enable: this contains things needed to be done for the bridge before - * its clock and timings are enabled by its source. For a bridge, its source - * is generally the encoder or bridge just before it in the encoder chain. - * - * enable: this contains things needed to be done for the bridge once its - * source is enabled. In other words, enable is called once the source is - * ready with clock and timing needed by the bridge. - * - * disable: this contains things needed to be done for the bridge assuming - * that its source is still enabled, i.e. clock and timings are still on. - * - * post_disable: this contains things needed to be done for the bridge once - * its source is disabled, i.e. once clocks and timings are off. - * - * mode_fixup: this should fixup the given mode for the bridge. It is called - * after the encoder's mode fixup. mode_fixup can also reject a mode completely - * if it's unsuitable for the hardware. - * - * mode_set: this sets up the mode for the bridge. It assumes that its source - * (an encoder or a bridge) has set the mode too. - */ - -/** - * drm_bridge_mode_fixup - fixup proposed mode for all bridges in the - * encoder chain - * @bridge: bridge control structure - * @mode: desired mode to be set for the bridge - * @adjusted_mode: updated mode that works for this bridge - * - * Calls 'mode_fixup' drm_bridge_funcs op for all the bridges in the - * encoder chain, starting from the first bridge to the last. - * - * Note: the bridge passed should be the one closest to the encoder - * - * RETURNS: - * true on success, false on failure - */ -bool drm_bridge_mode_fixup(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - bool ret = true; - - if (!bridge) - return true; - - if (bridge->funcs->mode_fixup) - ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode); - - ret = ret && drm_bridge_mode_fixup(bridge->next, mode, adjusted_mode); - - return ret; -} -EXPORT_SYMBOL(drm_bridge_mode_fixup); - -/** - * drm_bridge_disable - calls 'disable' drm_bridge_funcs op for all - * bridges in the encoder chain. - * @bridge: bridge control structure - * - * Calls 'disable' drm_bridge_funcs op for all the bridges in the encoder - * chain, starting from the last bridge to the first. These are called before - * calling the encoder's prepare op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_disable(struct drm_bridge *bridge) -{ - if (!bridge) - return; - - drm_bridge_disable(bridge->next); - - bridge->funcs->disable(bridge); -} -EXPORT_SYMBOL(drm_bridge_disable); - -/** - * drm_bridge_post_disable - calls 'post_disable' drm_bridge_funcs op for - * all bridges in the encoder chain. - * @bridge: bridge control structure - * - * Calls 'post_disable' drm_bridge_funcs op for all the bridges in the - * encoder chain, starting from the first bridge to the last. These are called - * after completing the encoder's prepare op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_post_disable(struct drm_bridge *bridge) -{ - if (!bridge) - return; - - bridge->funcs->post_disable(bridge); - - drm_bridge_post_disable(bridge->next); -} -EXPORT_SYMBOL(drm_bridge_post_disable); - -/** - * drm_bridge_mode_set - set proposed mode for all bridges in the - * encoder chain - * @bridge: bridge control structure - * @mode: desired mode to be set for the bridge - * @adjusted_mode: updated mode that works for this bridge - * - * Calls 'mode_set' drm_bridge_funcs op for all the bridges in the - * encoder chain, starting from the first bridge to the last. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_mode_set(struct drm_bridge *bridge, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - if (!bridge) - return; - - if (bridge->funcs->mode_set) - bridge->funcs->mode_set(bridge, mode, adjusted_mode); - - drm_bridge_mode_set(bridge->next, mode, adjusted_mode); -} -EXPORT_SYMBOL(drm_bridge_mode_set); - -/** - * drm_bridge_pre_enable - calls 'pre_enable' drm_bridge_funcs op for all - * bridges in the encoder chain. - * @bridge: bridge control structure - * - * Calls 'pre_enable' drm_bridge_funcs op for all the bridges in the encoder - * chain, starting from the last bridge to the first. These are called - * before calling the encoder's commit op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_pre_enable(struct drm_bridge *bridge) -{ - if (!bridge) - return; - - drm_bridge_pre_enable(bridge->next); - - bridge->funcs->pre_enable(bridge); -} -EXPORT_SYMBOL(drm_bridge_pre_enable); - -/** - * drm_bridge_enable - calls 'enable' drm_bridge_funcs op for all bridges - * in the encoder chain. - * @bridge: bridge control structure - * - * Calls 'enable' drm_bridge_funcs op for all the bridges in the encoder - * chain, starting from the first bridge to the last. These are called - * after completing the encoder's commit op. - * - * Note that the bridge passed should be the one closest to the encoder - */ -void drm_bridge_enable(struct drm_bridge *bridge) -{ - if (!bridge) - return; - - bridge->funcs->enable(bridge); - - drm_bridge_enable(bridge->next); -} -EXPORT_SYMBOL(drm_bridge_enable); - -#ifdef CONFIG_OF -/** - * of_drm_find_bridge - find the bridge corresponding to the device node in - * the global bridge list - * - * @np: device node - * - * RETURNS: - * drm_bridge control struct on success, NULL on failure - */ -struct drm_bridge *of_drm_find_bridge(struct device_node *np) -{ - struct drm_bridge *bridge; - - mutex_lock(&bridge_lock); - - list_for_each_entry(bridge, &bridge_list, list) { - if (bridge->of_node == np) { - mutex_unlock(&bridge_lock); - return bridge; - } - } - - mutex_unlock(&bridge_lock); - return NULL; -} -EXPORT_SYMBOL(of_drm_find_bridge); -#endif - -MODULE_AUTHOR("Ajay Kumar "); -MODULE_DESCRIPTION("DRM bridge infrastructure"); -MODULE_LICENSE("GPL and additional rights"); diff --git a/src/4.x/drivers/gpu/drm/drm_bufs.c b/src/4.x/drivers/gpu/drm/drm_bufs.c deleted file mode 100644 index ac22b8d86..000000000 --- a/src/4.x/drivers/gpu/drm/drm_bufs.c +++ /dev/null @@ -1,1476 +0,0 @@ -/* - * Legacy: Generic DRM Buffer Management - * - * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Author: Rickard E. (Rik) Faith - * Author: Gareth Hughes - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include "drm_legacy.h" - -#include - -static struct drm_map_list *drm_find_matching_map(struct drm_device *dev, - struct drm_local_map *map) -{ - struct drm_map_list *entry; - list_for_each_entry(entry, &dev->maplist, head) { - /* - * Because the kernel-userspace ABI is fixed at a 32-bit offset - * while PCI resources may live above that, we only compare the - * lower 32 bits of the map offset for maps of type - * _DRM_FRAMEBUFFER or _DRM_REGISTERS. - * It is assumed that if a driver have more than one resource - * of each type, the lower 32 bits are different. - */ - if (!entry->map || - map->type != entry->map->type || - entry->master != dev->primary->master) - continue; - switch (map->type) { - case _DRM_SHM: - if (map->flags != _DRM_CONTAINS_LOCK) - break; - return entry; - case _DRM_REGISTERS: - case _DRM_FRAME_BUFFER: - if ((entry->map->offset & 0xffffffff) == - (map->offset & 0xffffffff)) - return entry; - default: /* Make gcc happy */ - ; - } - if (entry->map->offset == map->offset) - return entry; - } - - return NULL; -} - -static int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash, - unsigned long user_token, int hashed_handle, int shm) -{ - int use_hashed_handle, shift; - unsigned long add; - -#if (BITS_PER_LONG == 64) - use_hashed_handle = ((user_token & 0xFFFFFFFF00000000UL) || hashed_handle); -#elif (BITS_PER_LONG == 32) - use_hashed_handle = hashed_handle; -#else -#error Unsupported long size. Neither 64 nor 32 bits. -#endif - - if (!use_hashed_handle) { - int ret; - hash->key = user_token >> PAGE_SHIFT; - ret = drm_ht_insert_item(&dev->map_hash, hash); - if (ret != -EINVAL) - return ret; - } - - shift = 0; - add = DRM_MAP_HASH_OFFSET >> PAGE_SHIFT; - if (shm && (SHMLBA > PAGE_SIZE)) { - int bits = ilog2(SHMLBA >> PAGE_SHIFT) + 1; - - /* For shared memory, we have to preserve the SHMLBA - * bits of the eventual vma->vm_pgoff value during - * mmap(). Otherwise we run into cache aliasing problems - * on some platforms. On these platforms, the pgoff of - * a mmap() request is used to pick a suitable virtual - * address for the mmap() region such that it will not - * cause cache aliasing problems. - * - * Therefore, make sure the SHMLBA relevant bits of the - * hash value we use are equal to those in the original - * kernel virtual address. - */ - shift = bits; - add |= ((user_token >> PAGE_SHIFT) & ((1UL << bits) - 1UL)); - } - - return drm_ht_just_insert_please(&dev->map_hash, hash, - user_token, 32 - PAGE_SHIFT - 3, - shift, add); -} - -/** - * Core function to create a range of memory available for mapping by a - * non-root process. - * - * Adjusts the memory offset to its absolute value according to the mapping - * type. Adds the map to the map list drm_device::maplist. Adds MTRR's where - * applicable and if supported by the kernel. - */ -static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, - unsigned int size, enum drm_map_type type, - enum drm_map_flags flags, - struct drm_map_list ** maplist) -{ - struct drm_local_map *map; - struct drm_map_list *list; - drm_dma_handle_t *dmah; - unsigned long user_token; - int ret; - - map = kmalloc(sizeof(*map), GFP_KERNEL); - if (!map) - return -ENOMEM; - - map->offset = offset; - map->size = size; - map->flags = flags; - map->type = type; - - /* Only allow shared memory to be removable since we only keep enough - * book keeping information about shared memory to allow for removal - * when processes fork. - */ - if ((map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM) { - kfree(map); - return -EINVAL; - } - DRM_DEBUG("offset = 0x%08llx, size = 0x%08lx, type = %d\n", - (unsigned long long)map->offset, map->size, map->type); - - /* page-align _DRM_SHM maps. They are allocated here so there is no security - * hole created by that and it works around various broken drivers that use - * a non-aligned quantity to map the SAREA. --BenH - */ - if (map->type == _DRM_SHM) - map->size = PAGE_ALIGN(map->size); - - if ((map->offset & (~(resource_size_t)PAGE_MASK)) || (map->size & (~PAGE_MASK))) { - kfree(map); - return -EINVAL; - } - map->mtrr = -1; - map->handle = NULL; - - switch (map->type) { - case _DRM_REGISTERS: - case _DRM_FRAME_BUFFER: -#if !defined(__sparc__) && !defined(__alpha__) && !defined(__ia64__) && !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__arm__) - if (map->offset + (map->size-1) < map->offset || - map->offset < virt_to_phys(high_memory)) { - kfree(map); - return -EINVAL; - } -#endif - /* Some drivers preinitialize some maps, without the X Server - * needing to be aware of it. Therefore, we just return success - * when the server tries to create a duplicate map. - */ - list = drm_find_matching_map(dev, map); - if (list != NULL) { - if (list->map->size != map->size) { - DRM_DEBUG("Matching maps of type %d with " - "mismatched sizes, (%ld vs %ld)\n", - map->type, map->size, - list->map->size); - list->map->size = map->size; - } - - kfree(map); - *maplist = list; - return 0; - } - - if (map->type == _DRM_FRAME_BUFFER || - (map->flags & _DRM_WRITE_COMBINING)) { - map->mtrr = - arch_phys_wc_add(map->offset, map->size); - } - if (map->type == _DRM_REGISTERS) { - if (map->flags & _DRM_WRITE_COMBINING) - map->handle = ioremap_wc(map->offset, - map->size); - else - map->handle = ioremap(map->offset, map->size); - if (!map->handle) { - kfree(map); - return -ENOMEM; - } - } - - break; - case _DRM_SHM: - list = drm_find_matching_map(dev, map); - if (list != NULL) { - if(list->map->size != map->size) { - DRM_DEBUG("Matching maps of type %d with " - "mismatched sizes, (%ld vs %ld)\n", - map->type, map->size, list->map->size); - list->map->size = map->size; - } - - kfree(map); - *maplist = list; - return 0; - } - map->handle = vmalloc_user(map->size); - DRM_DEBUG("%lu %d %p\n", - map->size, order_base_2(map->size), map->handle); - if (!map->handle) { - kfree(map); - return -ENOMEM; - } - map->offset = (unsigned long)map->handle; - if (map->flags & _DRM_CONTAINS_LOCK) { - /* Prevent a 2nd X Server from creating a 2nd lock */ - if (dev->primary->master->lock.hw_lock != NULL) { - vfree(map->handle); - kfree(map); - return -EBUSY; - } - dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle; /* Pointer to lock */ - } - break; - case _DRM_AGP: { - struct drm_agp_mem *entry; - int valid = 0; - - if (!dev->agp) { - kfree(map); - return -EINVAL; - } -#ifdef __alpha__ - map->offset += dev->hose->mem_space->start; -#endif - /* In some cases (i810 driver), user space may have already - * added the AGP base itself, because dev->agp->base previously - * only got set during AGP enable. So, only add the base - * address if the map's offset isn't already within the - * aperture. - */ - if (map->offset < dev->agp->base || - map->offset > dev->agp->base + - dev->agp->agp_info.aper_size * 1024 * 1024 - 1) { - map->offset += dev->agp->base; - } - map->mtrr = dev->agp->agp_mtrr; /* for getmap */ - - /* This assumes the DRM is in total control of AGP space. - * It's not always the case as AGP can be in the control - * of user space (i.e. i810 driver). So this loop will get - * skipped and we double check that dev->agp->memory is - * actually set as well as being invalid before EPERM'ing - */ - list_for_each_entry(entry, &dev->agp->memory, head) { - if ((map->offset >= entry->bound) && - (map->offset + map->size <= entry->bound + entry->pages * PAGE_SIZE)) { - valid = 1; - break; - } - } - if (!list_empty(&dev->agp->memory) && !valid) { - kfree(map); - return -EPERM; - } - DRM_DEBUG("AGP offset = 0x%08llx, size = 0x%08lx\n", - (unsigned long long)map->offset, map->size); - - break; - } - case _DRM_SCATTER_GATHER: - if (!dev->sg) { - kfree(map); - return -EINVAL; - } - map->offset += (unsigned long)dev->sg->virtual; - break; - case _DRM_CONSISTENT: - /* dma_addr_t is 64bit on i386 with CONFIG_HIGHMEM64G, - * As we're limiting the address to 2^32-1 (or less), - * casting it down to 32 bits is no problem, but we - * need to point to a 64bit variable first. */ - dmah = drm_pci_alloc(dev, map->size, map->size); - if (!dmah) { - kfree(map); - return -ENOMEM; - } - map->handle = dmah->vaddr; - map->offset = (unsigned long)dmah->busaddr; - kfree(dmah); - break; - default: - kfree(map); - return -EINVAL; - } - - list = kzalloc(sizeof(*list), GFP_KERNEL); - if (!list) { - if (map->type == _DRM_REGISTERS) - iounmap(map->handle); - kfree(map); - return -EINVAL; - } - list->map = map; - - mutex_lock(&dev->struct_mutex); - list_add(&list->head, &dev->maplist); - - /* Assign a 32-bit handle */ - /* We do it here so that dev->struct_mutex protects the increment */ - user_token = (map->type == _DRM_SHM) ? (unsigned long)map->handle : - map->offset; - ret = drm_map_handle(dev, &list->hash, user_token, 0, - (map->type == _DRM_SHM)); - if (ret) { - if (map->type == _DRM_REGISTERS) - iounmap(map->handle); - kfree(map); - kfree(list); - mutex_unlock(&dev->struct_mutex); - return ret; - } - - list->user_token = list->hash.key << PAGE_SHIFT; - mutex_unlock(&dev->struct_mutex); - - if (!(map->flags & _DRM_DRIVER)) - list->master = dev->primary->master; - *maplist = list; - return 0; -} - -int drm_legacy_addmap(struct drm_device * dev, resource_size_t offset, - unsigned int size, enum drm_map_type type, - enum drm_map_flags flags, struct drm_local_map **map_ptr) -{ - struct drm_map_list *list; - int rc; - - rc = drm_addmap_core(dev, offset, size, type, flags, &list); - if (!rc) - *map_ptr = list->map; - return rc; -} -EXPORT_SYMBOL(drm_legacy_addmap); - -/** - * Ioctl to specify a range of memory that is available for mapping by a - * non-root process. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a drm_map structure. - * \return zero on success or a negative value on error. - * - */ -int drm_legacy_addmap_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_map *map = data; - struct drm_map_list *maplist; - int err; - - if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP || map->type == _DRM_SHM)) - return -EPERM; - - err = drm_addmap_core(dev, map->offset, map->size, map->type, - map->flags, &maplist); - - if (err) - return err; - - /* avoid a warning on 64-bit, this casting isn't very nice, but the API is set so too late */ - map->handle = (void *)(unsigned long)maplist->user_token; - - /* - * It appears that there are no users of this value whatsoever -- - * drmAddMap just discards it. Let's not encourage its use. - * (Keeping drm_addmap_core's returned mtrr value would be wrong -- - * it's not a real mtrr index anymore.) - */ - map->mtrr = -1; - - return 0; -} - -/** - * Remove a map private from list and deallocate resources if the mapping - * isn't in use. - * - * Searches the map on drm_device::maplist, removes it from the list, see if - * its being used, and free any associate resource (such as MTRR's) if it's not - * being on use. - * - * \sa drm_legacy_addmap - */ -int drm_legacy_rmmap_locked(struct drm_device *dev, struct drm_local_map *map) -{ - struct drm_map_list *r_list = NULL, *list_t; - drm_dma_handle_t dmah; - int found = 0; - struct drm_master *master; - - /* Find the list entry for the map and remove it */ - list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) { - if (r_list->map == map) { - master = r_list->master; - list_del(&r_list->head); - drm_ht_remove_key(&dev->map_hash, - r_list->user_token >> PAGE_SHIFT); - kfree(r_list); - found = 1; - break; - } - } - - if (!found) - return -EINVAL; - - switch (map->type) { - case _DRM_REGISTERS: - iounmap(map->handle); - /* FALLTHROUGH */ - case _DRM_FRAME_BUFFER: - arch_phys_wc_del(map->mtrr); - break; - case _DRM_SHM: - vfree(map->handle); - if (master) { - if (dev->sigdata.lock == master->lock.hw_lock) - dev->sigdata.lock = NULL; - master->lock.hw_lock = NULL; /* SHM removed */ - master->lock.file_priv = NULL; - wake_up_interruptible_all(&master->lock.lock_queue); - } - break; - case _DRM_AGP: - case _DRM_SCATTER_GATHER: - break; - case _DRM_CONSISTENT: - dmah.vaddr = map->handle; - dmah.busaddr = map->offset; - dmah.size = map->size; - __drm_legacy_pci_free(dev, &dmah); - break; - } - kfree(map); - - return 0; -} -EXPORT_SYMBOL(drm_legacy_rmmap_locked); - -int drm_legacy_rmmap(struct drm_device *dev, struct drm_local_map *map) -{ - int ret; - - mutex_lock(&dev->struct_mutex); - ret = drm_legacy_rmmap_locked(dev, map); - mutex_unlock(&dev->struct_mutex); - - return ret; -} -EXPORT_SYMBOL(drm_legacy_rmmap); - -/* The rmmap ioctl appears to be unnecessary. All mappings are torn down on - * the last close of the device, and this is necessary for cleanup when things - * exit uncleanly. Therefore, having userland manually remove mappings seems - * like a pointless exercise since they're going away anyway. - * - * One use case might be after addmap is allowed for normal users for SHM and - * gets used by drivers that the server doesn't need to care about. This seems - * unlikely. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a struct drm_map structure. - * \return zero on success or a negative value on error. - */ -int drm_legacy_rmmap_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_map *request = data; - struct drm_local_map *map = NULL; - struct drm_map_list *r_list; - int ret; - - mutex_lock(&dev->struct_mutex); - list_for_each_entry(r_list, &dev->maplist, head) { - if (r_list->map && - r_list->user_token == (unsigned long)request->handle && - r_list->map->flags & _DRM_REMOVABLE) { - map = r_list->map; - break; - } - } - - /* List has wrapped around to the head pointer, or its empty we didn't - * find anything. - */ - if (list_empty(&dev->maplist) || !map) { - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - - /* Register and framebuffer maps are permanent */ - if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) { - mutex_unlock(&dev->struct_mutex); - return 0; - } - - ret = drm_legacy_rmmap_locked(dev, map); - - mutex_unlock(&dev->struct_mutex); - - return ret; -} - -/** - * Cleanup after an error on one of the addbufs() functions. - * - * \param dev DRM device. - * \param entry buffer entry where the error occurred. - * - * Frees any pages and buffers associated with the given entry. - */ -static void drm_cleanup_buf_error(struct drm_device * dev, - struct drm_buf_entry * entry) -{ - int i; - - if (entry->seg_count) { - for (i = 0; i < entry->seg_count; i++) { - if (entry->seglist[i]) { - drm_pci_free(dev, entry->seglist[i]); - } - } - kfree(entry->seglist); - - entry->seg_count = 0; - } - - if (entry->buf_count) { - for (i = 0; i < entry->buf_count; i++) { - kfree(entry->buflist[i].dev_private); - } - kfree(entry->buflist); - - entry->buf_count = 0; - } -} - -#if IS_ENABLED(CONFIG_AGP) -/** - * Add AGP buffers for DMA transfers. - * - * \param dev struct drm_device to which the buffers are to be added. - * \param request pointer to a struct drm_buf_desc describing the request. - * \return zero on success or a negative number on failure. - * - * After some sanity checks creates a drm_buf structure for each buffer and - * reallocates the buffer list of the same size order to accommodate the new - * buffers. - */ -int drm_legacy_addbufs_agp(struct drm_device *dev, - struct drm_buf_desc *request) -{ - struct drm_device_dma *dma = dev->dma; - struct drm_buf_entry *entry; - struct drm_agp_mem *agp_entry; - struct drm_buf *buf; - unsigned long offset; - unsigned long agp_offset; - int count; - int order; - int size; - int alignment; - int page_order; - int total; - int byte_count; - int i, valid; - struct drm_buf **temp_buflist; - - if (!dma) - return -EINVAL; - - count = request->count; - order = order_base_2(request->size); - size = 1 << order; - - alignment = (request->flags & _DRM_PAGE_ALIGN) - ? PAGE_ALIGN(size) : size; - page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; - total = PAGE_SIZE << page_order; - - byte_count = 0; - agp_offset = dev->agp->base + request->agp_start; - - DRM_DEBUG("count: %d\n", count); - DRM_DEBUG("order: %d\n", order); - DRM_DEBUG("size: %d\n", size); - DRM_DEBUG("agp_offset: %lx\n", agp_offset); - DRM_DEBUG("alignment: %d\n", alignment); - DRM_DEBUG("page_order: %d\n", page_order); - DRM_DEBUG("total: %d\n", total); - - if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) - return -EINVAL; - - /* Make sure buffers are located in AGP memory that we own */ - valid = 0; - list_for_each_entry(agp_entry, &dev->agp->memory, head) { - if ((agp_offset >= agp_entry->bound) && - (agp_offset + total * count <= agp_entry->bound + agp_entry->pages * PAGE_SIZE)) { - valid = 1; - break; - } - } - if (!list_empty(&dev->agp->memory) && !valid) { - DRM_DEBUG("zone invalid\n"); - return -EINVAL; - } - spin_lock(&dev->buf_lock); - if (dev->buf_use) { - spin_unlock(&dev->buf_lock); - return -EBUSY; - } - atomic_inc(&dev->buf_alloc); - spin_unlock(&dev->buf_lock); - - mutex_lock(&dev->struct_mutex); - entry = &dma->bufs[order]; - if (entry->buf_count) { - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; /* May only call once for each order */ - } - - if (count < 0 || count > 4096) { - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -EINVAL; - } - - entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL); - if (!entry->buflist) { - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - - entry->buf_size = size; - entry->page_order = page_order; - - offset = 0; - - while (entry->buf_count < count) { - buf = &entry->buflist[entry->buf_count]; - buf->idx = dma->buf_count + entry->buf_count; - buf->total = alignment; - buf->order = order; - buf->used = 0; - - buf->offset = (dma->byte_count + offset); - buf->bus_address = agp_offset + offset; - buf->address = (void *)(agp_offset + offset); - buf->next = NULL; - buf->waiting = 0; - buf->pending = 0; - buf->file_priv = NULL; - - buf->dev_priv_size = dev->driver->dev_priv_size; - buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL); - if (!buf->dev_private) { - /* Set count correctly so we free the proper amount. */ - entry->buf_count = count; - drm_cleanup_buf_error(dev, entry); - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - - DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); - - offset += alignment; - entry->buf_count++; - byte_count += PAGE_SIZE << page_order; - } - - DRM_DEBUG("byte_count: %d\n", byte_count); - - temp_buflist = krealloc(dma->buflist, - (dma->buf_count + entry->buf_count) * - sizeof(*dma->buflist), GFP_KERNEL); - if (!temp_buflist) { - /* Free the entry because it isn't valid */ - drm_cleanup_buf_error(dev, entry); - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - dma->buflist = temp_buflist; - - for (i = 0; i < entry->buf_count; i++) { - dma->buflist[i + dma->buf_count] = &entry->buflist[i]; - } - - dma->buf_count += entry->buf_count; - dma->seg_count += entry->seg_count; - dma->page_count += byte_count >> PAGE_SHIFT; - dma->byte_count += byte_count; - - DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); - DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); - - mutex_unlock(&dev->struct_mutex); - - request->count = entry->buf_count; - request->size = size; - - dma->flags = _DRM_DMA_USE_AGP; - - atomic_dec(&dev->buf_alloc); - return 0; -} -EXPORT_SYMBOL(drm_legacy_addbufs_agp); -#endif /* CONFIG_AGP */ - -int drm_legacy_addbufs_pci(struct drm_device *dev, - struct drm_buf_desc *request) -{ - struct drm_device_dma *dma = dev->dma; - int count; - int order; - int size; - int total; - int page_order; - struct drm_buf_entry *entry; - drm_dma_handle_t *dmah; - struct drm_buf *buf; - int alignment; - unsigned long offset; - int i; - int byte_count; - int page_count; - unsigned long *temp_pagelist; - struct drm_buf **temp_buflist; - - if (!drm_core_check_feature(dev, DRIVER_PCI_DMA)) - return -EINVAL; - - if (!dma) - return -EINVAL; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - count = request->count; - order = order_base_2(request->size); - size = 1 << order; - - DRM_DEBUG("count=%d, size=%d (%d), order=%d\n", - request->count, request->size, size, order); - - if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) - return -EINVAL; - - alignment = (request->flags & _DRM_PAGE_ALIGN) - ? PAGE_ALIGN(size) : size; - page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; - total = PAGE_SIZE << page_order; - - spin_lock(&dev->buf_lock); - if (dev->buf_use) { - spin_unlock(&dev->buf_lock); - return -EBUSY; - } - atomic_inc(&dev->buf_alloc); - spin_unlock(&dev->buf_lock); - - mutex_lock(&dev->struct_mutex); - entry = &dma->bufs[order]; - if (entry->buf_count) { - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; /* May only call once for each order */ - } - - if (count < 0 || count > 4096) { - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -EINVAL; - } - - entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL); - if (!entry->buflist) { - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - - entry->seglist = kzalloc(count * sizeof(*entry->seglist), GFP_KERNEL); - if (!entry->seglist) { - kfree(entry->buflist); - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - - /* Keep the original pagelist until we know all the allocations - * have succeeded - */ - temp_pagelist = kmalloc((dma->page_count + (count << page_order)) * - sizeof(*dma->pagelist), GFP_KERNEL); - if (!temp_pagelist) { - kfree(entry->buflist); - kfree(entry->seglist); - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - memcpy(temp_pagelist, - dma->pagelist, dma->page_count * sizeof(*dma->pagelist)); - DRM_DEBUG("pagelist: %d entries\n", - dma->page_count + (count << page_order)); - - entry->buf_size = size; - entry->page_order = page_order; - byte_count = 0; - page_count = 0; - - while (entry->buf_count < count) { - - dmah = drm_pci_alloc(dev, PAGE_SIZE << page_order, 0x1000); - - if (!dmah) { - /* Set count correctly so we free the proper amount. */ - entry->buf_count = count; - entry->seg_count = count; - drm_cleanup_buf_error(dev, entry); - kfree(temp_pagelist); - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - entry->seglist[entry->seg_count++] = dmah; - for (i = 0; i < (1 << page_order); i++) { - DRM_DEBUG("page %d @ 0x%08lx\n", - dma->page_count + page_count, - (unsigned long)dmah->vaddr + PAGE_SIZE * i); - temp_pagelist[dma->page_count + page_count++] - = (unsigned long)dmah->vaddr + PAGE_SIZE * i; - } - for (offset = 0; - offset + size <= total && entry->buf_count < count; - offset += alignment, ++entry->buf_count) { - buf = &entry->buflist[entry->buf_count]; - buf->idx = dma->buf_count + entry->buf_count; - buf->total = alignment; - buf->order = order; - buf->used = 0; - buf->offset = (dma->byte_count + byte_count + offset); - buf->address = (void *)(dmah->vaddr + offset); - buf->bus_address = dmah->busaddr + offset; - buf->next = NULL; - buf->waiting = 0; - buf->pending = 0; - buf->file_priv = NULL; - - buf->dev_priv_size = dev->driver->dev_priv_size; - buf->dev_private = kzalloc(buf->dev_priv_size, - GFP_KERNEL); - if (!buf->dev_private) { - /* Set count correctly so we free the proper amount. */ - entry->buf_count = count; - entry->seg_count = count; - drm_cleanup_buf_error(dev, entry); - kfree(temp_pagelist); - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - - DRM_DEBUG("buffer %d @ %p\n", - entry->buf_count, buf->address); - } - byte_count += PAGE_SIZE << page_order; - } - - temp_buflist = krealloc(dma->buflist, - (dma->buf_count + entry->buf_count) * - sizeof(*dma->buflist), GFP_KERNEL); - if (!temp_buflist) { - /* Free the entry because it isn't valid */ - drm_cleanup_buf_error(dev, entry); - kfree(temp_pagelist); - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - dma->buflist = temp_buflist; - - for (i = 0; i < entry->buf_count; i++) { - dma->buflist[i + dma->buf_count] = &entry->buflist[i]; - } - - /* No allocations failed, so now we can replace the original pagelist - * with the new one. - */ - if (dma->page_count) { - kfree(dma->pagelist); - } - dma->pagelist = temp_pagelist; - - dma->buf_count += entry->buf_count; - dma->seg_count += entry->seg_count; - dma->page_count += entry->seg_count << page_order; - dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); - - mutex_unlock(&dev->struct_mutex); - - request->count = entry->buf_count; - request->size = size; - - if (request->flags & _DRM_PCI_BUFFER_RO) - dma->flags = _DRM_DMA_USE_PCI_RO; - - atomic_dec(&dev->buf_alloc); - return 0; - -} -EXPORT_SYMBOL(drm_legacy_addbufs_pci); - -static int drm_legacy_addbufs_sg(struct drm_device *dev, - struct drm_buf_desc *request) -{ - struct drm_device_dma *dma = dev->dma; - struct drm_buf_entry *entry; - struct drm_buf *buf; - unsigned long offset; - unsigned long agp_offset; - int count; - int order; - int size; - int alignment; - int page_order; - int total; - int byte_count; - int i; - struct drm_buf **temp_buflist; - - if (!drm_core_check_feature(dev, DRIVER_SG)) - return -EINVAL; - - if (!dma) - return -EINVAL; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - count = request->count; - order = order_base_2(request->size); - size = 1 << order; - - alignment = (request->flags & _DRM_PAGE_ALIGN) - ? PAGE_ALIGN(size) : size; - page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; - total = PAGE_SIZE << page_order; - - byte_count = 0; - agp_offset = request->agp_start; - - DRM_DEBUG("count: %d\n", count); - DRM_DEBUG("order: %d\n", order); - DRM_DEBUG("size: %d\n", size); - DRM_DEBUG("agp_offset: %lu\n", agp_offset); - DRM_DEBUG("alignment: %d\n", alignment); - DRM_DEBUG("page_order: %d\n", page_order); - DRM_DEBUG("total: %d\n", total); - - if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) - return -EINVAL; - - spin_lock(&dev->buf_lock); - if (dev->buf_use) { - spin_unlock(&dev->buf_lock); - return -EBUSY; - } - atomic_inc(&dev->buf_alloc); - spin_unlock(&dev->buf_lock); - - mutex_lock(&dev->struct_mutex); - entry = &dma->bufs[order]; - if (entry->buf_count) { - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; /* May only call once for each order */ - } - - if (count < 0 || count > 4096) { - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -EINVAL; - } - - entry->buflist = kzalloc(count * sizeof(*entry->buflist), - GFP_KERNEL); - if (!entry->buflist) { - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - - entry->buf_size = size; - entry->page_order = page_order; - - offset = 0; - - while (entry->buf_count < count) { - buf = &entry->buflist[entry->buf_count]; - buf->idx = dma->buf_count + entry->buf_count; - buf->total = alignment; - buf->order = order; - buf->used = 0; - - buf->offset = (dma->byte_count + offset); - buf->bus_address = agp_offset + offset; - buf->address = (void *)(agp_offset + offset - + (unsigned long)dev->sg->virtual); - buf->next = NULL; - buf->waiting = 0; - buf->pending = 0; - buf->file_priv = NULL; - - buf->dev_priv_size = dev->driver->dev_priv_size; - buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL); - if (!buf->dev_private) { - /* Set count correctly so we free the proper amount. */ - entry->buf_count = count; - drm_cleanup_buf_error(dev, entry); - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - - DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); - - offset += alignment; - entry->buf_count++; - byte_count += PAGE_SIZE << page_order; - } - - DRM_DEBUG("byte_count: %d\n", byte_count); - - temp_buflist = krealloc(dma->buflist, - (dma->buf_count + entry->buf_count) * - sizeof(*dma->buflist), GFP_KERNEL); - if (!temp_buflist) { - /* Free the entry because it isn't valid */ - drm_cleanup_buf_error(dev, entry); - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - dma->buflist = temp_buflist; - - for (i = 0; i < entry->buf_count; i++) { - dma->buflist[i + dma->buf_count] = &entry->buflist[i]; - } - - dma->buf_count += entry->buf_count; - dma->seg_count += entry->seg_count; - dma->page_count += byte_count >> PAGE_SHIFT; - dma->byte_count += byte_count; - - DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); - DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); - - mutex_unlock(&dev->struct_mutex); - - request->count = entry->buf_count; - request->size = size; - - dma->flags = _DRM_DMA_USE_SG; - - atomic_dec(&dev->buf_alloc); - return 0; -} - -/** - * Add buffers for DMA transfers (ioctl). - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a struct drm_buf_desc request. - * \return zero on success or a negative number on failure. - * - * According with the memory type specified in drm_buf_desc::flags and the - * build options, it dispatches the call either to addbufs_agp(), - * addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent - * PCI memory respectively. - */ -int drm_legacy_addbufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_buf_desc *request = data; - int ret; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) - return -EINVAL; - -#if IS_ENABLED(CONFIG_AGP) - if (request->flags & _DRM_AGP_BUFFER) - ret = drm_legacy_addbufs_agp(dev, request); - else -#endif - if (request->flags & _DRM_SG_BUFFER) - ret = drm_legacy_addbufs_sg(dev, request); - else if (request->flags & _DRM_FB_BUFFER) - ret = -EINVAL; - else - ret = drm_legacy_addbufs_pci(dev, request); - - return ret; -} - -/** - * Get information about the buffer mappings. - * - * This was originally mean for debugging purposes, or by a sophisticated - * client library to determine how best to use the available buffers (e.g., - * large buffers can be used for image transfer). - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a drm_buf_info structure. - * \return zero on success or a negative number on failure. - * - * Increments drm_device::buf_use while holding the drm_device::buf_lock - * lock, preventing of allocating more buffers after this call. Information - * about each requested buffer is then copied into user space. - */ -int drm_legacy_infobufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_device_dma *dma = dev->dma; - struct drm_buf_info *request = data; - int i; - int count; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) - return -EINVAL; - - if (!dma) - return -EINVAL; - - spin_lock(&dev->buf_lock); - if (atomic_read(&dev->buf_alloc)) { - spin_unlock(&dev->buf_lock); - return -EBUSY; - } - ++dev->buf_use; /* Can't allocate more after this call */ - spin_unlock(&dev->buf_lock); - - for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) { - if (dma->bufs[i].buf_count) - ++count; - } - - DRM_DEBUG("count = %d\n", count); - - if (request->count >= count) { - for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) { - if (dma->bufs[i].buf_count) { - struct drm_buf_desc __user *to = - &request->list[count]; - struct drm_buf_entry *from = &dma->bufs[i]; - if (copy_to_user(&to->count, - &from->buf_count, - sizeof(from->buf_count)) || - copy_to_user(&to->size, - &from->buf_size, - sizeof(from->buf_size)) || - copy_to_user(&to->low_mark, - &from->low_mark, - sizeof(from->low_mark)) || - copy_to_user(&to->high_mark, - &from->high_mark, - sizeof(from->high_mark))) - return -EFAULT; - - DRM_DEBUG("%d %d %d %d %d\n", - i, - dma->bufs[i].buf_count, - dma->bufs[i].buf_size, - dma->bufs[i].low_mark, - dma->bufs[i].high_mark); - ++count; - } - } - } - request->count = count; - - return 0; -} - -/** - * Specifies a low and high water mark for buffer allocation - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg a pointer to a drm_buf_desc structure. - * \return zero on success or a negative number on failure. - * - * Verifies that the size order is bounded between the admissible orders and - * updates the respective drm_device_dma::bufs entry low and high water mark. - * - * \note This ioctl is deprecated and mostly never used. - */ -int drm_legacy_markbufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_device_dma *dma = dev->dma; - struct drm_buf_desc *request = data; - int order; - struct drm_buf_entry *entry; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) - return -EINVAL; - - if (!dma) - return -EINVAL; - - DRM_DEBUG("%d, %d, %d\n", - request->size, request->low_mark, request->high_mark); - order = order_base_2(request->size); - if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) - return -EINVAL; - entry = &dma->bufs[order]; - - if (request->low_mark < 0 || request->low_mark > entry->buf_count) - return -EINVAL; - if (request->high_mark < 0 || request->high_mark > entry->buf_count) - return -EINVAL; - - entry->low_mark = request->low_mark; - entry->high_mark = request->high_mark; - - return 0; -} - -/** - * Unreserve the buffers in list, previously reserved using drmDMA. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a drm_buf_free structure. - * \return zero on success or a negative number on failure. - * - * Calls free_buffer() for each used buffer. - * This function is primarily used for debugging. - */ -int drm_legacy_freebufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_device_dma *dma = dev->dma; - struct drm_buf_free *request = data; - int i; - int idx; - struct drm_buf *buf; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) - return -EINVAL; - - if (!dma) - return -EINVAL; - - DRM_DEBUG("%d\n", request->count); - for (i = 0; i < request->count; i++) { - if (copy_from_user(&idx, &request->list[i], sizeof(idx))) - return -EFAULT; - if (idx < 0 || idx >= dma->buf_count) { - DRM_ERROR("Index %d (of %d max)\n", - idx, dma->buf_count - 1); - return -EINVAL; - } - idx = array_index_nospec(idx, dma->buf_count); - buf = dma->buflist[idx]; - if (buf->file_priv != file_priv) { - DRM_ERROR("Process %d freeing buffer not owned\n", - task_pid_nr(current)); - return -EINVAL; - } - drm_legacy_free_buffer(dev, buf); - } - - return 0; -} - -/** - * Maps all of the DMA buffers into client-virtual space (ioctl). - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a drm_buf_map structure. - * \return zero on success or a negative number on failure. - * - * Maps the AGP, SG or PCI buffer region with vm_mmap(), and copies information - * about each buffer into user space. For PCI buffers, it calls vm_mmap() with - * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls - * drm_mmap_dma(). - */ -int drm_legacy_mapbufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_device_dma *dma = dev->dma; - int retcode = 0; - const int zero = 0; - unsigned long virtual; - unsigned long address; - struct drm_buf_map *request = data; - int i; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) - return -EINVAL; - - if (!dma) - return -EINVAL; - - spin_lock(&dev->buf_lock); - if (atomic_read(&dev->buf_alloc)) { - spin_unlock(&dev->buf_lock); - return -EBUSY; - } - dev->buf_use++; /* Can't allocate more after this call */ - spin_unlock(&dev->buf_lock); - - if (request->count >= dma->buf_count) { - if ((dev->agp && (dma->flags & _DRM_DMA_USE_AGP)) - || (drm_core_check_feature(dev, DRIVER_SG) - && (dma->flags & _DRM_DMA_USE_SG))) { - struct drm_local_map *map = dev->agp_buffer_map; - unsigned long token = dev->agp_buffer_token; - - if (!map) { - retcode = -EINVAL; - goto done; - } - virtual = vm_mmap(file_priv->filp, 0, map->size, - PROT_READ | PROT_WRITE, - MAP_SHARED, - token); - } else { - virtual = vm_mmap(file_priv->filp, 0, dma->byte_count, - PROT_READ | PROT_WRITE, - MAP_SHARED, 0); - } - if (virtual > -1024UL) { - /* Real error */ - retcode = (signed long)virtual; - goto done; - } - request->virtual = (void __user *)virtual; - - for (i = 0; i < dma->buf_count; i++) { - if (copy_to_user(&request->list[i].idx, - &dma->buflist[i]->idx, - sizeof(request->list[0].idx))) { - retcode = -EFAULT; - goto done; - } - if (copy_to_user(&request->list[i].total, - &dma->buflist[i]->total, - sizeof(request->list[0].total))) { - retcode = -EFAULT; - goto done; - } - if (copy_to_user(&request->list[i].used, - &zero, sizeof(zero))) { - retcode = -EFAULT; - goto done; - } - address = virtual + dma->buflist[i]->offset; /* *** */ - if (copy_to_user(&request->list[i].address, - &address, sizeof(address))) { - retcode = -EFAULT; - goto done; - } - } - } - done: - request->count = dma->buf_count; - DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode); - - return retcode; -} - -int drm_legacy_dma_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (dev->driver->dma_ioctl) - return dev->driver->dma_ioctl(dev, data, file_priv); - else - return -EINVAL; -} - -struct drm_local_map *drm_legacy_getsarea(struct drm_device *dev) -{ - struct drm_map_list *entry; - - list_for_each_entry(entry, &dev->maplist, head) { - if (entry->map && entry->map->type == _DRM_SHM && - (entry->map->flags & _DRM_CONTAINS_LOCK)) { - return entry->map; - } - } - return NULL; -} -EXPORT_SYMBOL(drm_legacy_getsarea); diff --git a/src/4.x/drivers/gpu/drm/drm_cache.c b/src/4.x/drivers/gpu/drm/drm_cache.c deleted file mode 100644 index 7f4a6c550..000000000 --- a/src/4.x/drivers/gpu/drm/drm_cache.c +++ /dev/null @@ -1,151 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2006-2007 Tungsten Graphics, Inc., Cedar Park, TX., USA - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ -/* - * Authors: Thomas Hellström - */ - -#include -#include - -#if defined(CONFIG_X86) -#include - -/* - * clflushopt is an unordered instruction which needs fencing with mfence or - * sfence to avoid ordering issues. For drm_clflush_page this fencing happens - * in the caller. - */ -static void -drm_clflush_page(struct page *page) -{ - uint8_t *page_virtual; - unsigned int i; - const int size = boot_cpu_data.x86_clflush_size; - - if (unlikely(page == NULL)) - return; - - page_virtual = kmap_atomic(page); - for (i = 0; i < PAGE_SIZE; i += size) - clflushopt(page_virtual + i); - kunmap_atomic(page_virtual); -} - -static void drm_cache_flush_clflush(struct page *pages[], - unsigned long num_pages) -{ - unsigned long i; - - mb(); - for (i = 0; i < num_pages; i++) - drm_clflush_page(*pages++); - mb(); -} -#endif - -void -drm_clflush_pages(struct page *pages[], unsigned long num_pages) -{ - -#if defined(CONFIG_X86) - if (cpu_has_clflush) { - drm_cache_flush_clflush(pages, num_pages); - return; - } - - if (wbinvd_on_all_cpus()) - printk(KERN_ERR "Timed out waiting for cache flush.\n"); - -#elif defined(__powerpc__) - unsigned long i; - for (i = 0; i < num_pages; i++) { - struct page *page = pages[i]; - void *page_virtual; - - if (unlikely(page == NULL)) - continue; - - page_virtual = kmap_atomic(page); - flush_dcache_range((unsigned long)page_virtual, - (unsigned long)page_virtual + PAGE_SIZE); - kunmap_atomic(page_virtual); - } -#else - printk(KERN_ERR "Architecture has no drm_cache.c support\n"); - WARN_ON_ONCE(1); -#endif -} -EXPORT_SYMBOL(drm_clflush_pages); - -void -drm_clflush_sg(struct sg_table *st) -{ -#if defined(CONFIG_X86) - if (cpu_has_clflush) { - struct sg_page_iter sg_iter; - - mb(); - for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) - drm_clflush_page(sg_page_iter_page(&sg_iter)); - mb(); - - return; - } - - if (wbinvd_on_all_cpus()) - printk(KERN_ERR "Timed out waiting for cache flush.\n"); -#else - printk(KERN_ERR "Architecture has no drm_cache.c support\n"); - WARN_ON_ONCE(1); -#endif -} -EXPORT_SYMBOL(drm_clflush_sg); - -void -drm_clflush_virt_range(void *addr, unsigned long length) -{ -#if defined(CONFIG_X86) - if (cpu_has_clflush) { - const int size = boot_cpu_data.x86_clflush_size; - void *end = addr + length; - addr = (void *)(((unsigned long)addr) & -size); - mb(); - for (; addr < end; addr += size) - clflushopt(addr); - clflushopt(end - 1); /* force serialisation */ - mb(); - return; - } - - if (wbinvd_on_all_cpus()) - printk(KERN_ERR "Timed out waiting for cache flush.\n"); -#else - printk(KERN_ERR "Architecture has no drm_cache.c support\n"); - WARN_ON_ONCE(1); -#endif -} -EXPORT_SYMBOL(drm_clflush_virt_range); diff --git a/src/4.x/drivers/gpu/drm/drm_context.c b/src/4.x/drivers/gpu/drm/drm_context.c deleted file mode 100644 index 192a5f9ee..000000000 --- a/src/4.x/drivers/gpu/drm/drm_context.c +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Legacy: Generic DRM Contexts - * - * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Author: Rickard E. (Rik) Faith - * Author: Gareth Hughes - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include "drm_legacy.h" - -struct drm_ctx_list { - struct list_head head; - drm_context_t handle; - struct drm_file *tag; -}; - -/******************************************************************/ -/** \name Context bitmap support */ -/*@{*/ - -/** - * Free a handle from the context bitmap. - * - * \param dev DRM device. - * \param ctx_handle context handle. - * - * Clears the bit specified by \p ctx_handle in drm_device::ctx_bitmap and the entry - * in drm_device::ctx_idr, while holding the drm_device::struct_mutex - * lock. - */ -void drm_legacy_ctxbitmap_free(struct drm_device * dev, int ctx_handle) -{ - if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) - return; - - mutex_lock(&dev->struct_mutex); - idr_remove(&dev->ctx_idr, ctx_handle); - mutex_unlock(&dev->struct_mutex); -} - -/** - * Context bitmap allocation. - * - * \param dev DRM device. - * \return (non-negative) context handle on success or a negative number on failure. - * - * Allocate a new idr from drm_device::ctx_idr while holding the - * drm_device::struct_mutex lock. - */ -static int drm_legacy_ctxbitmap_next(struct drm_device * dev) -{ - int ret; - - mutex_lock(&dev->struct_mutex); - ret = idr_alloc(&dev->ctx_idr, NULL, DRM_RESERVED_CONTEXTS, 0, - GFP_KERNEL); - mutex_unlock(&dev->struct_mutex); - return ret; -} - -/** - * Context bitmap initialization. - * - * \param dev DRM device. - * - * Initialise the drm_device::ctx_idr - */ -void drm_legacy_ctxbitmap_init(struct drm_device * dev) -{ - if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) - return; - - idr_init(&dev->ctx_idr); -} - -/** - * Context bitmap cleanup. - * - * \param dev DRM device. - * - * Free all idr members using drm_ctx_sarea_free helper function - * while holding the drm_device::struct_mutex lock. - */ -void drm_legacy_ctxbitmap_cleanup(struct drm_device * dev) -{ - if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) - return; - - mutex_lock(&dev->struct_mutex); - idr_destroy(&dev->ctx_idr); - mutex_unlock(&dev->struct_mutex); -} - -/** - * drm_ctxbitmap_flush() - Flush all contexts owned by a file - * @dev: DRM device to operate on - * @file: Open file to flush contexts for - * - * This iterates over all contexts on @dev and drops them if they're owned by - * @file. Note that after this call returns, new contexts might be added if - * the file is still alive. - */ -void drm_legacy_ctxbitmap_flush(struct drm_device *dev, struct drm_file *file) -{ - struct drm_ctx_list *pos, *tmp; - - if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) - return; - - mutex_lock(&dev->ctxlist_mutex); - - list_for_each_entry_safe(pos, tmp, &dev->ctxlist, head) { - if (pos->tag == file && - pos->handle != DRM_KERNEL_CONTEXT) { - if (dev->driver->context_dtor) - dev->driver->context_dtor(dev, pos->handle); - - drm_legacy_ctxbitmap_free(dev, pos->handle); - list_del(&pos->head); - kfree(pos); - } - } - - mutex_unlock(&dev->ctxlist_mutex); -} - -/*@}*/ - -/******************************************************************/ -/** \name Per Context SAREA Support */ -/*@{*/ - -/** - * Get per-context SAREA. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument pointing to a drm_ctx_priv_map structure. - * \return zero on success or a negative number on failure. - * - * Gets the map from drm_device::ctx_idr with the handle specified and - * returns its handle. - */ -int drm_legacy_getsareactx(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_ctx_priv_map *request = data; - struct drm_local_map *map; - struct drm_map_list *_entry; - - if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - mutex_lock(&dev->struct_mutex); - - map = idr_find(&dev->ctx_idr, request->ctx_id); - if (!map) { - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - - request->handle = NULL; - list_for_each_entry(_entry, &dev->maplist, head) { - if (_entry->map == map) { - request->handle = - (void *)(unsigned long)_entry->user_token; - break; - } - } - - mutex_unlock(&dev->struct_mutex); - - if (request->handle == NULL) - return -EINVAL; - - return 0; -} - -/** - * Set per-context SAREA. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument pointing to a drm_ctx_priv_map structure. - * \return zero on success or a negative number on failure. - * - * Searches the mapping specified in \p arg and update the entry in - * drm_device::ctx_idr with it. - */ -int drm_legacy_setsareactx(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_ctx_priv_map *request = data; - struct drm_local_map *map = NULL; - struct drm_map_list *r_list = NULL; - - if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - mutex_lock(&dev->struct_mutex); - list_for_each_entry(r_list, &dev->maplist, head) { - if (r_list->map - && r_list->user_token == (unsigned long) request->handle) - goto found; - } - bad: - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - - found: - map = r_list->map; - if (!map) - goto bad; - - if (IS_ERR(idr_replace(&dev->ctx_idr, map, request->ctx_id))) - goto bad; - - mutex_unlock(&dev->struct_mutex); - - return 0; -} - -/*@}*/ - -/******************************************************************/ -/** \name The actual DRM context handling routines */ -/*@{*/ - -/** - * Switch context. - * - * \param dev DRM device. - * \param old old context handle. - * \param new new context handle. - * \return zero on success or a negative number on failure. - * - * Attempt to set drm_device::context_flag. - */ -static int drm_context_switch(struct drm_device * dev, int old, int new) -{ - if (test_and_set_bit(0, &dev->context_flag)) { - DRM_ERROR("Reentering -- FIXME\n"); - return -EBUSY; - } - - DRM_DEBUG("Context switch from %d to %d\n", old, new); - - if (new == dev->last_context) { - clear_bit(0, &dev->context_flag); - return 0; - } - - return 0; -} - -/** - * Complete context switch. - * - * \param dev DRM device. - * \param new new context handle. - * \return zero on success or a negative number on failure. - * - * Updates drm_device::last_context and drm_device::last_switch. Verifies the - * hardware lock is held, clears the drm_device::context_flag and wakes up - * drm_device::context_wait. - */ -static int drm_context_switch_complete(struct drm_device *dev, - struct drm_file *file_priv, int new) -{ - dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ - - if (!_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock)) { - DRM_ERROR("Lock isn't held after context switch\n"); - } - - /* If a context switch is ever initiated - when the kernel holds the lock, release - that lock here. */ - clear_bit(0, &dev->context_flag); - - return 0; -} - -/** - * Reserve contexts. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument pointing to a drm_ctx_res structure. - * \return zero on success or a negative number on failure. - */ -int drm_legacy_resctx(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_ctx_res *res = data; - struct drm_ctx ctx; - int i; - - if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (res->count >= DRM_RESERVED_CONTEXTS) { - memset(&ctx, 0, sizeof(ctx)); - for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { - ctx.handle = i; - if (copy_to_user(&res->contexts[i], &ctx, sizeof(ctx))) - return -EFAULT; - } - } - res->count = DRM_RESERVED_CONTEXTS; - - return 0; -} - -/** - * Add context. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument pointing to a drm_ctx structure. - * \return zero on success or a negative number on failure. - * - * Get a new handle for the context and copy to userspace. - */ -int drm_legacy_addctx(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_ctx_list *ctx_entry; - struct drm_ctx *ctx = data; - - if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - ctx->handle = drm_legacy_ctxbitmap_next(dev); - if (ctx->handle == DRM_KERNEL_CONTEXT) { - /* Skip kernel's context and get a new one. */ - ctx->handle = drm_legacy_ctxbitmap_next(dev); - } - DRM_DEBUG("%d\n", ctx->handle); - if (ctx->handle == -1) { - DRM_DEBUG("Not enough free contexts.\n"); - /* Should this return -EBUSY instead? */ - return -ENOMEM; - } - - ctx_entry = kmalloc(sizeof(*ctx_entry), GFP_KERNEL); - if (!ctx_entry) { - DRM_DEBUG("out of memory\n"); - return -ENOMEM; - } - - INIT_LIST_HEAD(&ctx_entry->head); - ctx_entry->handle = ctx->handle; - ctx_entry->tag = file_priv; - - mutex_lock(&dev->ctxlist_mutex); - list_add(&ctx_entry->head, &dev->ctxlist); - mutex_unlock(&dev->ctxlist_mutex); - - return 0; -} - -/** - * Get context. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument pointing to a drm_ctx structure. - * \return zero on success or a negative number on failure. - */ -int drm_legacy_getctx(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_ctx *ctx = data; - - if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - /* This is 0, because we don't handle any context flags */ - ctx->flags = 0; - - return 0; -} - -/** - * Switch context. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument pointing to a drm_ctx structure. - * \return zero on success or a negative number on failure. - * - * Calls context_switch(). - */ -int drm_legacy_switchctx(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_ctx *ctx = data; - - if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - DRM_DEBUG("%d\n", ctx->handle); - return drm_context_switch(dev, dev->last_context, ctx->handle); -} - -/** - * New context. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument pointing to a drm_ctx structure. - * \return zero on success or a negative number on failure. - * - * Calls context_switch_complete(). - */ -int drm_legacy_newctx(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_ctx *ctx = data; - - if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - DRM_DEBUG("%d\n", ctx->handle); - drm_context_switch_complete(dev, file_priv, ctx->handle); - - return 0; -} - -/** - * Remove context. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument pointing to a drm_ctx structure. - * \return zero on success or a negative number on failure. - * - * If not the special kernel context, calls ctxbitmap_free() to free the specified context. - */ -int drm_legacy_rmctx(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_ctx *ctx = data; - - if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - DRM_DEBUG("%d\n", ctx->handle); - if (ctx->handle != DRM_KERNEL_CONTEXT) { - if (dev->driver->context_dtor) - dev->driver->context_dtor(dev, ctx->handle); - drm_legacy_ctxbitmap_free(dev, ctx->handle); - } - - mutex_lock(&dev->ctxlist_mutex); - if (!list_empty(&dev->ctxlist)) { - struct drm_ctx_list *pos, *n; - - list_for_each_entry_safe(pos, n, &dev->ctxlist, head) { - if (pos->handle == ctx->handle) { - list_del(&pos->head); - kfree(pos); - } - } - } - mutex_unlock(&dev->ctxlist_mutex); - - return 0; -} - -/*@}*/ diff --git a/src/4.x/drivers/gpu/drm/drm_crtc.c b/src/4.x/drivers/gpu/drm/drm_crtc.c deleted file mode 100644 index 5e4bb4837..000000000 --- a/src/4.x/drivers/gpu/drm/drm_crtc.c +++ /dev/null @@ -1,5959 +0,0 @@ -/* - * Copyright (c) 2006-2008 Intel Corporation - * Copyright (c) 2007 Dave Airlie - * Copyright (c) 2008 Red Hat Inc. - * - * DRM core CRTC related functions - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that copyright - * notice and this permission notice appear in supporting documentation, and - * that the name of the copyright holders not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. The copyright holders make no representations - * about the suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - * - * Authors: - * Keith Packard - * Eric Anholt - * Dave Airlie - * Jesse Barnes - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "drm_crtc_internal.h" -#include "drm_internal.h" - -static struct drm_framebuffer * -internal_framebuffer_create(struct drm_device *dev, - struct drm_mode_fb_cmd2 *r, - struct drm_file *file_priv); - -/* Avoid boilerplate. I'm tired of typing. */ -#define DRM_ENUM_NAME_FN(fnname, list) \ - const char *fnname(int val) \ - { \ - int i; \ - for (i = 0; i < ARRAY_SIZE(list); i++) { \ - if (list[i].type == val) \ - return list[i].name; \ - } \ - return "(unknown)"; \ - } - -/* - * Global properties - */ -static const struct drm_prop_enum_list drm_dpms_enum_list[] = { - { DRM_MODE_DPMS_ON, "On" }, - { DRM_MODE_DPMS_STANDBY, "Standby" }, - { DRM_MODE_DPMS_SUSPEND, "Suspend" }, - { DRM_MODE_DPMS_OFF, "Off" } -}; - -DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) - -static const struct drm_prop_enum_list drm_plane_type_enum_list[] = { - { DRM_PLANE_TYPE_OVERLAY, "Overlay" }, - { DRM_PLANE_TYPE_PRIMARY, "Primary" }, - { DRM_PLANE_TYPE_CURSOR, "Cursor" }, -}; - -/* - * Optional properties - */ -static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = { - { DRM_MODE_SCALE_NONE, "None" }, - { DRM_MODE_SCALE_FULLSCREEN, "Full" }, - { DRM_MODE_SCALE_CENTER, "Center" }, - { DRM_MODE_SCALE_ASPECT, "Full aspect" }, -}; - -static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = { - { DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" }, - { DRM_MODE_PICTURE_ASPECT_4_3, "4:3" }, - { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" }, -}; - -/* - * Non-global properties, but "required" for certain connectors. - */ -static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = { - { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ - { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ - { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ -}; - -DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) - -static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = { - { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ - { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ - { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ -}; - -DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, - drm_dvi_i_subconnector_enum_list) - -static const struct drm_prop_enum_list drm_tv_select_enum_list[] = { - { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ - { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ - { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ - { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ - { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ -}; - -DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) - -static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = { - { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ - { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ - { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ - { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ - { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ -}; - -DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, - drm_tv_subconnector_enum_list) - -static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = { - { DRM_MODE_DIRTY_OFF, "Off" }, - { DRM_MODE_DIRTY_ON, "On" }, - { DRM_MODE_DIRTY_ANNOTATE, "Annotate" }, -}; - -struct drm_conn_prop_enum_list { - int type; - const char *name; - struct ida ida; -}; - -/* - * Connector and encoder types. - */ -static struct drm_conn_prop_enum_list drm_connector_enum_list[] = { - { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, - { DRM_MODE_CONNECTOR_VGA, "VGA" }, - { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, - { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, - { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, - { DRM_MODE_CONNECTOR_Composite, "Composite" }, - { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" }, - { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, - { DRM_MODE_CONNECTOR_Component, "Component" }, - { DRM_MODE_CONNECTOR_9PinDIN, "DIN" }, - { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, - { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, - { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, - { DRM_MODE_CONNECTOR_TV, "TV" }, - { DRM_MODE_CONNECTOR_eDP, "eDP" }, - { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, - { DRM_MODE_CONNECTOR_DSI, "DSI" }, -}; - -static const struct drm_prop_enum_list drm_encoder_enum_list[] = { - { DRM_MODE_ENCODER_NONE, "None" }, - { DRM_MODE_ENCODER_DAC, "DAC" }, - { DRM_MODE_ENCODER_TMDS, "TMDS" }, - { DRM_MODE_ENCODER_LVDS, "LVDS" }, - { DRM_MODE_ENCODER_TVDAC, "TV" }, - { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, - { DRM_MODE_ENCODER_DSI, "DSI" }, - { DRM_MODE_ENCODER_DPMST, "DP MST" }, -}; - -static const struct drm_prop_enum_list drm_subpixel_enum_list[] = { - { SubPixelUnknown, "Unknown" }, - { SubPixelHorizontalRGB, "Horizontal RGB" }, - { SubPixelHorizontalBGR, "Horizontal BGR" }, - { SubPixelVerticalRGB, "Vertical RGB" }, - { SubPixelVerticalBGR, "Vertical BGR" }, - { SubPixelNone, "None" }, -}; - -void drm_connector_ida_init(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) - ida_init(&drm_connector_enum_list[i].ida); -} - -void drm_connector_ida_destroy(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) - ida_destroy(&drm_connector_enum_list[i].ida); -} - -/** - * drm_get_connector_status_name - return a string for connector status - * @status: connector status to compute name of - * - * In contrast to the other drm_get_*_name functions this one here returns a - * const pointer and hence is threadsafe. - */ -const char *drm_get_connector_status_name(enum drm_connector_status status) -{ - if (status == connector_status_connected) - return "connected"; - else if (status == connector_status_disconnected) - return "disconnected"; - else - return "unknown"; -} -EXPORT_SYMBOL(drm_get_connector_status_name); - -/** - * drm_get_subpixel_order_name - return a string for a given subpixel enum - * @order: enum of subpixel_order - * - * Note you could abuse this and return something out of bounds, but that - * would be a caller error. No unscrubbed user data should make it here. - */ -const char *drm_get_subpixel_order_name(enum subpixel_order order) -{ - return drm_subpixel_enum_list[order].name; -} -EXPORT_SYMBOL(drm_get_subpixel_order_name); - -static char printable_char(int c) -{ - return isascii(c) && isprint(c) ? c : '?'; -} - -/** - * drm_get_format_name - return a string for drm fourcc format - * @format: format to compute name of - * - * Note that the buffer used by this function is globally shared and owned by - * the function itself. - * - * FIXME: This isn't really multithreading safe. - */ -const char *drm_get_format_name(uint32_t format) -{ - static char buf[32]; - - snprintf(buf, sizeof(buf), - "%c%c%c%c %s-endian (0x%08x)", - printable_char(format & 0xff), - printable_char((format >> 8) & 0xff), - printable_char((format >> 16) & 0xff), - printable_char((format >> 24) & 0x7f), - format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little", - format); - - return buf; -} -EXPORT_SYMBOL(drm_get_format_name); - -/* - * Internal function to assign a slot in the object idr and optionally - * register the object into the idr. - */ -static int drm_mode_object_get_reg(struct drm_device *dev, - struct drm_mode_object *obj, - uint32_t obj_type, - bool register_obj) -{ - int ret; - - mutex_lock(&dev->mode_config.idr_mutex); - ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL); - if (ret >= 0) { - /* - * Set up the object linking under the protection of the idr - * lock so that other users can't see inconsistent state. - */ - obj->id = ret; - obj->type = obj_type; - } - mutex_unlock(&dev->mode_config.idr_mutex); - - return ret < 0 ? ret : 0; -} - -/** - * drm_mode_object_get - allocate a new modeset identifier - * @dev: DRM device - * @obj: object pointer, used to generate unique ID - * @obj_type: object type - * - * Create a unique identifier based on @ptr in @dev's identifier space. Used - * for tracking modes, CRTCs and connectors. Note that despite the _get postfix - * modeset identifiers are _not_ reference counted. Hence don't use this for - * reference counted modeset objects like framebuffers. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_mode_object_get(struct drm_device *dev, - struct drm_mode_object *obj, uint32_t obj_type) -{ - return drm_mode_object_get_reg(dev, obj, obj_type, true); -} - -static void drm_mode_object_register(struct drm_device *dev, - struct drm_mode_object *obj) -{ - mutex_lock(&dev->mode_config.idr_mutex); - idr_replace(&dev->mode_config.crtc_idr, obj, obj->id); - mutex_unlock(&dev->mode_config.idr_mutex); -} - -/** - * drm_mode_object_put - free a modeset identifer - * @dev: DRM device - * @object: object to free - * - * Free @id from @dev's unique identifier pool. Note that despite the _get - * postfix modeset identifiers are _not_ reference counted. Hence don't use this - * for reference counted modeset objects like framebuffers. - */ -void drm_mode_object_put(struct drm_device *dev, - struct drm_mode_object *object) -{ - mutex_lock(&dev->mode_config.idr_mutex); - idr_remove(&dev->mode_config.crtc_idr, object->id); - mutex_unlock(&dev->mode_config.idr_mutex); -} - -static struct drm_mode_object *_object_find(struct drm_device *dev, - uint32_t id, uint32_t type) -{ - struct drm_mode_object *obj = NULL; - - mutex_lock(&dev->mode_config.idr_mutex); - obj = idr_find(&dev->mode_config.crtc_idr, id); - if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type) - obj = NULL; - if (obj && obj->id != id) - obj = NULL; - /* don't leak out unref'd fb's */ - if (obj && - (obj->type == DRM_MODE_OBJECT_FB || - obj->type == DRM_MODE_OBJECT_BLOB)) - obj = NULL; - mutex_unlock(&dev->mode_config.idr_mutex); - - return obj; -} - -/** - * drm_mode_object_find - look up a drm object with static lifetime - * @dev: drm device - * @id: id of the mode object - * @type: type of the mode object - * - * Note that framebuffers cannot be looked up with this functions - since those - * are reference counted, they need special treatment. Even with - * DRM_MODE_OBJECT_ANY (although that will simply return NULL - * rather than WARN_ON()). - */ -struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, - uint32_t id, uint32_t type) -{ - struct drm_mode_object *obj = NULL; - - /* Framebuffers are reference counted and need their own lookup - * function.*/ - WARN_ON(type == DRM_MODE_OBJECT_FB || type == DRM_MODE_OBJECT_BLOB); - obj = _object_find(dev, id, type); - return obj; -} -EXPORT_SYMBOL(drm_mode_object_find); - -/** - * drm_framebuffer_init - initialize a framebuffer - * @dev: DRM device - * @fb: framebuffer to be initialized - * @funcs: ... with these functions - * - * Allocates an ID for the framebuffer's parent mode object, sets its mode - * functions & device file and adds it to the master fd list. - * - * IMPORTANT: - * This functions publishes the fb and makes it available for concurrent access - * by other users. Which means by this point the fb _must_ be fully set up - - * since all the fb attributes are invariant over its lifetime, no further - * locking but only correct reference counting is required. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, - const struct drm_framebuffer_funcs *funcs) -{ - int ret; - - mutex_lock(&dev->mode_config.fb_lock); - kref_init(&fb->refcount); - INIT_LIST_HEAD(&fb->filp_head); - fb->dev = dev; - fb->funcs = funcs; - - ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); - if (ret) - goto out; - - dev->mode_config.num_fb++; - list_add(&fb->head, &dev->mode_config.fb_list); -out: - mutex_unlock(&dev->mode_config.fb_lock); - - return ret; -} -EXPORT_SYMBOL(drm_framebuffer_init); - -/* dev->mode_config.fb_lock must be held! */ -static void __drm_framebuffer_unregister(struct drm_device *dev, - struct drm_framebuffer *fb) -{ - mutex_lock(&dev->mode_config.idr_mutex); - idr_remove(&dev->mode_config.crtc_idr, fb->base.id); - mutex_unlock(&dev->mode_config.idr_mutex); - - fb->base.id = 0; -} - -static void drm_framebuffer_free(struct kref *kref) -{ - struct drm_framebuffer *fb = - container_of(kref, struct drm_framebuffer, refcount); - struct drm_device *dev = fb->dev; - - /* - * The lookup idr holds a weak reference, which has not necessarily been - * removed at this point. Check for that. - */ - mutex_lock(&dev->mode_config.fb_lock); - if (fb->base.id) { - /* Mark fb as reaped and drop idr ref. */ - __drm_framebuffer_unregister(dev, fb); - } - mutex_unlock(&dev->mode_config.fb_lock); - - fb->funcs->destroy(fb); -} - -static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev, - uint32_t id) -{ - struct drm_mode_object *obj = NULL; - struct drm_framebuffer *fb; - - mutex_lock(&dev->mode_config.idr_mutex); - obj = idr_find(&dev->mode_config.crtc_idr, id); - if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) - fb = NULL; - else - fb = obj_to_fb(obj); - mutex_unlock(&dev->mode_config.idr_mutex); - - return fb; -} - -/** - * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference - * @dev: drm device - * @id: id of the fb object - * - * If successful, this grabs an additional reference to the framebuffer - - * callers need to make sure to eventually unreference the returned framebuffer - * again, using @drm_framebuffer_unreference. - */ -struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, - uint32_t id) -{ - struct drm_framebuffer *fb; - - mutex_lock(&dev->mode_config.fb_lock); - fb = __drm_framebuffer_lookup(dev, id); - if (fb) { - if (!kref_get_unless_zero(&fb->refcount)) - fb = NULL; - } - mutex_unlock(&dev->mode_config.fb_lock); - - return fb; -} -EXPORT_SYMBOL(drm_framebuffer_lookup); - -/** - * drm_framebuffer_unreference - unref a framebuffer - * @fb: framebuffer to unref - * - * This functions decrements the fb's refcount and frees it if it drops to zero. - */ -void drm_framebuffer_unreference(struct drm_framebuffer *fb) -{ - DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); - kref_put(&fb->refcount, drm_framebuffer_free); -} -EXPORT_SYMBOL(drm_framebuffer_unreference); - -/** - * drm_framebuffer_reference - incr the fb refcnt - * @fb: framebuffer - * - * This functions increments the fb's refcount. - */ -void drm_framebuffer_reference(struct drm_framebuffer *fb) -{ - DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); - kref_get(&fb->refcount); -} -EXPORT_SYMBOL(drm_framebuffer_reference); - -/** - * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr - * @fb: fb to unregister - * - * Drivers need to call this when cleaning up driver-private framebuffers, e.g. - * those used for fbdev. Note that the caller must hold a reference of it's own, - * i.e. the object may not be destroyed through this call (since it'll lead to a - * locking inversion). - */ -void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) -{ - struct drm_device *dev; - - if (!fb) - return; - - dev = fb->dev; - - mutex_lock(&dev->mode_config.fb_lock); - /* Mark fb as reaped and drop idr ref. */ - __drm_framebuffer_unregister(dev, fb); - mutex_unlock(&dev->mode_config.fb_lock); -} -EXPORT_SYMBOL(drm_framebuffer_unregister_private); - -/** - * drm_framebuffer_cleanup - remove a framebuffer object - * @fb: framebuffer to remove - * - * Cleanup framebuffer. This function is intended to be used from the drivers - * ->destroy callback. It can also be used to clean up driver private - * framebuffers embedded into a larger structure. - * - * Note that this function does not remove the fb from active usuage - if it is - * still used anywhere, hilarity can ensue since userspace could call getfb on - * the id and get back -EINVAL. Obviously no concern at driver unload time. - * - * Also, the framebuffer will not be removed from the lookup idr - for - * user-created framebuffers this will happen in in the rmfb ioctl. For - * driver-private objects (e.g. for fbdev) drivers need to explicitly call - * drm_framebuffer_unregister_private. - */ -void drm_framebuffer_cleanup(struct drm_framebuffer *fb) -{ - struct drm_device *dev = fb->dev; - - mutex_lock(&dev->mode_config.fb_lock); - list_del(&fb->head); - dev->mode_config.num_fb--; - mutex_unlock(&dev->mode_config.fb_lock); -} -EXPORT_SYMBOL(drm_framebuffer_cleanup); - -/** - * drm_framebuffer_remove - remove and unreference a framebuffer object - * @fb: framebuffer to remove - * - * Scans all the CRTCs and planes in @dev's mode_config. If they're - * using @fb, removes it, setting it to NULL. Then drops the reference to the - * passed-in framebuffer. Might take the modeset locks. - * - * Note that this function optimizes the cleanup away if the caller holds the - * last reference to the framebuffer. It is also guaranteed to not take the - * modeset locks in this case. - */ -void drm_framebuffer_remove(struct drm_framebuffer *fb) -{ - struct drm_device *dev; - struct drm_crtc *crtc; - struct drm_plane *plane; - struct drm_mode_set set; - int ret; - - if (!fb) - return; - - dev = fb->dev; - - WARN_ON(!list_empty(&fb->filp_head)); - - /* - * drm ABI mandates that we remove any deleted framebuffers from active - * useage. But since most sane clients only remove framebuffers they no - * longer need, try to optimize this away. - * - * Since we're holding a reference ourselves, observing a refcount of 1 - * means that we're the last holder and can skip it. Also, the refcount - * can never increase from 1 again, so we don't need any barriers or - * locks. - * - * Note that userspace could try to race with use and instate a new - * usage _after_ we've cleared all current ones. End result will be an - * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot - * in this manner. - */ - if (atomic_read(&fb->refcount.refcount) > 1) { - drm_modeset_lock_all(dev); - /* remove from any CRTC */ - drm_for_each_crtc(crtc, dev) { - if (crtc->primary->fb == fb) { - /* should turn off the crtc */ - memset(&set, 0, sizeof(struct drm_mode_set)); - set.crtc = crtc; - set.fb = NULL; - ret = drm_mode_set_config_internal(&set); - if (ret) - DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); - } - } - - drm_for_each_plane(plane, dev) { - if (plane->fb == fb) - drm_plane_force_disable(plane); - } - drm_modeset_unlock_all(dev); - } - - drm_framebuffer_unreference(fb); -} -EXPORT_SYMBOL(drm_framebuffer_remove); - -DEFINE_WW_CLASS(crtc_ww_class); - -/** - * drm_crtc_init_with_planes - Initialise a new CRTC object with - * specified primary and cursor planes. - * @dev: DRM device - * @crtc: CRTC object to init - * @primary: Primary plane for CRTC - * @cursor: Cursor plane for CRTC - * @funcs: callbacks for the new CRTC - * - * Inits a new object created as base part of a driver crtc object. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, - struct drm_plane *primary, - struct drm_plane *cursor, - const struct drm_crtc_funcs *funcs) -{ - struct drm_mode_config *config = &dev->mode_config; - int ret; - - WARN_ON(primary && primary->type != DRM_PLANE_TYPE_PRIMARY); - WARN_ON(cursor && cursor->type != DRM_PLANE_TYPE_CURSOR); - - crtc->dev = dev; - crtc->funcs = funcs; - - drm_modeset_lock_init(&crtc->mutex); - ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); - if (ret) - return ret; - - crtc->base.properties = &crtc->properties; - - list_add_tail(&crtc->head, &config->crtc_list); - config->num_crtc++; - - crtc->primary = primary; - crtc->cursor = cursor; - if (primary) - primary->possible_crtcs = 1 << drm_crtc_index(crtc); - if (cursor) - cursor->possible_crtcs = 1 << drm_crtc_index(crtc); - - if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { - drm_object_attach_property(&crtc->base, config->prop_active, 0); - drm_object_attach_property(&crtc->base, config->prop_mode_id, 0); - } - - return 0; -} -EXPORT_SYMBOL(drm_crtc_init_with_planes); - -/** - * drm_crtc_cleanup - Clean up the core crtc usage - * @crtc: CRTC to cleanup - * - * This function cleans up @crtc and removes it from the DRM mode setting - * core. Note that the function does *not* free the crtc structure itself, - * this is the responsibility of the caller. - */ -void drm_crtc_cleanup(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - - kfree(crtc->gamma_store); - crtc->gamma_store = NULL; - - drm_modeset_lock_fini(&crtc->mutex); - - drm_mode_object_put(dev, &crtc->base); - list_del(&crtc->head); - dev->mode_config.num_crtc--; - - WARN_ON(crtc->state && !crtc->funcs->atomic_destroy_state); - if (crtc->state && crtc->funcs->atomic_destroy_state) - crtc->funcs->atomic_destroy_state(crtc, crtc->state); - - memset(crtc, 0, sizeof(*crtc)); -} -EXPORT_SYMBOL(drm_crtc_cleanup); - -/** - * drm_crtc_index - find the index of a registered CRTC - * @crtc: CRTC to find index for - * - * Given a registered CRTC, return the index of that CRTC within a DRM - * device's list of CRTCs. - */ -unsigned int drm_crtc_index(struct drm_crtc *crtc) -{ - unsigned int index = 0; - struct drm_crtc *tmp; - - drm_for_each_crtc(tmp, crtc->dev) { - if (tmp == crtc) - return index; - - index++; - } - - BUG(); -} -EXPORT_SYMBOL(drm_crtc_index); - -/* - * drm_mode_remove - remove and free a mode - * @connector: connector list to modify - * @mode: mode to remove - * - * Remove @mode from @connector's mode list, then free it. - */ -static void drm_mode_remove(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - list_del(&mode->head); - drm_mode_destroy(connector->dev, mode); -} - -/** - * drm_display_info_set_bus_formats - set the supported bus formats - * @info: display info to store bus formats in - * @formats: array containing the supported bus formats - * @num_formats: the number of entries in the fmts array - * - * Store the supported bus formats in display info structure. - * See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for - * a full list of available formats. - */ -int drm_display_info_set_bus_formats(struct drm_display_info *info, - const u32 *formats, - unsigned int num_formats) -{ - u32 *fmts = NULL; - - if (!formats && num_formats) - return -EINVAL; - - if (formats && num_formats) { - fmts = kmemdup(formats, sizeof(*formats) * num_formats, - GFP_KERNEL); - if (!fmts) - return -ENOMEM; - } - - kfree(info->bus_formats); - info->bus_formats = fmts; - info->num_bus_formats = num_formats; - - return 0; -} -EXPORT_SYMBOL(drm_display_info_set_bus_formats); - -/** - * drm_connector_get_cmdline_mode - reads the user's cmdline mode - * @connector: connector to quwery - * - * The kernel supports per-connector configration of its consoles through - * use of the video= parameter. This function parses that option and - * extracts the user's specified mode (or enable/disable status) for a - * particular connector. This is typically only used during the early fbdev - * setup. - */ -static void drm_connector_get_cmdline_mode(struct drm_connector *connector) -{ - struct drm_cmdline_mode *mode = &connector->cmdline_mode; - char *option = NULL; - - if (fb_get_options(connector->name, &option)) - return; - - if (!drm_mode_parse_command_line_for_connector(option, - connector, - mode)) - return; - - if (mode->force) { - const char *s; - - switch (mode->force) { - case DRM_FORCE_OFF: - s = "OFF"; - break; - case DRM_FORCE_ON_DIGITAL: - s = "ON - dig"; - break; - default: - case DRM_FORCE_ON: - s = "ON"; - break; - } - - DRM_INFO("forcing %s connector %s\n", connector->name, s); - connector->force = mode->force; - } - - DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", - connector->name, - mode->xres, mode->yres, - mode->refresh_specified ? mode->refresh : 60, - mode->rb ? " reduced blanking" : "", - mode->margins ? " with margins" : "", - mode->interlace ? " interlaced" : ""); -} - -/** - * drm_connector_init - Init a preallocated connector - * @dev: DRM device - * @connector: the connector to init - * @funcs: callbacks for this connector - * @connector_type: user visible type of the connector - * - * Initialises a preallocated connector. Connectors should be - * subclassed as part of driver connector objects. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_connector_init(struct drm_device *dev, - struct drm_connector *connector, - const struct drm_connector_funcs *funcs, - int connector_type) -{ - struct drm_mode_config *config = &dev->mode_config; - int ret; - struct ida *connector_ida = - &drm_connector_enum_list[connector_type].ida; - - drm_modeset_lock_all(dev); - - ret = drm_mode_object_get_reg(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR, false); - if (ret) - goto out_unlock; - - connector->base.properties = &connector->properties; - connector->dev = dev; - connector->funcs = funcs; - connector->connector_type = connector_type; - connector->connector_type_id = - ida_simple_get(connector_ida, 1, 0, GFP_KERNEL); - if (connector->connector_type_id < 0) { - ret = connector->connector_type_id; - goto out_put; - } - connector->name = - kasprintf(GFP_KERNEL, "%s-%d", - drm_connector_enum_list[connector_type].name, - connector->connector_type_id); - if (!connector->name) { - ret = -ENOMEM; - goto out_put; - } - - INIT_LIST_HEAD(&connector->probed_modes); - INIT_LIST_HEAD(&connector->modes); - connector->edid_blob_ptr = NULL; - connector->status = connector_status_unknown; - - drm_connector_get_cmdline_mode(connector); - - /* We should add connectors at the end to avoid upsetting the connector - * index too much. */ - list_add_tail(&connector->head, &config->connector_list); - config->num_connector++; - - if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) - drm_object_attach_property(&connector->base, - config->edid_property, - 0); - - drm_object_attach_property(&connector->base, - config->dpms_property, 0); - - if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { - drm_object_attach_property(&connector->base, config->prop_crtc_id, 0); - } - - connector->debugfs_entry = NULL; - -out_put: - if (ret) - drm_mode_object_put(dev, &connector->base); - -out_unlock: - drm_modeset_unlock_all(dev); - - return ret; -} -EXPORT_SYMBOL(drm_connector_init); - -/** - * drm_connector_cleanup - cleans up an initialised connector - * @connector: connector to cleanup - * - * Cleans up the connector but doesn't free the object. - */ -void drm_connector_cleanup(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct drm_display_mode *mode, *t; - - if (connector->tile_group) { - drm_mode_put_tile_group(dev, connector->tile_group); - connector->tile_group = NULL; - } - - list_for_each_entry_safe(mode, t, &connector->probed_modes, head) - drm_mode_remove(connector, mode); - - list_for_each_entry_safe(mode, t, &connector->modes, head) - drm_mode_remove(connector, mode); - - ida_remove(&drm_connector_enum_list[connector->connector_type].ida, - connector->connector_type_id); - - kfree(connector->display_info.bus_formats); - drm_mode_object_put(dev, &connector->base); - kfree(connector->name); - connector->name = NULL; - list_del(&connector->head); - dev->mode_config.num_connector--; - - WARN_ON(connector->state && !connector->funcs->atomic_destroy_state); - if (connector->state && connector->funcs->atomic_destroy_state) - connector->funcs->atomic_destroy_state(connector, - connector->state); - - memset(connector, 0, sizeof(*connector)); -} -EXPORT_SYMBOL(drm_connector_cleanup); - -/** - * drm_connector_index - find the index of a registered connector - * @connector: connector to find index for - * - * Given a registered connector, return the index of that connector within a DRM - * device's list of connectors. - */ -unsigned int drm_connector_index(struct drm_connector *connector) -{ - unsigned int index = 0; - struct drm_connector *tmp; - struct drm_mode_config *config = &connector->dev->mode_config; - - WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); - - drm_for_each_connector(tmp, connector->dev) { - if (tmp == connector) - return index; - - index++; - } - - BUG(); -} -EXPORT_SYMBOL(drm_connector_index); - -/** - * drm_connector_register - register a connector - * @connector: the connector to register - * - * Register userspace interfaces for a connector - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_connector_register(struct drm_connector *connector) -{ - int ret; - - drm_mode_object_register(connector->dev, &connector->base); - - ret = drm_sysfs_connector_add(connector); - if (ret) - return ret; - - ret = drm_debugfs_connector_add(connector); - if (ret) { - drm_sysfs_connector_remove(connector); - return ret; - } - - return 0; -} -EXPORT_SYMBOL(drm_connector_register); - -/** - * drm_connector_unregister - unregister a connector - * @connector: the connector to unregister - * - * Unregister userspace interfaces for a connector - */ -void drm_connector_unregister(struct drm_connector *connector) -{ - drm_sysfs_connector_remove(connector); - drm_debugfs_connector_remove(connector); -} -EXPORT_SYMBOL(drm_connector_unregister); - - -/** - * drm_connector_unplug_all - unregister connector userspace interfaces - * @dev: drm device - * - * This function unregisters all connector userspace interfaces in sysfs. Should - * be call when the device is disconnected, e.g. from an usb driver's - * ->disconnect callback. - */ -void drm_connector_unplug_all(struct drm_device *dev) -{ - struct drm_connector *connector; - - /* FIXME: taking the mode config mutex ends up in a clash with sysfs */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) - drm_connector_unregister(connector); - -} -EXPORT_SYMBOL(drm_connector_unplug_all); - -/** - * drm_encoder_init - Init a preallocated encoder - * @dev: drm device - * @encoder: the encoder to init - * @funcs: callbacks for this encoder - * @encoder_type: user visible type of the encoder - * - * Initialises a preallocated encoder. Encoder should be - * subclassed as part of driver encoder objects. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_encoder_init(struct drm_device *dev, - struct drm_encoder *encoder, - const struct drm_encoder_funcs *funcs, - int encoder_type) -{ - int ret; - - drm_modeset_lock_all(dev); - - ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); - if (ret) - goto out_unlock; - - encoder->dev = dev; - encoder->encoder_type = encoder_type; - encoder->funcs = funcs; - encoder->name = kasprintf(GFP_KERNEL, "%s-%d", - drm_encoder_enum_list[encoder_type].name, - encoder->base.id); - if (!encoder->name) { - ret = -ENOMEM; - goto out_put; - } - - list_add_tail(&encoder->head, &dev->mode_config.encoder_list); - dev->mode_config.num_encoder++; - -out_put: - if (ret) - drm_mode_object_put(dev, &encoder->base); - -out_unlock: - drm_modeset_unlock_all(dev); - - return ret; -} -EXPORT_SYMBOL(drm_encoder_init); - -/** - * drm_encoder_cleanup - cleans up an initialised encoder - * @encoder: encoder to cleanup - * - * Cleans up the encoder but doesn't free the object. - */ -void drm_encoder_cleanup(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - - drm_modeset_lock_all(dev); - drm_mode_object_put(dev, &encoder->base); - kfree(encoder->name); - list_del(&encoder->head); - dev->mode_config.num_encoder--; - drm_modeset_unlock_all(dev); - - memset(encoder, 0, sizeof(*encoder)); -} -EXPORT_SYMBOL(drm_encoder_cleanup); - -/** - * drm_universal_plane_init - Initialize a new universal plane object - * @dev: DRM device - * @plane: plane object to init - * @possible_crtcs: bitmask of possible CRTCs - * @funcs: callbacks for the new plane - * @formats: array of supported formats (%DRM_FORMAT_*) - * @format_count: number of elements in @formats - * @type: type of plane (overlay, primary, cursor) - * - * Initializes a plane object of type @type. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, - unsigned long possible_crtcs, - const struct drm_plane_funcs *funcs, - const uint32_t *formats, unsigned int format_count, - enum drm_plane_type type) -{ - struct drm_mode_config *config = &dev->mode_config; - int ret; - - ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); - if (ret) - return ret; - - drm_modeset_lock_init(&plane->mutex); - - plane->base.properties = &plane->properties; - plane->dev = dev; - plane->funcs = funcs; - plane->format_types = kmalloc_array(format_count, sizeof(uint32_t), - GFP_KERNEL); - if (!plane->format_types) { - DRM_DEBUG_KMS("out of memory when allocating plane\n"); - drm_mode_object_put(dev, &plane->base); - return -ENOMEM; - } - - memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); - plane->format_count = format_count; - plane->possible_crtcs = possible_crtcs; - plane->type = type; - - list_add_tail(&plane->head, &config->plane_list); - config->num_total_plane++; - if (plane->type == DRM_PLANE_TYPE_OVERLAY) - config->num_overlay_plane++; - - drm_object_attach_property(&plane->base, - config->plane_type_property, - plane->type); - - if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { - drm_object_attach_property(&plane->base, config->prop_fb_id, 0); - drm_object_attach_property(&plane->base, config->prop_crtc_id, 0); - drm_object_attach_property(&plane->base, config->prop_crtc_x, 0); - drm_object_attach_property(&plane->base, config->prop_crtc_y, 0); - drm_object_attach_property(&plane->base, config->prop_crtc_w, 0); - drm_object_attach_property(&plane->base, config->prop_crtc_h, 0); - drm_object_attach_property(&plane->base, config->prop_src_x, 0); - drm_object_attach_property(&plane->base, config->prop_src_y, 0); - drm_object_attach_property(&plane->base, config->prop_src_w, 0); - drm_object_attach_property(&plane->base, config->prop_src_h, 0); - } - - return 0; -} -EXPORT_SYMBOL(drm_universal_plane_init); - -/** - * drm_plane_init - Initialize a legacy plane - * @dev: DRM device - * @plane: plane object to init - * @possible_crtcs: bitmask of possible CRTCs - * @funcs: callbacks for the new plane - * @formats: array of supported formats (%DRM_FORMAT_*) - * @format_count: number of elements in @formats - * @is_primary: plane type (primary vs overlay) - * - * Legacy API to initialize a DRM plane. - * - * New drivers should call drm_universal_plane_init() instead. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, - unsigned long possible_crtcs, - const struct drm_plane_funcs *funcs, - const uint32_t *formats, unsigned int format_count, - bool is_primary) -{ - enum drm_plane_type type; - - type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; - return drm_universal_plane_init(dev, plane, possible_crtcs, funcs, - formats, format_count, type); -} -EXPORT_SYMBOL(drm_plane_init); - -/** - * drm_plane_cleanup - Clean up the core plane usage - * @plane: plane to cleanup - * - * This function cleans up @plane and removes it from the DRM mode setting - * core. Note that the function does *not* free the plane structure itself, - * this is the responsibility of the caller. - */ -void drm_plane_cleanup(struct drm_plane *plane) -{ - struct drm_device *dev = plane->dev; - - drm_modeset_lock_all(dev); - kfree(plane->format_types); - drm_mode_object_put(dev, &plane->base); - - BUG_ON(list_empty(&plane->head)); - - list_del(&plane->head); - dev->mode_config.num_total_plane--; - if (plane->type == DRM_PLANE_TYPE_OVERLAY) - dev->mode_config.num_overlay_plane--; - drm_modeset_unlock_all(dev); - - WARN_ON(plane->state && !plane->funcs->atomic_destroy_state); - if (plane->state && plane->funcs->atomic_destroy_state) - plane->funcs->atomic_destroy_state(plane, plane->state); - - memset(plane, 0, sizeof(*plane)); -} -EXPORT_SYMBOL(drm_plane_cleanup); - -/** - * drm_plane_index - find the index of a registered plane - * @plane: plane to find index for - * - * Given a registered plane, return the index of that CRTC within a DRM - * device's list of planes. - */ -unsigned int drm_plane_index(struct drm_plane *plane) -{ - unsigned int index = 0; - struct drm_plane *tmp; - - drm_for_each_plane(tmp, plane->dev) { - if (tmp == plane) - return index; - - index++; - } - - BUG(); -} -EXPORT_SYMBOL(drm_plane_index); - -/** - * drm_plane_from_index - find the registered plane at an index - * @dev: DRM device - * @idx: index of registered plane to find for - * - * Given a plane index, return the registered plane from DRM device's - * list of planes with matching index. - */ -struct drm_plane * -drm_plane_from_index(struct drm_device *dev, int idx) -{ - struct drm_plane *plane; - unsigned int i = 0; - - drm_for_each_plane(plane, dev) { - if (i == idx) - return plane; - i++; - } - return NULL; -} -EXPORT_SYMBOL(drm_plane_from_index); - -/** - * drm_plane_force_disable - Forcibly disable a plane - * @plane: plane to disable - * - * Forces the plane to be disabled. - * - * Used when the plane's current framebuffer is destroyed, - * and when restoring fbdev mode. - */ -void drm_plane_force_disable(struct drm_plane *plane) -{ - int ret; - - if (!plane->fb) - return; - - plane->old_fb = plane->fb; - ret = plane->funcs->disable_plane(plane); - if (ret) { - DRM_ERROR("failed to disable plane with busy fb\n"); - plane->old_fb = NULL; - return; - } - /* disconnect the plane from the fb and crtc: */ - drm_framebuffer_unreference(plane->old_fb); - plane->old_fb = NULL; - plane->fb = NULL; - plane->crtc = NULL; -} -EXPORT_SYMBOL(drm_plane_force_disable); - -static int drm_mode_create_standard_properties(struct drm_device *dev) -{ - struct drm_property *prop; - - /* - * Standard properties (apply to all connectors) - */ - prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | - DRM_MODE_PROP_IMMUTABLE, - "EDID", 0); - if (!prop) - return -ENOMEM; - dev->mode_config.edid_property = prop; - - prop = drm_property_create_enum(dev, 0, - "DPMS", drm_dpms_enum_list, - ARRAY_SIZE(drm_dpms_enum_list)); - if (!prop) - return -ENOMEM; - dev->mode_config.dpms_property = prop; - - prop = drm_property_create(dev, - DRM_MODE_PROP_BLOB | - DRM_MODE_PROP_IMMUTABLE, - "PATH", 0); - if (!prop) - return -ENOMEM; - dev->mode_config.path_property = prop; - - prop = drm_property_create(dev, - DRM_MODE_PROP_BLOB | - DRM_MODE_PROP_IMMUTABLE, - "TILE", 0); - if (!prop) - return -ENOMEM; - dev->mode_config.tile_property = prop; - - prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, - "type", drm_plane_type_enum_list, - ARRAY_SIZE(drm_plane_type_enum_list)); - if (!prop) - return -ENOMEM; - dev->mode_config.plane_type_property = prop; - - prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, - "SRC_X", 0, UINT_MAX); - if (!prop) - return -ENOMEM; - dev->mode_config.prop_src_x = prop; - - prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, - "SRC_Y", 0, UINT_MAX); - if (!prop) - return -ENOMEM; - dev->mode_config.prop_src_y = prop; - - prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, - "SRC_W", 0, UINT_MAX); - if (!prop) - return -ENOMEM; - dev->mode_config.prop_src_w = prop; - - prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, - "SRC_H", 0, UINT_MAX); - if (!prop) - return -ENOMEM; - dev->mode_config.prop_src_h = prop; - - prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC, - "CRTC_X", INT_MIN, INT_MAX); - if (!prop) - return -ENOMEM; - dev->mode_config.prop_crtc_x = prop; - - prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC, - "CRTC_Y", INT_MIN, INT_MAX); - if (!prop) - return -ENOMEM; - dev->mode_config.prop_crtc_y = prop; - - prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, - "CRTC_W", 0, INT_MAX); - if (!prop) - return -ENOMEM; - dev->mode_config.prop_crtc_w = prop; - - prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, - "CRTC_H", 0, INT_MAX); - if (!prop) - return -ENOMEM; - dev->mode_config.prop_crtc_h = prop; - - prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, - "FB_ID", DRM_MODE_OBJECT_FB); - if (!prop) - return -ENOMEM; - dev->mode_config.prop_fb_id = prop; - - prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, - "CRTC_ID", DRM_MODE_OBJECT_CRTC); - if (!prop) - return -ENOMEM; - dev->mode_config.prop_crtc_id = prop; - - prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC, - "ACTIVE"); - if (!prop) - return -ENOMEM; - dev->mode_config.prop_active = prop; - - prop = drm_property_create(dev, - DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB, - "MODE_ID", 0); - if (!prop) - return -ENOMEM; - dev->mode_config.prop_mode_id = prop; - - return 0; -} - -/** - * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties - * @dev: DRM device - * - * Called by a driver the first time a DVI-I connector is made. - */ -int drm_mode_create_dvi_i_properties(struct drm_device *dev) -{ - struct drm_property *dvi_i_selector; - struct drm_property *dvi_i_subconnector; - - if (dev->mode_config.dvi_i_select_subconnector_property) - return 0; - - dvi_i_selector = - drm_property_create_enum(dev, 0, - "select subconnector", - drm_dvi_i_select_enum_list, - ARRAY_SIZE(drm_dvi_i_select_enum_list)); - dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; - - dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, - "subconnector", - drm_dvi_i_subconnector_enum_list, - ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); - dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; - - return 0; -} -EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); - -/** - * drm_create_tv_properties - create TV specific connector properties - * @dev: DRM device - * @num_modes: number of different TV formats (modes) supported - * @modes: array of pointers to strings containing name of each format - * - * Called by a driver's TV initialization routine, this function creates - * the TV specific connector properties for a given device. Caller is - * responsible for allocating a list of format names and passing them to - * this routine. - */ -int drm_mode_create_tv_properties(struct drm_device *dev, - unsigned int num_modes, - const char * const modes[]) -{ - struct drm_property *tv_selector; - struct drm_property *tv_subconnector; - unsigned int i; - - if (dev->mode_config.tv_select_subconnector_property) - return 0; - - /* - * Basic connector properties - */ - tv_selector = drm_property_create_enum(dev, 0, - "select subconnector", - drm_tv_select_enum_list, - ARRAY_SIZE(drm_tv_select_enum_list)); - if (!tv_selector) - goto nomem; - - dev->mode_config.tv_select_subconnector_property = tv_selector; - - tv_subconnector = - drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, - "subconnector", - drm_tv_subconnector_enum_list, - ARRAY_SIZE(drm_tv_subconnector_enum_list)); - if (!tv_subconnector) - goto nomem; - dev->mode_config.tv_subconnector_property = tv_subconnector; - - /* - * Other, TV specific properties: margins & TV modes. - */ - dev->mode_config.tv_left_margin_property = - drm_property_create_range(dev, 0, "left margin", 0, 100); - if (!dev->mode_config.tv_left_margin_property) - goto nomem; - - dev->mode_config.tv_right_margin_property = - drm_property_create_range(dev, 0, "right margin", 0, 100); - if (!dev->mode_config.tv_right_margin_property) - goto nomem; - - dev->mode_config.tv_top_margin_property = - drm_property_create_range(dev, 0, "top margin", 0, 100); - if (!dev->mode_config.tv_top_margin_property) - goto nomem; - - dev->mode_config.tv_bottom_margin_property = - drm_property_create_range(dev, 0, "bottom margin", 0, 100); - if (!dev->mode_config.tv_bottom_margin_property) - goto nomem; - - dev->mode_config.tv_mode_property = - drm_property_create(dev, DRM_MODE_PROP_ENUM, - "mode", num_modes); - if (!dev->mode_config.tv_mode_property) - goto nomem; - - for (i = 0; i < num_modes; i++) - drm_property_add_enum(dev->mode_config.tv_mode_property, i, - i, modes[i]); - - dev->mode_config.tv_brightness_property = - drm_property_create_range(dev, 0, "brightness", 0, 100); - if (!dev->mode_config.tv_brightness_property) - goto nomem; - - dev->mode_config.tv_contrast_property = - drm_property_create_range(dev, 0, "contrast", 0, 100); - if (!dev->mode_config.tv_contrast_property) - goto nomem; - - dev->mode_config.tv_flicker_reduction_property = - drm_property_create_range(dev, 0, "flicker reduction", 0, 100); - if (!dev->mode_config.tv_flicker_reduction_property) - goto nomem; - - dev->mode_config.tv_overscan_property = - drm_property_create_range(dev, 0, "overscan", 0, 100); - if (!dev->mode_config.tv_overscan_property) - goto nomem; - - dev->mode_config.tv_saturation_property = - drm_property_create_range(dev, 0, "saturation", 0, 100); - if (!dev->mode_config.tv_saturation_property) - goto nomem; - - dev->mode_config.tv_hue_property = - drm_property_create_range(dev, 0, "hue", 0, 100); - if (!dev->mode_config.tv_hue_property) - goto nomem; - - return 0; -nomem: - return -ENOMEM; -} -EXPORT_SYMBOL(drm_mode_create_tv_properties); - -/** - * drm_mode_create_scaling_mode_property - create scaling mode property - * @dev: DRM device - * - * Called by a driver the first time it's needed, must be attached to desired - * connectors. - */ -int drm_mode_create_scaling_mode_property(struct drm_device *dev) -{ - struct drm_property *scaling_mode; - - if (dev->mode_config.scaling_mode_property) - return 0; - - scaling_mode = - drm_property_create_enum(dev, 0, "scaling mode", - drm_scaling_mode_enum_list, - ARRAY_SIZE(drm_scaling_mode_enum_list)); - - dev->mode_config.scaling_mode_property = scaling_mode; - - return 0; -} -EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); - -/** - * drm_mode_create_aspect_ratio_property - create aspect ratio property - * @dev: DRM device - * - * Called by a driver the first time it's needed, must be attached to desired - * connectors. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_create_aspect_ratio_property(struct drm_device *dev) -{ - if (dev->mode_config.aspect_ratio_property) - return 0; - - dev->mode_config.aspect_ratio_property = - drm_property_create_enum(dev, 0, "aspect ratio", - drm_aspect_ratio_enum_list, - ARRAY_SIZE(drm_aspect_ratio_enum_list)); - - if (dev->mode_config.aspect_ratio_property == NULL) - return -ENOMEM; - - return 0; -} -EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); - -/** - * drm_mode_create_dirty_property - create dirty property - * @dev: DRM device - * - * Called by a driver the first time it's needed, must be attached to desired - * connectors. - */ -int drm_mode_create_dirty_info_property(struct drm_device *dev) -{ - struct drm_property *dirty_info; - - if (dev->mode_config.dirty_info_property) - return 0; - - dirty_info = - drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, - "dirty", - drm_dirty_info_enum_list, - ARRAY_SIZE(drm_dirty_info_enum_list)); - dev->mode_config.dirty_info_property = dirty_info; - - return 0; -} -EXPORT_SYMBOL(drm_mode_create_dirty_info_property); - -/** - * drm_mode_create_suggested_offset_properties - create suggests offset properties - * @dev: DRM device - * - * Create the the suggested x/y offset property for connectors. - */ -int drm_mode_create_suggested_offset_properties(struct drm_device *dev) -{ - if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property) - return 0; - - dev->mode_config.suggested_x_property = - drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff); - - dev->mode_config.suggested_y_property = - drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff); - - if (dev->mode_config.suggested_x_property == NULL || - dev->mode_config.suggested_y_property == NULL) - return -ENOMEM; - return 0; -} -EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties); - -/** - * drm_mode_getresources - get graphics configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Construct a set of configuration description structures and return - * them to the user, including CRTC, connector and framebuffer configuration. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getresources(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_card_res *card_res = data; - struct list_head *lh; - struct drm_framebuffer *fb; - struct drm_connector *connector; - struct drm_crtc *crtc; - struct drm_encoder *encoder; - int ret = 0; - int connector_count = 0; - int crtc_count = 0; - int fb_count = 0; - int encoder_count = 0; - int copied = 0; - uint32_t __user *fb_id; - uint32_t __user *crtc_id; - uint32_t __user *connector_id; - uint32_t __user *encoder_id; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - - mutex_lock(&file_priv->fbs_lock); - /* - * For the non-control nodes we need to limit the list of resources - * by IDs in the group list for this node - */ - list_for_each(lh, &file_priv->fbs) - fb_count++; - - /* handle this in 4 parts */ - /* FBs */ - if (card_res->count_fbs >= fb_count) { - copied = 0; - fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; - list_for_each_entry(fb, &file_priv->fbs, filp_head) { - if (put_user(fb->base.id, fb_id + copied)) { - mutex_unlock(&file_priv->fbs_lock); - return -EFAULT; - } - copied++; - } - } - card_res->count_fbs = fb_count; - mutex_unlock(&file_priv->fbs_lock); - - /* mode_config.mutex protects the connector list against e.g. DP MST - * connector hot-adding. CRTC/Plane lists are invariant. */ - mutex_lock(&dev->mode_config.mutex); - drm_for_each_crtc(crtc, dev) - crtc_count++; - - drm_for_each_connector(connector, dev) - connector_count++; - - drm_for_each_encoder(encoder, dev) - encoder_count++; - - card_res->max_height = dev->mode_config.max_height; - card_res->min_height = dev->mode_config.min_height; - card_res->max_width = dev->mode_config.max_width; - card_res->min_width = dev->mode_config.min_width; - - /* CRTCs */ - if (card_res->count_crtcs >= crtc_count) { - copied = 0; - crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr; - drm_for_each_crtc(crtc, dev) { - DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); - if (put_user(crtc->base.id, crtc_id + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - card_res->count_crtcs = crtc_count; - - /* Encoders */ - if (card_res->count_encoders >= encoder_count) { - copied = 0; - encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr; - drm_for_each_encoder(encoder, dev) { - DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id, - encoder->name); - if (put_user(encoder->base.id, encoder_id + - copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - card_res->count_encoders = encoder_count; - - /* Connectors */ - if (card_res->count_connectors >= connector_count) { - copied = 0; - connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr; - drm_for_each_connector(connector, dev) { - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); - if (put_user(connector->base.id, - connector_id + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - card_res->count_connectors = connector_count; - - DRM_DEBUG_KMS("CRTC[%d] CONNECTORS[%d] ENCODERS[%d]\n", card_res->count_crtcs, - card_res->count_connectors, card_res->count_encoders); - -out: - mutex_unlock(&dev->mode_config.mutex); - return ret; -} - -/** - * drm_mode_getcrtc - get CRTC configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Construct a CRTC configuration structure to return to the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getcrtc(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_crtc *crtc_resp = data; - struct drm_crtc *crtc; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - crtc = drm_crtc_find(dev, crtc_resp->crtc_id); - if (!crtc) - return -ENOENT; - - drm_modeset_lock_crtc(crtc, crtc->primary); - crtc_resp->gamma_size = crtc->gamma_size; - if (crtc->primary->fb) - crtc_resp->fb_id = crtc->primary->fb->base.id; - else - crtc_resp->fb_id = 0; - - if (crtc->state) { - crtc_resp->x = crtc->primary->state->src_x >> 16; - crtc_resp->y = crtc->primary->state->src_y >> 16; - if (crtc->state->enable) { - drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode); - crtc_resp->mode_valid = 1; - - } else { - crtc_resp->mode_valid = 0; - } - } else { - crtc_resp->x = crtc->x; - crtc_resp->y = crtc->y; - if (crtc->enabled) { - drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode); - crtc_resp->mode_valid = 1; - - } else { - crtc_resp->mode_valid = 0; - } - } - drm_modeset_unlock_crtc(crtc); - - return 0; -} - -static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, - const struct drm_file *file_priv) -{ - /* - * If user-space hasn't configured the driver to expose the stereo 3D - * modes, don't expose them. - */ - if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode)) - return false; - - return true; -} - -static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector) -{ - /* For atomic drivers only state objects are synchronously updated and - * protected by modeset locks, so check those first. */ - if (connector->state) - return connector->state->best_encoder; - return connector->encoder; -} - -/* helper for getconnector and getproperties ioctls */ -static int get_properties(struct drm_mode_object *obj, bool atomic, - uint32_t __user *prop_ptr, uint64_t __user *prop_values, - uint32_t *arg_count_props) -{ - int props_count; - int i, ret, copied; - - props_count = obj->properties->count; - if (!atomic) - props_count -= obj->properties->atomic_count; - - if ((*arg_count_props >= props_count) && props_count) { - for (i = 0, copied = 0; copied < props_count; i++) { - struct drm_property *prop = obj->properties->properties[i]; - uint64_t val; - - if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic) - continue; - - ret = drm_object_property_get_value(obj, prop, &val); - if (ret) - return ret; - - if (put_user(prop->base.id, prop_ptr + copied)) - return -EFAULT; - - if (put_user(val, prop_values + copied)) - return -EFAULT; - - copied++; - } - } - *arg_count_props = props_count; - - return 0; -} - -/** - * drm_mode_getconnector - get connector configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Construct a connector configuration structure to return to the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getconnector(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_get_connector *out_resp = data; - struct drm_connector *connector; - struct drm_encoder *encoder; - struct drm_display_mode *mode; - int mode_count = 0; - int encoders_count = 0; - int ret = 0; - int copied = 0; - int i; - struct drm_mode_modeinfo u_mode; - struct drm_mode_modeinfo __user *mode_ptr; - uint32_t __user *encoder_ptr; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); - - DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); - - mutex_lock(&dev->mode_config.mutex); - - connector = drm_connector_find(dev, out_resp->connector_id); - if (!connector) { - ret = -ENOENT; - goto out_unlock; - } - - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) - if (connector->encoder_ids[i] != 0) - encoders_count++; - - if (out_resp->count_modes == 0) { - connector->funcs->fill_modes(connector, - dev->mode_config.max_width, - dev->mode_config.max_height); - } - - /* delayed so we get modes regardless of pre-fill_modes state */ - list_for_each_entry(mode, &connector->modes, head) - if (drm_mode_expose_to_userspace(mode, file_priv)) - mode_count++; - - out_resp->connector_id = connector->base.id; - out_resp->connector_type = connector->connector_type; - out_resp->connector_type_id = connector->connector_type_id; - out_resp->mm_width = connector->display_info.width_mm; - out_resp->mm_height = connector->display_info.height_mm; - out_resp->subpixel = connector->display_info.subpixel_order; - out_resp->connection = connector->status; - - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - encoder = drm_connector_get_encoder(connector); - if (encoder) - out_resp->encoder_id = encoder->base.id; - else - out_resp->encoder_id = 0; - - /* - * This ioctl is called twice, once to determine how much space is - * needed, and the 2nd time to fill it. - */ - if ((out_resp->count_modes >= mode_count) && mode_count) { - copied = 0; - mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr; - list_for_each_entry(mode, &connector->modes, head) { - if (!drm_mode_expose_to_userspace(mode, file_priv)) - continue; - - drm_mode_convert_to_umode(&u_mode, mode); - if (copy_to_user(mode_ptr + copied, - &u_mode, sizeof(u_mode))) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - out_resp->count_modes = mode_count; - - ret = get_properties(&connector->base, file_priv->atomic, - (uint32_t __user *)(unsigned long)(out_resp->props_ptr), - (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr), - &out_resp->count_props); - if (ret) - goto out; - - if ((out_resp->count_encoders >= encoders_count) && encoders_count) { - copied = 0; - encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] != 0) { - if (put_user(connector->encoder_ids[i], - encoder_ptr + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - } - out_resp->count_encoders = encoders_count; - -out: - drm_modeset_unlock(&dev->mode_config.connection_mutex); - -out_unlock: - mutex_unlock(&dev->mode_config.mutex); - - return ret; -} - -static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) -{ - struct drm_connector *connector; - struct drm_device *dev = encoder->dev; - bool uses_atomic = false; - - /* For atomic drivers only state objects are synchronously updated and - * protected by modeset locks, so check those first. */ - drm_for_each_connector(connector, dev) { - if (!connector->state) - continue; - - uses_atomic = true; - - if (connector->state->best_encoder != encoder) - continue; - - return connector->state->crtc; - } - - /* Don't return stale data (e.g. pending async disable). */ - if (uses_atomic) - return NULL; - - return encoder->crtc; -} - -/** - * drm_mode_getencoder - get encoder configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Construct a encoder configuration structure to return to the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getencoder(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_get_encoder *enc_resp = data; - struct drm_encoder *encoder; - struct drm_crtc *crtc; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - encoder = drm_encoder_find(dev, enc_resp->encoder_id); - if (!encoder) - return -ENOENT; - - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - crtc = drm_encoder_get_crtc(encoder); - if (crtc) - enc_resp->crtc_id = crtc->base.id; - else - enc_resp->crtc_id = 0; - drm_modeset_unlock(&dev->mode_config.connection_mutex); - - enc_resp->encoder_type = encoder->encoder_type; - enc_resp->encoder_id = encoder->base.id; - enc_resp->possible_crtcs = encoder->possible_crtcs; - enc_resp->possible_clones = encoder->possible_clones; - - return 0; -} - -/** - * drm_mode_getplane_res - enumerate all plane resources - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Construct a list of plane ids to return to the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getplane_res(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_get_plane_res *plane_resp = data; - struct drm_mode_config *config; - struct drm_plane *plane; - uint32_t __user *plane_ptr; - int copied = 0; - unsigned num_planes; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - config = &dev->mode_config; - - if (file_priv->universal_planes) - num_planes = config->num_total_plane; - else - num_planes = config->num_overlay_plane; - - /* - * This ioctl is called twice, once to determine how much space is - * needed, and the 2nd time to fill it. - */ - if (num_planes && - (plane_resp->count_planes >= num_planes)) { - plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; - - /* Plane lists are invariant, no locking needed. */ - drm_for_each_plane(plane, dev) { - /* - * Unless userspace set the 'universal planes' - * capability bit, only advertise overlays. - */ - if (plane->type != DRM_PLANE_TYPE_OVERLAY && - !file_priv->universal_planes) - continue; - - if (put_user(plane->base.id, plane_ptr + copied)) - return -EFAULT; - copied++; - } - } - plane_resp->count_planes = num_planes; - - return 0; -} - -/** - * drm_mode_getplane - get plane configuration - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Construct a plane configuration structure to return to the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getplane(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_get_plane *plane_resp = data; - struct drm_plane *plane; - uint32_t __user *format_ptr; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - plane = drm_plane_find(dev, plane_resp->plane_id); - if (!plane) - return -ENOENT; - - drm_modeset_lock(&plane->mutex, NULL); - if (plane->crtc) - plane_resp->crtc_id = plane->crtc->base.id; - else - plane_resp->crtc_id = 0; - - if (plane->fb) - plane_resp->fb_id = plane->fb->base.id; - else - plane_resp->fb_id = 0; - drm_modeset_unlock(&plane->mutex); - - plane_resp->plane_id = plane->base.id; - plane_resp->possible_crtcs = plane->possible_crtcs; - plane_resp->gamma_size = 0; - - /* - * This ioctl is called twice, once to determine how much space is - * needed, and the 2nd time to fill it. - */ - if (plane->format_count && - (plane_resp->count_format_types >= plane->format_count)) { - format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr; - if (copy_to_user(format_ptr, - plane->format_types, - sizeof(uint32_t) * plane->format_count)) { - return -EFAULT; - } - } - plane_resp->count_format_types = plane->format_count; - - return 0; -} - -/** - * drm_plane_check_pixel_format - Check if the plane supports the pixel format - * @plane: plane to check for format support - * @format: the pixel format - * - * Returns: - * Zero of @plane has @format in its list of supported pixel formats, -EINVAL - * otherwise. - */ -int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format) -{ - unsigned int i; - - for (i = 0; i < plane->format_count; i++) { - if (format == plane->format_types[i]) - return 0; - } - - return -EINVAL; -} - -static int check_src_coords(uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h, - const struct drm_framebuffer *fb) -{ - unsigned int fb_width, fb_height; - - fb_width = fb->width << 16; - fb_height = fb->height << 16; - - /* Make sure source coordinates are inside the fb. */ - if (src_w > fb_width || - src_x > fb_width - src_w || - src_h > fb_height || - src_y > fb_height - src_h) { - DRM_DEBUG_KMS("Invalid source coordinates " - "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", - src_w >> 16, ((src_w & 0xffff) * 15625) >> 10, - src_h >> 16, ((src_h & 0xffff) * 15625) >> 10, - src_x >> 16, ((src_x & 0xffff) * 15625) >> 10, - src_y >> 16, ((src_y & 0xffff) * 15625) >> 10); - return -ENOSPC; - } - - return 0; -} - -/* - * setplane_internal - setplane handler for internal callers - * - * Note that we assume an extra reference has already been taken on fb. If the - * update fails, this reference will be dropped before return; if it succeeds, - * the previous framebuffer (if any) will be unreferenced instead. - * - * src_{x,y,w,h} are provided in 16.16 fixed point format - */ -static int __setplane_internal(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int32_t crtc_x, int32_t crtc_y, - uint32_t crtc_w, uint32_t crtc_h, - /* src_{x,y,w,h} values are 16.16 fixed point */ - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) -{ - int ret = 0; - - /* No fb means shut it down */ - if (!fb) { - plane->old_fb = plane->fb; - ret = plane->funcs->disable_plane(plane); - if (!ret) { - plane->crtc = NULL; - plane->fb = NULL; - } else { - plane->old_fb = NULL; - } - goto out; - } - - /* Check whether this plane is usable on this CRTC */ - if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { - DRM_DEBUG_KMS("Invalid crtc for plane\n"); - ret = -EINVAL; - goto out; - } - - /* Check whether this plane supports the fb pixel format. */ - ret = drm_plane_check_pixel_format(plane, fb->pixel_format); - if (ret) { - DRM_DEBUG_KMS("Invalid pixel format %s\n", - drm_get_format_name(fb->pixel_format)); - goto out; - } - - /* Give drivers some help against integer overflows */ - if (crtc_w > INT_MAX || - crtc_x > INT_MAX - (int32_t) crtc_w || - crtc_h > INT_MAX || - crtc_y > INT_MAX - (int32_t) crtc_h) { - DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", - crtc_w, crtc_h, crtc_x, crtc_y); - ret = -ERANGE; - goto out; - } - - ret = check_src_coords(src_x, src_y, src_w, src_h, fb); - if (ret) - goto out; - - plane->old_fb = plane->fb; - ret = plane->funcs->update_plane(plane, crtc, fb, - crtc_x, crtc_y, crtc_w, crtc_h, - src_x, src_y, src_w, src_h); - if (!ret) { - plane->crtc = crtc; - plane->fb = fb; - fb = NULL; - } else { - plane->old_fb = NULL; - } - -out: - if (fb) - drm_framebuffer_unreference(fb); - if (plane->old_fb) - drm_framebuffer_unreference(plane->old_fb); - plane->old_fb = NULL; - - return ret; -} - -static int setplane_internal(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int32_t crtc_x, int32_t crtc_y, - uint32_t crtc_w, uint32_t crtc_h, - /* src_{x,y,w,h} values are 16.16 fixed point */ - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) -{ - int ret; - - drm_modeset_lock_all(plane->dev); - ret = __setplane_internal(plane, crtc, fb, - crtc_x, crtc_y, crtc_w, crtc_h, - src_x, src_y, src_w, src_h); - drm_modeset_unlock_all(plane->dev); - - return ret; -} - -/** - * drm_mode_setplane - configure a plane's configuration - * @dev: DRM device - * @data: ioctl data* - * @file_priv: DRM file info - * - * Set plane configuration, including placement, fb, scaling, and other factors. - * Or pass a NULL fb to disable (planes may be disabled without providing a - * valid crtc). - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_setplane(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_set_plane *plane_req = data; - struct drm_plane *plane; - struct drm_crtc *crtc = NULL; - struct drm_framebuffer *fb = NULL; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - /* - * First, find the plane, crtc, and fb objects. If not available, - * we don't bother to call the driver. - */ - plane = drm_plane_find(dev, plane_req->plane_id); - if (!plane) { - DRM_DEBUG_KMS("Unknown plane ID %d\n", - plane_req->plane_id); - return -ENOENT; - } - - if (plane_req->fb_id) { - fb = drm_framebuffer_lookup(dev, plane_req->fb_id); - if (!fb) { - DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", - plane_req->fb_id); - return -ENOENT; - } - - crtc = drm_crtc_find(dev, plane_req->crtc_id); - if (!crtc) { - DRM_DEBUG_KMS("Unknown crtc ID %d\n", - plane_req->crtc_id); - return -ENOENT; - } - } - - /* - * setplane_internal will take care of deref'ing either the old or new - * framebuffer depending on success. - */ - return setplane_internal(plane, crtc, fb, - plane_req->crtc_x, plane_req->crtc_y, - plane_req->crtc_w, plane_req->crtc_h, - plane_req->src_x, plane_req->src_y, - plane_req->src_w, plane_req->src_h); -} - -/** - * drm_mode_set_config_internal - helper to call ->set_config - * @set: modeset config to set - * - * This is a little helper to wrap internal calls to the ->set_config driver - * interface. The only thing it adds is correct refcounting dance. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_set_config_internal(struct drm_mode_set *set) -{ - struct drm_crtc *crtc = set->crtc; - struct drm_framebuffer *fb; - struct drm_crtc *tmp; - int ret; - - /* - * NOTE: ->set_config can also disable other crtcs (if we steal all - * connectors from it), hence we need to refcount the fbs across all - * crtcs. Atomic modeset will have saner semantics ... - */ - drm_for_each_crtc(tmp, crtc->dev) - tmp->primary->old_fb = tmp->primary->fb; - - fb = set->fb; - - ret = crtc->funcs->set_config(set); - if (ret == 0) { - crtc->primary->crtc = crtc; - crtc->primary->fb = fb; - } - - drm_for_each_crtc(tmp, crtc->dev) { - if (tmp->primary->fb) - drm_framebuffer_reference(tmp->primary->fb); - if (tmp->primary->old_fb) - drm_framebuffer_unreference(tmp->primary->old_fb); - tmp->primary->old_fb = NULL; - } - - return ret; -} -EXPORT_SYMBOL(drm_mode_set_config_internal); - -/** - * drm_crtc_get_hv_timing - Fetches hdisplay/vdisplay for given mode - * @mode: mode to query - * @hdisplay: hdisplay value to fill in - * @vdisplay: vdisplay value to fill in - * - * The vdisplay value will be doubled if the specified mode is a stereo mode of - * the appropriate layout. - */ -void drm_crtc_get_hv_timing(const struct drm_display_mode *mode, - int *hdisplay, int *vdisplay) -{ - struct drm_display_mode adjusted; - - drm_mode_copy(&adjusted, mode); - drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY); - *hdisplay = adjusted.crtc_hdisplay; - *vdisplay = adjusted.crtc_vdisplay; -} -EXPORT_SYMBOL(drm_crtc_get_hv_timing); - -/** - * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the - * CRTC viewport - * @crtc: CRTC that framebuffer will be displayed on - * @x: x panning - * @y: y panning - * @mode: mode that framebuffer will be displayed under - * @fb: framebuffer to check size of - */ -int drm_crtc_check_viewport(const struct drm_crtc *crtc, - int x, int y, - const struct drm_display_mode *mode, - const struct drm_framebuffer *fb) - -{ - int hdisplay, vdisplay; - - drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay); - - if (crtc->state && - crtc->primary->state->rotation & (BIT(DRM_ROTATE_90) | - BIT(DRM_ROTATE_270))) - swap(hdisplay, vdisplay); - - return check_src_coords(x << 16, y << 16, - hdisplay << 16, vdisplay << 16, fb); -} -EXPORT_SYMBOL(drm_crtc_check_viewport); - -/** - * drm_mode_setcrtc - set CRTC configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Build a new CRTC configuration based on user request. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_setcrtc(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_config *config = &dev->mode_config; - struct drm_mode_crtc *crtc_req = data; - struct drm_crtc *crtc; - struct drm_connector **connector_set = NULL, *connector; - struct drm_framebuffer *fb = NULL; - struct drm_display_mode *mode = NULL; - struct drm_mode_set set; - uint32_t __user *set_connectors_ptr; - int ret; - int i; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - /* - * Universal plane src offsets are only 16.16, prevent havoc for - * drivers using universal plane code internally. - */ - if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000) - return -ERANGE; - - drm_modeset_lock_all(dev); - crtc = drm_crtc_find(dev, crtc_req->crtc_id); - if (!crtc) { - DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); - ret = -ENOENT; - goto out; - } - DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); - - if (crtc_req->mode_valid) { - /* If we have a mode we need a framebuffer. */ - /* If we pass -1, set the mode with the currently bound fb */ - if (crtc_req->fb_id == -1) { - if (!crtc->primary->fb) { - DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); - ret = -EINVAL; - goto out; - } - fb = crtc->primary->fb; - /* Make refcounting symmetric with the lookup path. */ - drm_framebuffer_reference(fb); - } else { - fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); - if (!fb) { - DRM_DEBUG_KMS("Unknown FB ID%d\n", - crtc_req->fb_id); - ret = -ENOENT; - goto out; - } - } - - mode = drm_mode_create(dev); - if (!mode) { - ret = -ENOMEM; - goto out; - } - - ret = drm_mode_convert_umode(mode, &crtc_req->mode); - if (ret) { - DRM_DEBUG_KMS("Invalid mode\n"); - goto out; - } - - /* - * Check whether the primary plane supports the fb pixel format. - * Drivers not implementing the universal planes API use a - * default formats list provided by the DRM core which doesn't - * match real hardware capabilities. Skip the check in that - * case. - */ - if (!crtc->primary->format_default) { - ret = drm_plane_check_pixel_format(crtc->primary, - fb->pixel_format); - if (ret) { - DRM_DEBUG_KMS("Invalid pixel format %s\n", - drm_get_format_name(fb->pixel_format)); - goto out; - } - } - - ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y, - mode, fb); - if (ret) - goto out; - - } - - if (crtc_req->count_connectors == 0 && mode) { - DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); - ret = -EINVAL; - goto out; - } - - if (crtc_req->count_connectors > 0 && (!mode || !fb)) { - DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n", - crtc_req->count_connectors); - ret = -EINVAL; - goto out; - } - - if (crtc_req->count_connectors > 0) { - u32 out_id; - - /* Avoid unbounded kernel memory allocation */ - if (crtc_req->count_connectors > config->num_connector) { - ret = -EINVAL; - goto out; - } - - connector_set = kmalloc_array(crtc_req->count_connectors, - sizeof(struct drm_connector *), - GFP_KERNEL); - if (!connector_set) { - ret = -ENOMEM; - goto out; - } - - for (i = 0; i < crtc_req->count_connectors; i++) { - set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr; - if (get_user(out_id, &set_connectors_ptr[i])) { - ret = -EFAULT; - goto out; - } - - connector = drm_connector_find(dev, out_id); - if (!connector) { - DRM_DEBUG_KMS("Connector id %d unknown\n", - out_id); - ret = -ENOENT; - goto out; - } - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); - - connector_set[i] = connector; - } - } - - set.crtc = crtc; - set.x = crtc_req->x; - set.y = crtc_req->y; - set.mode = mode; - set.connectors = connector_set; - set.num_connectors = crtc_req->count_connectors; - set.fb = fb; - ret = drm_mode_set_config_internal(&set); - -out: - if (fb) - drm_framebuffer_unreference(fb); - - kfree(connector_set); - drm_mode_destroy(dev, mode); - drm_modeset_unlock_all(dev); - return ret; -} - -/** - * drm_mode_cursor_universal - translate legacy cursor ioctl call into a - * universal plane handler call - * @crtc: crtc to update cursor for - * @req: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Legacy cursor ioctl's work directly with driver buffer handles. To - * translate legacy ioctl calls into universal plane handler calls, we need to - * wrap the native buffer handle in a drm_framebuffer. - * - * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB - * buffer with a pitch of 4*width; the universal plane interface should be used - * directly in cases where the hardware can support other buffer settings and - * userspace wants to make use of these capabilities. - * - * Returns: - * Zero on success, negative errno on failure. - */ -static int drm_mode_cursor_universal(struct drm_crtc *crtc, - struct drm_mode_cursor2 *req, - struct drm_file *file_priv) -{ - struct drm_device *dev = crtc->dev; - struct drm_framebuffer *fb = NULL; - struct drm_mode_fb_cmd2 fbreq = { - .width = req->width, - .height = req->height, - .pixel_format = DRM_FORMAT_ARGB8888, - .pitches = { req->width * 4 }, - .handles = { req->handle }, - }; - int32_t crtc_x, crtc_y; - uint32_t crtc_w = 0, crtc_h = 0; - uint32_t src_w = 0, src_h = 0; - int ret = 0; - - BUG_ON(!crtc->cursor); - WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL); - - /* - * Obtain fb we'll be using (either new or existing) and take an extra - * reference to it if fb != null. setplane will take care of dropping - * the reference if the plane update fails. - */ - if (req->flags & DRM_MODE_CURSOR_BO) { - if (req->handle) { - fb = internal_framebuffer_create(dev, &fbreq, file_priv); - if (IS_ERR(fb)) { - DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); - return PTR_ERR(fb); - } - } else { - fb = NULL; - } - } else { - fb = crtc->cursor->fb; - if (fb) - drm_framebuffer_reference(fb); - } - - if (req->flags & DRM_MODE_CURSOR_MOVE) { - crtc_x = req->x; - crtc_y = req->y; - } else { - crtc_x = crtc->cursor_x; - crtc_y = crtc->cursor_y; - } - - if (fb) { - crtc_w = fb->width; - crtc_h = fb->height; - src_w = fb->width << 16; - src_h = fb->height << 16; - } - - /* - * setplane_internal will take care of deref'ing either the old or new - * framebuffer depending on success. - */ - ret = __setplane_internal(crtc->cursor, crtc, fb, - crtc_x, crtc_y, crtc_w, crtc_h, - 0, 0, src_w, src_h); - - /* Update successful; save new cursor position, if necessary */ - if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) { - crtc->cursor_x = req->x; - crtc->cursor_y = req->y; - } - - return ret; -} - -static int drm_mode_cursor_common(struct drm_device *dev, - struct drm_mode_cursor2 *req, - struct drm_file *file_priv) -{ - struct drm_crtc *crtc; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) - return -EINVAL; - - crtc = drm_crtc_find(dev, req->crtc_id); - if (!crtc) { - DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); - return -ENOENT; - } - - /* - * If this crtc has a universal cursor plane, call that plane's update - * handler rather than using legacy cursor handlers. - */ - drm_modeset_lock_crtc(crtc, crtc->cursor); - if (crtc->cursor) { - ret = drm_mode_cursor_universal(crtc, req, file_priv); - goto out; - } - - if (req->flags & DRM_MODE_CURSOR_BO) { - if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { - ret = -ENXIO; - goto out; - } - /* Turns off the cursor if handle is 0 */ - if (crtc->funcs->cursor_set2) - ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle, - req->width, req->height, req->hot_x, req->hot_y); - else - ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, - req->width, req->height); - } - - if (req->flags & DRM_MODE_CURSOR_MOVE) { - if (crtc->funcs->cursor_move) { - ret = crtc->funcs->cursor_move(crtc, req->x, req->y); - } else { - ret = -EFAULT; - goto out; - } - } -out: - drm_modeset_unlock_crtc(crtc); - - return ret; - -} - - -/** - * drm_mode_cursor_ioctl - set CRTC's cursor configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Set the cursor configuration based on user request. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_cursor_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_cursor *req = data; - struct drm_mode_cursor2 new_req; - - memcpy(&new_req, req, sizeof(struct drm_mode_cursor)); - new_req.hot_x = new_req.hot_y = 0; - - return drm_mode_cursor_common(dev, &new_req, file_priv); -} - -/** - * drm_mode_cursor2_ioctl - set CRTC's cursor configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Set the cursor configuration based on user request. This implements the 2nd - * version of the cursor ioctl, which allows userspace to additionally specify - * the hotspot of the pointer. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_cursor2_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_cursor2 *req = data; - - return drm_mode_cursor_common(dev, req, file_priv); -} - -/** - * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description - * @bpp: bits per pixels - * @depth: bit depth per pixel - * - * Computes a drm fourcc pixel format code for the given @bpp/@depth values. - * Useful in fbdev emulation code, since that deals in those values. - */ -uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) -{ - uint32_t fmt; - - switch (bpp) { - case 8: - fmt = DRM_FORMAT_C8; - break; - case 16: - if (depth == 15) - fmt = DRM_FORMAT_XRGB1555; - else - fmt = DRM_FORMAT_RGB565; - break; - case 24: - fmt = DRM_FORMAT_RGB888; - break; - case 32: - if (depth == 24) - fmt = DRM_FORMAT_XRGB8888; - else if (depth == 30) - fmt = DRM_FORMAT_XRGB2101010; - else - fmt = DRM_FORMAT_ARGB8888; - break; - default: - DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n"); - fmt = DRM_FORMAT_XRGB8888; - break; - } - - return fmt; -} -EXPORT_SYMBOL(drm_mode_legacy_fb_format); - -/** - * drm_mode_addfb - add an FB to the graphics configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Add a new FB to the specified CRTC, given a user request. This is the - * original addfb ioctl which only supported RGB formats. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_addfb(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_fb_cmd *or = data; - struct drm_mode_fb_cmd2 r = {}; - int ret; - - /* convert to new format and call new ioctl */ - r.fb_id = or->fb_id; - r.width = or->width; - r.height = or->height; - r.pitches[0] = or->pitch; - r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); - r.handles[0] = or->handle; - - ret = drm_mode_addfb2(dev, &r, file_priv); - if (ret) - return ret; - - or->fb_id = r.fb_id; - - return 0; -} - -static int format_check(const struct drm_mode_fb_cmd2 *r) -{ - uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; - - switch (format) { - case DRM_FORMAT_C8: - case DRM_FORMAT_RGB332: - case DRM_FORMAT_BGR233: - case DRM_FORMAT_XRGB4444: - case DRM_FORMAT_XBGR4444: - case DRM_FORMAT_RGBX4444: - case DRM_FORMAT_BGRX4444: - case DRM_FORMAT_ARGB4444: - case DRM_FORMAT_ABGR4444: - case DRM_FORMAT_RGBA4444: - case DRM_FORMAT_BGRA4444: - case DRM_FORMAT_XRGB1555: - case DRM_FORMAT_XBGR1555: - case DRM_FORMAT_RGBX5551: - case DRM_FORMAT_BGRX5551: - case DRM_FORMAT_ARGB1555: - case DRM_FORMAT_ABGR1555: - case DRM_FORMAT_RGBA5551: - case DRM_FORMAT_BGRA5551: - case DRM_FORMAT_RGB565: - case DRM_FORMAT_BGR565: - case DRM_FORMAT_RGB888: - case DRM_FORMAT_BGR888: - case DRM_FORMAT_XRGB8888: - case DRM_FORMAT_XBGR8888: - case DRM_FORMAT_RGBX8888: - case DRM_FORMAT_BGRX8888: - case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_ABGR8888: - case DRM_FORMAT_RGBA8888: - case DRM_FORMAT_BGRA8888: - case DRM_FORMAT_XRGB2101010: - case DRM_FORMAT_XBGR2101010: - case DRM_FORMAT_RGBX1010102: - case DRM_FORMAT_BGRX1010102: - case DRM_FORMAT_ARGB2101010: - case DRM_FORMAT_ABGR2101010: - case DRM_FORMAT_RGBA1010102: - case DRM_FORMAT_BGRA1010102: - case DRM_FORMAT_YUYV: - case DRM_FORMAT_YVYU: - case DRM_FORMAT_UYVY: - case DRM_FORMAT_VYUY: - case DRM_FORMAT_AYUV: - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV21: - case DRM_FORMAT_NV16: - case DRM_FORMAT_NV61: - case DRM_FORMAT_NV24: - case DRM_FORMAT_NV42: - case DRM_FORMAT_YUV410: - case DRM_FORMAT_YVU410: - case DRM_FORMAT_YUV411: - case DRM_FORMAT_YVU411: - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YVU420: - case DRM_FORMAT_YUV422: - case DRM_FORMAT_YVU422: - case DRM_FORMAT_YUV444: - case DRM_FORMAT_YVU444: - return 0; - default: - DRM_DEBUG_KMS("invalid pixel format %s\n", - drm_get_format_name(r->pixel_format)); - return -EINVAL; - } -} - -static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) -{ - int ret, hsub, vsub, num_planes, i; - - ret = format_check(r); - if (ret) { - DRM_DEBUG_KMS("bad framebuffer format %s\n", - drm_get_format_name(r->pixel_format)); - return ret; - } - - hsub = drm_format_horz_chroma_subsampling(r->pixel_format); - vsub = drm_format_vert_chroma_subsampling(r->pixel_format); - num_planes = drm_format_num_planes(r->pixel_format); - - if (r->width == 0 || r->width % hsub) { - DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width); - return -EINVAL; - } - - if (r->height == 0 || r->height % vsub) { - DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height); - return -EINVAL; - } - - for (i = 0; i < num_planes; i++) { - unsigned int width = r->width / (i != 0 ? hsub : 1); - unsigned int height = r->height / (i != 0 ? vsub : 1); - unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i); - - if (!r->handles[i]) { - DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); - return -EINVAL; - } - - if ((uint64_t) width * cpp > UINT_MAX) - return -ERANGE; - - if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) - return -ERANGE; - - if (r->pitches[i] < width * cpp) { - DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); - return -EINVAL; - } - - if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) { - DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n", - r->modifier[i], i); - return -EINVAL; - } - - /* modifier specific checks: */ - switch (r->modifier[i]) { - case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: - /* NOTE: the pitch restriction may be lifted later if it turns - * out that no hw has this restriction: - */ - if (r->pixel_format != DRM_FORMAT_NV12 || - width % 128 || height % 32 || - r->pitches[i] % 128) { - DRM_DEBUG_KMS("bad modifier data for plane %d\n", i); - return -EINVAL; - } - break; - - default: - break; - } - } - - for (i = num_planes; i < 4; i++) { - if (r->modifier[i]) { - DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i); - return -EINVAL; - } - - /* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */ - if (!(r->flags & DRM_MODE_FB_MODIFIERS)) - continue; - - if (r->handles[i]) { - DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i); - return -EINVAL; - } - - if (r->pitches[i]) { - DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i); - return -EINVAL; - } - - if (r->offsets[i]) { - DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i); - return -EINVAL; - } - } - - return 0; -} - -static struct drm_framebuffer * -internal_framebuffer_create(struct drm_device *dev, - struct drm_mode_fb_cmd2 *r, - struct drm_file *file_priv) -{ - struct drm_mode_config *config = &dev->mode_config; - struct drm_framebuffer *fb; - int ret; - - if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) { - DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); - return ERR_PTR(-EINVAL); - } - - if ((config->min_width > r->width) || (r->width > config->max_width)) { - DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n", - r->width, config->min_width, config->max_width); - return ERR_PTR(-EINVAL); - } - if ((config->min_height > r->height) || (r->height > config->max_height)) { - DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n", - r->height, config->min_height, config->max_height); - return ERR_PTR(-EINVAL); - } - - if (r->flags & DRM_MODE_FB_MODIFIERS && - !dev->mode_config.allow_fb_modifiers) { - DRM_DEBUG_KMS("driver does not support fb modifiers\n"); - return ERR_PTR(-EINVAL); - } - - ret = framebuffer_check(r); - if (ret) - return ERR_PTR(ret); - - fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); - if (IS_ERR(fb)) { - DRM_DEBUG_KMS("could not create framebuffer\n"); - return fb; - } - - return fb; -} - -/** - * drm_mode_addfb2 - add an FB to the graphics configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Add a new FB to the specified CRTC, given a user request with format. This is - * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers - * and uses fourcc codes as pixel format specifiers. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_addfb2(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_fb_cmd2 *r = data; - struct drm_framebuffer *fb; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - fb = internal_framebuffer_create(dev, r, file_priv); - if (IS_ERR(fb)) - return PTR_ERR(fb); - - /* Transfer ownership to the filp for reaping on close */ - - DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); - mutex_lock(&file_priv->fbs_lock); - r->fb_id = fb->base.id; - list_add(&fb->filp_head, &file_priv->fbs); - mutex_unlock(&file_priv->fbs_lock); - - return 0; -} - -struct drm_mode_rmfb_work { - struct work_struct work; - struct list_head fbs; -}; - -static void drm_mode_rmfb_work_fn(struct work_struct *w) -{ - struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work); - - while (!list_empty(&arg->fbs)) { - struct drm_framebuffer *fb = - list_first_entry(&arg->fbs, typeof(*fb), filp_head); - - list_del_init(&fb->filp_head); - drm_framebuffer_remove(fb); - } -} - -/** - * drm_mode_rmfb - remove an FB from the configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Remove the FB specified by the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_rmfb(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_framebuffer *fb = NULL; - struct drm_framebuffer *fbl = NULL; - uint32_t *id = data; - int found = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - mutex_lock(&file_priv->fbs_lock); - mutex_lock(&dev->mode_config.fb_lock); - fb = __drm_framebuffer_lookup(dev, *id); - if (!fb) - goto fail_lookup; - - list_for_each_entry(fbl, &file_priv->fbs, filp_head) - if (fb == fbl) - found = 1; - if (!found) - goto fail_lookup; - - list_del_init(&fb->filp_head); - mutex_unlock(&dev->mode_config.fb_lock); - mutex_unlock(&file_priv->fbs_lock); - - /* - * we now own the reference that was stored in the fbs list - * - * drm_framebuffer_remove may fail with -EINTR on pending signals, - * so run this in a separate stack as there's no way to correctly - * handle this after the fb is already removed from the lookup table. - */ - if (atomic_read(&fb->refcount.refcount) > 1) { - struct drm_mode_rmfb_work arg; - - INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn); - INIT_LIST_HEAD(&arg.fbs); - list_add_tail(&fb->filp_head, &arg.fbs); - - schedule_work(&arg.work); - flush_work(&arg.work); - destroy_work_on_stack(&arg.work); - } else - drm_framebuffer_unreference(fb); - - return 0; - -fail_lookup: - mutex_unlock(&dev->mode_config.fb_lock); - mutex_unlock(&file_priv->fbs_lock); - - return -ENOENT; -} - -/** - * drm_mode_getfb - get FB info - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Lookup the FB given its ID and return info about it. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getfb(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_fb_cmd *r = data; - struct drm_framebuffer *fb; - int ret; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - fb = drm_framebuffer_lookup(dev, r->fb_id); - if (!fb) - return -ENOENT; - - r->height = fb->height; - r->width = fb->width; - r->depth = fb->depth; - r->bpp = fb->bits_per_pixel; - r->pitch = fb->pitches[0]; - if (fb->funcs->create_handle) { - if (file_priv->is_master || capable(CAP_SYS_ADMIN) || - drm_is_control_client(file_priv)) { - ret = fb->funcs->create_handle(fb, file_priv, - &r->handle); - } else { - /* GET_FB() is an unprivileged ioctl so we must not - * return a buffer-handle to non-master processes! For - * backwards-compatibility reasons, we cannot make - * GET_FB() privileged, so just return an invalid handle - * for non-masters. */ - r->handle = 0; - ret = 0; - } - } else { - ret = -ENODEV; - } - - drm_framebuffer_unreference(fb); - - return ret; -} - -/** - * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Lookup the FB and flush out the damaged area supplied by userspace as a clip - * rectangle list. Generic userspace which does frontbuffer rendering must call - * this ioctl to flush out the changes on manual-update display outputs, e.g. - * usb display-link, mipi manual update panels or edp panel self refresh modes. - * - * Modesetting drivers which always update the frontbuffer do not need to - * implement the corresponding ->dirty framebuffer callback. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_dirtyfb_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_clip_rect __user *clips_ptr; - struct drm_clip_rect *clips = NULL; - struct drm_mode_fb_dirty_cmd *r = data; - struct drm_framebuffer *fb; - unsigned flags; - int num_clips; - int ret; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - fb = drm_framebuffer_lookup(dev, r->fb_id); - if (!fb) - return -ENOENT; - - num_clips = r->num_clips; - clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; - - if (!num_clips != !clips_ptr) { - ret = -EINVAL; - goto out_err1; - } - - flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags; - - /* If userspace annotates copy, clips must come in pairs */ - if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) { - ret = -EINVAL; - goto out_err1; - } - - if (num_clips && clips_ptr) { - if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { - ret = -EINVAL; - goto out_err1; - } - clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); - if (!clips) { - ret = -ENOMEM; - goto out_err1; - } - - ret = copy_from_user(clips, clips_ptr, - num_clips * sizeof(*clips)); - if (ret) { - ret = -EFAULT; - goto out_err2; - } - } - - if (fb->funcs->dirty) { - ret = fb->funcs->dirty(fb, file_priv, flags, r->color, - clips, num_clips); - } else { - ret = -ENOSYS; - } - -out_err2: - kfree(clips); -out_err1: - drm_framebuffer_unreference(fb); - - return ret; -} - -/** - * drm_fb_release - remove and free the FBs on this file - * @priv: drm file for the ioctl - * - * Destroy all the FBs associated with @filp. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -void drm_fb_release(struct drm_file *priv) -{ - struct drm_framebuffer *fb, *tfb; - struct drm_mode_rmfb_work arg; - - INIT_LIST_HEAD(&arg.fbs); - - /* - * When the file gets released that means no one else can access the fb - * list any more, so no need to grab fpriv->fbs_lock. And we need to - * avoid upsetting lockdep since the universal cursor code adds a - * framebuffer while holding mutex locks. - * - * Note that a real deadlock between fpriv->fbs_lock and the modeset - * locks is impossible here since no one else but this function can get - * at it any more. - */ - list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { - if (atomic_read(&fb->refcount.refcount) > 1) { - list_move_tail(&fb->filp_head, &arg.fbs); - } else { - list_del_init(&fb->filp_head); - - /* This drops the fpriv->fbs reference. */ - drm_framebuffer_unreference(fb); - } - } - - if (!list_empty(&arg.fbs)) { - INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn); - - schedule_work(&arg.work); - flush_work(&arg.work); - destroy_work_on_stack(&arg.work); - } -} - -/** - * drm_property_create - create a new property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @num_values: number of pre-defined values - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Note that the DRM core keeps a per-device list of properties and that, if - * drm_mode_config_cleanup() is called, it will destroy all properties created - * by the driver. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create(struct drm_device *dev, int flags, - const char *name, int num_values) -{ - struct drm_property *property = NULL; - int ret; - - property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); - if (!property) - return NULL; - - property->dev = dev; - - if (num_values) { - property->values = kcalloc(num_values, sizeof(uint64_t), - GFP_KERNEL); - if (!property->values) - goto fail; - } - - ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); - if (ret) - goto fail; - - property->flags = flags; - property->num_values = num_values; - INIT_LIST_HEAD(&property->enum_list); - - if (name) { - strncpy(property->name, name, DRM_PROP_NAME_LEN); - property->name[DRM_PROP_NAME_LEN-1] = '\0'; - } - - list_add_tail(&property->head, &dev->mode_config.property_list); - - WARN_ON(!drm_property_type_valid(property)); - - return property; -fail: - kfree(property->values); - kfree(property); - return NULL; -} -EXPORT_SYMBOL(drm_property_create); - -/** - * drm_property_create_enum - create a new enumeration property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @props: enumeration lists with property values - * @num_values: number of pre-defined values - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Userspace is only allowed to set one of the predefined values for enumeration - * properties. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, - const char *name, - const struct drm_prop_enum_list *props, - int num_values) -{ - struct drm_property *property; - int i, ret; - - flags |= DRM_MODE_PROP_ENUM; - - property = drm_property_create(dev, flags, name, num_values); - if (!property) - return NULL; - - for (i = 0; i < num_values; i++) { - ret = drm_property_add_enum(property, i, - props[i].type, - props[i].name); - if (ret) { - drm_property_destroy(dev, property); - return NULL; - } - } - - return property; -} -EXPORT_SYMBOL(drm_property_create_enum); - -/** - * drm_property_create_bitmask - create a new bitmask property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @props: enumeration lists with property bitflags - * @num_props: size of the @props array - * @supported_bits: bitmask of all supported enumeration values - * - * This creates a new bitmask drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Compared to plain enumeration properties userspace is allowed to set any - * or'ed together combination of the predefined property bitflag values - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_bitmask(struct drm_device *dev, - int flags, const char *name, - const struct drm_prop_enum_list *props, - int num_props, - uint64_t supported_bits) -{ - struct drm_property *property; - int i, ret, index = 0; - int num_values = hweight64(supported_bits); - - flags |= DRM_MODE_PROP_BITMASK; - - property = drm_property_create(dev, flags, name, num_values); - if (!property) - return NULL; - for (i = 0; i < num_props; i++) { - if (!(supported_bits & (1ULL << props[i].type))) - continue; - - if (WARN_ON(index >= num_values)) { - drm_property_destroy(dev, property); - return NULL; - } - - ret = drm_property_add_enum(property, index++, - props[i].type, - props[i].name); - if (ret) { - drm_property_destroy(dev, property); - return NULL; - } - } - - return property; -} -EXPORT_SYMBOL(drm_property_create_bitmask); - -static struct drm_property *property_create_range(struct drm_device *dev, - int flags, const char *name, - uint64_t min, uint64_t max) -{ - struct drm_property *property; - - property = drm_property_create(dev, flags, name, 2); - if (!property) - return NULL; - - property->values[0] = min; - property->values[1] = max; - - return property; -} - -/** - * drm_property_create_range - create a new unsigned ranged property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @min: minimum value of the property - * @max: maximum value of the property - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Userspace is allowed to set any unsigned integer value in the (min, max) - * range inclusive. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, - const char *name, - uint64_t min, uint64_t max) -{ - return property_create_range(dev, DRM_MODE_PROP_RANGE | flags, - name, min, max); -} -EXPORT_SYMBOL(drm_property_create_range); - -/** - * drm_property_create_signed_range - create a new signed ranged property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @min: minimum value of the property - * @max: maximum value of the property - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Userspace is allowed to set any signed integer value in the (min, max) - * range inclusive. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_signed_range(struct drm_device *dev, - int flags, const char *name, - int64_t min, int64_t max) -{ - return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags, - name, I642U64(min), I642U64(max)); -} -EXPORT_SYMBOL(drm_property_create_signed_range); - -/** - * drm_property_create_object - create a new object property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @type: object type from DRM_MODE_OBJECT_* defines - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Userspace is only allowed to set this to any property value of the given - * @type. Only useful for atomic properties, which is enforced. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_object(struct drm_device *dev, - int flags, const char *name, uint32_t type) -{ - struct drm_property *property; - - flags |= DRM_MODE_PROP_OBJECT; - - if (WARN_ON(!(flags & DRM_MODE_PROP_ATOMIC))) - return NULL; - - property = drm_property_create(dev, flags, name, 1); - if (!property) - return NULL; - - property->values[0] = type; - - return property; -} -EXPORT_SYMBOL(drm_property_create_object); - -/** - * drm_property_create_bool - create a new boolean property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * This is implemented as a ranged property with only {0, 1} as valid values. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags, - const char *name) -{ - return drm_property_create_range(dev, flags, name, 0, 1); -} -EXPORT_SYMBOL(drm_property_create_bool); - -/** - * drm_property_add_enum - add a possible value to an enumeration property - * @property: enumeration property to change - * @index: index of the new enumeration - * @value: value of the new enumeration - * @name: symbolic name of the new enumeration - * - * This functions adds enumerations to a property. - * - * It's use is deprecated, drivers should use one of the more specific helpers - * to directly create the property with all enumerations already attached. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_property_add_enum(struct drm_property *property, int index, - uint64_t value, const char *name) -{ - struct drm_property_enum *prop_enum; - - if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) || - drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) - return -EINVAL; - - /* - * Bitmask enum properties have the additional constraint of values - * from 0 to 63 - */ - if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) && - (value > 63)) - return -EINVAL; - - if (!list_empty(&property->enum_list)) { - list_for_each_entry(prop_enum, &property->enum_list, head) { - if (prop_enum->value == value) { - strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); - prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; - return 0; - } - } - } - - prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); - if (!prop_enum) - return -ENOMEM; - - strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); - prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; - prop_enum->value = value; - - property->values[index] = value; - list_add_tail(&prop_enum->head, &property->enum_list); - return 0; -} -EXPORT_SYMBOL(drm_property_add_enum); - -/** - * drm_property_destroy - destroy a drm property - * @dev: drm device - * @property: property to destry - * - * This function frees a property including any attached resources like - * enumeration values. - */ -void drm_property_destroy(struct drm_device *dev, struct drm_property *property) -{ - struct drm_property_enum *prop_enum, *pt; - - list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) { - list_del(&prop_enum->head); - kfree(prop_enum); - } - - if (property->num_values) - kfree(property->values); - drm_mode_object_put(dev, &property->base); - list_del(&property->head); - kfree(property); -} -EXPORT_SYMBOL(drm_property_destroy); - -/** - * drm_object_attach_property - attach a property to a modeset object - * @obj: drm modeset object - * @property: property to attach - * @init_val: initial value of the property - * - * This attaches the given property to the modeset object with the given initial - * value. Currently this function cannot fail since the properties are stored in - * a statically sized array. - */ -void drm_object_attach_property(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t init_val) -{ - int count = obj->properties->count; - - if (count == DRM_OBJECT_MAX_PROPERTY) { - WARN(1, "Failed to attach object property (type: 0x%x). Please " - "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " - "you see this message on the same object type.\n", - obj->type); - return; - } - - obj->properties->properties[count] = property; - obj->properties->values[count] = init_val; - obj->properties->count++; - if (property->flags & DRM_MODE_PROP_ATOMIC) - obj->properties->atomic_count++; -} -EXPORT_SYMBOL(drm_object_attach_property); - -/** - * drm_object_property_set_value - set the value of a property - * @obj: drm mode object to set property value for - * @property: property to set - * @val: value the property should be set to - * - * This functions sets a given property on a given object. This function only - * changes the software state of the property, it does not call into the - * driver's ->set_property callback. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_object_property_set_value(struct drm_mode_object *obj, - struct drm_property *property, uint64_t val) -{ - int i; - - for (i = 0; i < obj->properties->count; i++) { - if (obj->properties->properties[i] == property) { - obj->properties->values[i] = val; - return 0; - } - } - - return -EINVAL; -} -EXPORT_SYMBOL(drm_object_property_set_value); - -/** - * drm_object_property_get_value - retrieve the value of a property - * @obj: drm mode object to get property value from - * @property: property to retrieve - * @val: storage for the property value - * - * This function retrieves the softare state of the given property for the given - * property. Since there is no driver callback to retrieve the current property - * value this might be out of sync with the hardware, depending upon the driver - * and property. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_object_property_get_value(struct drm_mode_object *obj, - struct drm_property *property, uint64_t *val) -{ - int i; - - /* read-only properties bypass atomic mechanism and still store - * their value in obj->properties->values[].. mostly to avoid - * having to deal w/ EDID and similar props in atomic paths: - */ - if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) && - !(property->flags & DRM_MODE_PROP_IMMUTABLE)) - return drm_atomic_get_property(obj, property, val); - - for (i = 0; i < obj->properties->count; i++) { - if (obj->properties->properties[i] == property) { - *val = obj->properties->values[i]; - return 0; - } - } - - return -EINVAL; -} -EXPORT_SYMBOL(drm_object_property_get_value); - -/** - * drm_mode_getproperty_ioctl - get the property metadata - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function retrieves the metadata for a given property, like the different - * possible values for an enum property or the limits for a range property. - * - * Blob properties are special - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getproperty_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_get_property *out_resp = data; - struct drm_property *property; - int enum_count = 0; - int value_count = 0; - int ret = 0, i; - int copied; - struct drm_property_enum *prop_enum; - struct drm_mode_property_enum __user *enum_ptr; - uint64_t __user *values_ptr; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - property = drm_property_find(dev, out_resp->prop_id); - if (!property) { - ret = -ENOENT; - goto done; - } - - if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || - drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - list_for_each_entry(prop_enum, &property->enum_list, head) - enum_count++; - } - - value_count = property->num_values; - - strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); - out_resp->name[DRM_PROP_NAME_LEN-1] = 0; - out_resp->flags = property->flags; - - if ((out_resp->count_values >= value_count) && value_count) { - values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr; - for (i = 0; i < value_count; i++) { - if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { - ret = -EFAULT; - goto done; - } - } - } - out_resp->count_values = value_count; - - if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || - drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { - copied = 0; - enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; - list_for_each_entry(prop_enum, &property->enum_list, head) { - - if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { - ret = -EFAULT; - goto done; - } - - if (copy_to_user(&enum_ptr[copied].name, - &prop_enum->name, DRM_PROP_NAME_LEN)) { - ret = -EFAULT; - goto done; - } - copied++; - } - } - out_resp->count_enum_blobs = enum_count; - } - - /* - * NOTE: The idea seems to have been to use this to read all the blob - * property values. But nothing ever added them to the corresponding - * list, userspace always used the special-purpose get_blob ioctl to - * read the value for a blob property. It also doesn't make a lot of - * sense to return values here when everything else is just metadata for - * the property itself. - */ - if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) - out_resp->count_enum_blobs = 0; -done: - drm_modeset_unlock_all(dev); - return ret; -} - -/** - * drm_property_create_blob - Create new blob property - * - * Creates a new blob property for a specified DRM device, optionally - * copying data. - * - * @dev: DRM device to create property for - * @length: Length to allocate for blob data - * @data: If specified, copies data into blob - * - * Returns: - * New blob property with a single reference on success, or an ERR_PTR - * value on failure. - */ -struct drm_property_blob * -drm_property_create_blob(struct drm_device *dev, size_t length, - const void *data) -{ - struct drm_property_blob *blob; - int ret; - - if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob)) - return ERR_PTR(-EINVAL); - - blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); - if (!blob) - return ERR_PTR(-ENOMEM); - - /* This must be explicitly initialised, so we can safely call list_del - * on it in the removal handler, even if it isn't in a file list. */ - INIT_LIST_HEAD(&blob->head_file); - blob->length = length; - blob->dev = dev; - - if (data) - memcpy(blob->data, data, length); - - mutex_lock(&dev->mode_config.blob_lock); - - ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); - if (ret) { - kfree(blob); - mutex_unlock(&dev->mode_config.blob_lock); - return ERR_PTR(-EINVAL); - } - - kref_init(&blob->refcount); - - list_add_tail(&blob->head_global, - &dev->mode_config.property_blob_list); - - mutex_unlock(&dev->mode_config.blob_lock); - - return blob; -} -EXPORT_SYMBOL(drm_property_create_blob); - -/** - * drm_property_free_blob - Blob property destructor - * - * Internal free function for blob properties; must not be used directly. - * - * @kref: Reference - */ -static void drm_property_free_blob(struct kref *kref) -{ - struct drm_property_blob *blob = - container_of(kref, struct drm_property_blob, refcount); - - WARN_ON(!mutex_is_locked(&blob->dev->mode_config.blob_lock)); - - list_del(&blob->head_global); - list_del(&blob->head_file); - drm_mode_object_put(blob->dev, &blob->base); - - kfree(blob); -} - -/** - * drm_property_unreference_blob - Unreference a blob property - * - * Drop a reference on a blob property. May free the object. - * - * @blob: Pointer to blob property - */ -void drm_property_unreference_blob(struct drm_property_blob *blob) -{ - struct drm_device *dev; - - if (!blob) - return; - - dev = blob->dev; - - DRM_DEBUG("%p: blob ID: %d (%d)\n", blob, blob->base.id, atomic_read(&blob->refcount.refcount)); - - if (kref_put_mutex(&blob->refcount, drm_property_free_blob, - &dev->mode_config.blob_lock)) - mutex_unlock(&dev->mode_config.blob_lock); - else - might_lock(&dev->mode_config.blob_lock); -} -EXPORT_SYMBOL(drm_property_unreference_blob); - -/** - * drm_property_unreference_blob_locked - Unreference a blob property with blob_lock held - * - * Drop a reference on a blob property. May free the object. This must be - * called with blob_lock held. - * - * @blob: Pointer to blob property - */ -static void drm_property_unreference_blob_locked(struct drm_property_blob *blob) -{ - if (!blob) - return; - - DRM_DEBUG("%p: blob ID: %d (%d)\n", blob, blob->base.id, atomic_read(&blob->refcount.refcount)); - - kref_put(&blob->refcount, drm_property_free_blob); -} - -/** - * drm_property_destroy_user_blobs - destroy all blobs created by this client - * @dev: DRM device - * @file_priv: destroy all blobs owned by this file handle - */ -void drm_property_destroy_user_blobs(struct drm_device *dev, - struct drm_file *file_priv) -{ - struct drm_property_blob *blob, *bt; - - mutex_lock(&dev->mode_config.blob_lock); - - list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) { - list_del_init(&blob->head_file); - drm_property_unreference_blob_locked(blob); - } - - mutex_unlock(&dev->mode_config.blob_lock); -} - -/** - * drm_property_reference_blob - Take a reference on an existing property - * - * Take a new reference on an existing blob property. - * - * @blob: Pointer to blob property - */ -struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob) -{ - DRM_DEBUG("%p: blob ID: %d (%d)\n", blob, blob->base.id, atomic_read(&blob->refcount.refcount)); - kref_get(&blob->refcount); - return blob; -} -EXPORT_SYMBOL(drm_property_reference_blob); - -/* - * Like drm_property_lookup_blob, but does not return an additional reference. - * Must be called with blob_lock held. - */ -static struct drm_property_blob *__drm_property_lookup_blob(struct drm_device *dev, - uint32_t id) -{ - struct drm_mode_object *obj = NULL; - struct drm_property_blob *blob; - - WARN_ON(!mutex_is_locked(&dev->mode_config.blob_lock)); - - mutex_lock(&dev->mode_config.idr_mutex); - obj = idr_find(&dev->mode_config.crtc_idr, id); - if (!obj || (obj->type != DRM_MODE_OBJECT_BLOB) || (obj->id != id)) - blob = NULL; - else - blob = obj_to_blob(obj); - mutex_unlock(&dev->mode_config.idr_mutex); - - return blob; -} - -/** - * drm_property_lookup_blob - look up a blob property and take a reference - * @dev: drm device - * @id: id of the blob property - * - * If successful, this takes an additional reference to the blob property. - * callers need to make sure to eventually unreference the returned property - * again, using @drm_property_unreference_blob. - */ -struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, - uint32_t id) -{ - struct drm_property_blob *blob; - - mutex_lock(&dev->mode_config.blob_lock); - blob = __drm_property_lookup_blob(dev, id); - if (blob) { - if (!kref_get_unless_zero(&blob->refcount)) - blob = NULL; - } - mutex_unlock(&dev->mode_config.blob_lock); - - return blob; -} -EXPORT_SYMBOL(drm_property_lookup_blob); - -/** - * drm_property_replace_global_blob - atomically replace existing blob property - * @dev: drm device - * @replace: location of blob property pointer to be replaced - * @length: length of data for new blob, or 0 for no data - * @data: content for new blob, or NULL for no data - * @obj_holds_id: optional object for property holding blob ID - * @prop_holds_id: optional property holding blob ID - * @return 0 on success or error on failure - * - * This function will atomically replace a global property in the blob list, - * optionally updating a property which holds the ID of that property. It is - * guaranteed to be atomic: no caller will be allowed to see intermediate - * results, and either the entire operation will succeed and clean up the - * previous property, or it will fail and the state will be unchanged. - * - * If length is 0 or data is NULL, no new blob will be created, and the holding - * property, if specified, will be set to 0. - * - * Access to the replace pointer is assumed to be protected by the caller, e.g. - * by holding the relevant modesetting object lock for its parent. - * - * For example, a drm_connector has a 'PATH' property, which contains the ID - * of a blob property with the value of the MST path information. Calling this - * function with replace pointing to the connector's path_blob_ptr, length and - * data set for the new path information, obj_holds_id set to the connector's - * base object, and prop_holds_id set to the path property name, will perform - * a completely atomic update. The access to path_blob_ptr is protected by the - * caller holding a lock on the connector. - */ -static int drm_property_replace_global_blob(struct drm_device *dev, - struct drm_property_blob **replace, - size_t length, - const void *data, - struct drm_mode_object *obj_holds_id, - struct drm_property *prop_holds_id) -{ - struct drm_property_blob *new_blob = NULL; - struct drm_property_blob *old_blob = NULL; - int ret; - - WARN_ON(replace == NULL); - - old_blob = *replace; - - if (length && data) { - new_blob = drm_property_create_blob(dev, length, data); - if (IS_ERR(new_blob)) - return PTR_ERR(new_blob); - } - - /* This does not need to be synchronised with blob_lock, as the - * get_properties ioctl locks all modesetting objects, and - * obj_holds_id must be locked before calling here, so we cannot - * have its value out of sync with the list membership modified - * below under blob_lock. */ - if (obj_holds_id) { - ret = drm_object_property_set_value(obj_holds_id, - prop_holds_id, - new_blob ? - new_blob->base.id : 0); - if (ret != 0) - goto err_created; - } - - drm_property_unreference_blob(old_blob); - *replace = new_blob; - - return 0; - -err_created: - drm_property_unreference_blob(new_blob); - return ret; -} - -/** - * drm_mode_getblob_ioctl - get the contents of a blob property value - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function retrieves the contents of a blob property. The value stored in - * an object's blob property is just a normal modeset object id. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getblob_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_get_blob *out_resp = data; - struct drm_property_blob *blob; - int ret = 0; - void __user *blob_ptr; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - mutex_lock(&dev->mode_config.blob_lock); - blob = __drm_property_lookup_blob(dev, out_resp->blob_id); - if (!blob) { - ret = -ENOENT; - goto done; - } - - if (out_resp->length == blob->length) { - blob_ptr = (void __user *)(unsigned long)out_resp->data; - if (copy_to_user(blob_ptr, blob->data, blob->length)) { - ret = -EFAULT; - goto done; - } - } - out_resp->length = blob->length; - -done: - mutex_unlock(&dev->mode_config.blob_lock); - drm_modeset_unlock_all(dev); - return ret; -} - -/** - * drm_mode_createblob_ioctl - create a new blob property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function creates a new blob property with user-defined values. In order - * to give us sensible validation and checking when creating, rather than at - * every potential use, we also require a type to be provided upfront. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_createblob_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_create_blob *out_resp = data; - struct drm_property_blob *blob; - void __user *blob_ptr; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - blob = drm_property_create_blob(dev, out_resp->length, NULL); - if (IS_ERR(blob)) - return PTR_ERR(blob); - - blob_ptr = (void __user *)(unsigned long)out_resp->data; - if (copy_from_user(blob->data, blob_ptr, out_resp->length)) { - ret = -EFAULT; - goto out_blob; - } - - /* Dropping the lock between create_blob and our access here is safe - * as only the same file_priv can remove the blob; at this point, it is - * not associated with any file_priv. */ - mutex_lock(&dev->mode_config.blob_lock); - out_resp->blob_id = blob->base.id; - list_add_tail(&blob->head_file, &file_priv->blobs); - mutex_unlock(&dev->mode_config.blob_lock); - - return 0; - -out_blob: - drm_property_unreference_blob(blob); - return ret; -} - -/** - * drm_mode_destroyblob_ioctl - destroy a user blob property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Destroy an existing user-defined blob property. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_destroyblob_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_destroy_blob *out_resp = data; - struct drm_property_blob *blob = NULL, *bt; - bool found = false; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - mutex_lock(&dev->mode_config.blob_lock); - blob = __drm_property_lookup_blob(dev, out_resp->blob_id); - if (!blob) { - ret = -ENOENT; - goto err; - } - - /* Ensure the property was actually created by this user. */ - list_for_each_entry(bt, &file_priv->blobs, head_file) { - if (bt == blob) { - found = true; - break; - } - } - - if (!found) { - ret = -EPERM; - goto err; - } - - /* We must drop head_file here, because we may not be the last - * reference on the blob. */ - list_del_init(&blob->head_file); - drm_property_unreference_blob_locked(blob); - mutex_unlock(&dev->mode_config.blob_lock); - - return 0; - -err: - mutex_unlock(&dev->mode_config.blob_lock); - return ret; -} - -/** - * drm_mode_connector_set_path_property - set tile property on connector - * @connector: connector to set property on. - * @path: path to use for property; must not be NULL. - * - * This creates a property to expose to userspace to specify a - * connector path. This is mainly used for DisplayPort MST where - * connectors have a topology and we want to allow userspace to give - * them more meaningful names. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_connector_set_path_property(struct drm_connector *connector, - const char *path) -{ - struct drm_device *dev = connector->dev; - int ret; - - ret = drm_property_replace_global_blob(dev, - &connector->path_blob_ptr, - strlen(path) + 1, - path, - &connector->base, - dev->mode_config.path_property); - return ret; -} -EXPORT_SYMBOL(drm_mode_connector_set_path_property); - -/** - * drm_mode_connector_set_tile_property - set tile property on connector - * @connector: connector to set property on. - * - * This looks up the tile information for a connector, and creates a - * property for userspace to parse if it exists. The property is of - * the form of 8 integers using ':' as a separator. - * - * Returns: - * Zero on success, errno on failure. - */ -int drm_mode_connector_set_tile_property(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - char tile[256]; - int ret; - - if (!connector->has_tile) { - ret = drm_property_replace_global_blob(dev, - &connector->tile_blob_ptr, - 0, - NULL, - &connector->base, - dev->mode_config.tile_property); - return ret; - } - - snprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d", - connector->tile_group->id, connector->tile_is_single_monitor, - connector->num_h_tile, connector->num_v_tile, - connector->tile_h_loc, connector->tile_v_loc, - connector->tile_h_size, connector->tile_v_size); - - ret = drm_property_replace_global_blob(dev, - &connector->tile_blob_ptr, - strlen(tile) + 1, - tile, - &connector->base, - dev->mode_config.tile_property); - return ret; -} -EXPORT_SYMBOL(drm_mode_connector_set_tile_property); - -/** - * drm_mode_connector_update_edid_property - update the edid property of a connector - * @connector: drm connector - * @edid: new value of the edid property - * - * This function creates a new blob modeset object and assigns its id to the - * connector's edid property. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_connector_update_edid_property(struct drm_connector *connector, - const struct edid *edid) -{ - struct drm_device *dev = connector->dev; - size_t size = 0; - int ret; - - /* ignore requests to set edid when overridden */ - if (connector->override_edid) - return 0; - - if (edid) - size = EDID_LENGTH * (1 + edid->extensions); - - ret = drm_property_replace_global_blob(dev, - &connector->edid_blob_ptr, - size, - edid, - &connector->base, - dev->mode_config.edid_property); - return ret; -} -EXPORT_SYMBOL(drm_mode_connector_update_edid_property); - -/* Some properties could refer to dynamic refcnt'd objects, or things that - * need special locking to handle lifetime issues (ie. to ensure the prop - * value doesn't become invalid part way through the property update due to - * race). The value returned by reference via 'obj' should be passed back - * to drm_property_change_valid_put() after the property is set (and the - * object to which the property is attached has a chance to take it's own - * reference). - */ -bool drm_property_change_valid_get(struct drm_property *property, - uint64_t value, struct drm_mode_object **ref) -{ - int i; - - if (property->flags & DRM_MODE_PROP_IMMUTABLE) - return false; - - *ref = NULL; - - if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { - if (value < property->values[0] || value > property->values[1]) - return false; - return true; - } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) { - int64_t svalue = U642I64(value); - - if (svalue < U642I64(property->values[0]) || - svalue > U642I64(property->values[1])) - return false; - return true; - } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - uint64_t valid_mask = 0; - - for (i = 0; i < property->num_values; i++) - valid_mask |= (1ULL << property->values[i]); - return !(value & ~valid_mask); - } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { - struct drm_property_blob *blob; - - if (value == 0) - return true; - - blob = drm_property_lookup_blob(property->dev, value); - if (blob) { - *ref = &blob->base; - return true; - } else { - return false; - } - } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { - /* a zero value for an object property translates to null: */ - if (value == 0) - return true; - - /* handle refcnt'd objects specially: */ - if (property->values[0] == DRM_MODE_OBJECT_FB) { - struct drm_framebuffer *fb; - fb = drm_framebuffer_lookup(property->dev, value); - if (fb) { - *ref = &fb->base; - return true; - } else { - return false; - } - } else { - return _object_find(property->dev, value, property->values[0]) != NULL; - } - } - - for (i = 0; i < property->num_values; i++) - if (property->values[i] == value) - return true; - return false; -} - -void drm_property_change_valid_put(struct drm_property *property, - struct drm_mode_object *ref) -{ - if (!ref) - return; - - if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { - if (property->values[0] == DRM_MODE_OBJECT_FB) - drm_framebuffer_unreference(obj_to_fb(ref)); - } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) - drm_property_unreference_blob(obj_to_blob(ref)); -} - -/** - * drm_mode_connector_property_set_ioctl - set the current value of a connector property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function sets the current value for a connectors's property. It also - * calls into a driver's ->set_property callback to update the hardware state - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_connector_property_set_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_connector_set_property *conn_set_prop = data; - struct drm_mode_obj_set_property obj_set_prop = { - .value = conn_set_prop->value, - .prop_id = conn_set_prop->prop_id, - .obj_id = conn_set_prop->connector_id, - .obj_type = DRM_MODE_OBJECT_CONNECTOR - }; - - /* It does all the locking and checking we need */ - return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); -} - -static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t value) -{ - int ret = -EINVAL; - struct drm_connector *connector = obj_to_connector(obj); - - /* Do DPMS ourselves */ - if (property == connector->dev->mode_config.dpms_property) { - ret = 0; - if (connector->funcs->dpms) - ret = (*connector->funcs->dpms)(connector, (int)value); - } else if (connector->funcs->set_property) - ret = connector->funcs->set_property(connector, property, value); - - /* store the property value if successful */ - if (!ret) - drm_object_property_set_value(&connector->base, property, value); - return ret; -} - -static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t value) -{ - int ret = -EINVAL; - struct drm_crtc *crtc = obj_to_crtc(obj); - - if (crtc->funcs->set_property) - ret = crtc->funcs->set_property(crtc, property, value); - if (!ret) - drm_object_property_set_value(obj, property, value); - - return ret; -} - -/** - * drm_mode_plane_set_obj_prop - set the value of a property - * @plane: drm plane object to set property value for - * @property: property to set - * @value: value the property should be set to - * - * This functions sets a given property on a given plane object. This function - * calls the driver's ->set_property callback and changes the software state of - * the property if the callback succeeds. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_mode_plane_set_obj_prop(struct drm_plane *plane, - struct drm_property *property, - uint64_t value) -{ - int ret = -EINVAL; - struct drm_mode_object *obj = &plane->base; - - if (plane->funcs->set_property) - ret = plane->funcs->set_property(plane, property, value); - if (!ret) - drm_object_property_set_value(obj, property, value); - - return ret; -} -EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); - -/** - * drm_mode_obj_get_properties_ioctl - get the current value of a object's property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function retrieves the current value for an object's property. Compared - * to the connector specific ioctl this one is extended to also work on crtc and - * plane objects. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_obj_get_properties *arg = data; - struct drm_mode_object *obj; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - - obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); - if (!obj) { - ret = -ENOENT; - goto out; - } - if (!obj->properties) { - ret = -EINVAL; - goto out; - } - - ret = get_properties(obj, file_priv->atomic, - (uint32_t __user *)(unsigned long)(arg->props_ptr), - (uint64_t __user *)(unsigned long)(arg->prop_values_ptr), - &arg->count_props); - -out: - drm_modeset_unlock_all(dev); - return ret; -} - -/** - * drm_mode_obj_set_property_ioctl - set the current value of an object's property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function sets the current value for an object's property. It also calls - * into a driver's ->set_property callback to update the hardware state. - * Compared to the connector specific ioctl this one is extended to also work on - * crtc and plane objects. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_obj_set_property *arg = data; - struct drm_mode_object *arg_obj; - struct drm_mode_object *prop_obj; - struct drm_property *property; - int i, ret = -EINVAL; - struct drm_mode_object *ref; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - - arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); - if (!arg_obj) { - ret = -ENOENT; - goto out; - } - if (!arg_obj->properties) - goto out; - - for (i = 0; i < arg_obj->properties->count; i++) - if (arg_obj->properties->properties[i]->base.id == arg->prop_id) - break; - - if (i == arg_obj->properties->count) - goto out; - - prop_obj = drm_mode_object_find(dev, arg->prop_id, - DRM_MODE_OBJECT_PROPERTY); - if (!prop_obj) { - ret = -ENOENT; - goto out; - } - property = obj_to_property(prop_obj); - - if (!drm_property_change_valid_get(property, arg->value, &ref)) - goto out; - - switch (arg_obj->type) { - case DRM_MODE_OBJECT_CONNECTOR: - ret = drm_mode_connector_set_obj_prop(arg_obj, property, - arg->value); - break; - case DRM_MODE_OBJECT_CRTC: - ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); - break; - case DRM_MODE_OBJECT_PLANE: - ret = drm_mode_plane_set_obj_prop(obj_to_plane(arg_obj), - property, arg->value); - break; - } - - drm_property_change_valid_put(property, ref); - -out: - drm_modeset_unlock_all(dev); - return ret; -} - -/** - * drm_mode_connector_attach_encoder - attach a connector to an encoder - * @connector: connector to attach - * @encoder: encoder to attach @connector to - * - * This function links up a connector to an encoder. Note that the routing - * restrictions between encoders and crtcs are exposed to userspace through the - * possible_clones and possible_crtcs bitmasks. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_connector_attach_encoder(struct drm_connector *connector, - struct drm_encoder *encoder) -{ - int i; - - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) { - connector->encoder_ids[i] = encoder->base.id; - return 0; - } - } - return -ENOMEM; -} -EXPORT_SYMBOL(drm_mode_connector_attach_encoder); - -/** - * drm_mode_crtc_set_gamma_size - set the gamma table size - * @crtc: CRTC to set the gamma table size for - * @gamma_size: size of the gamma table - * - * Drivers which support gamma tables should set this to the supported gamma - * table size when initializing the CRTC. Currently the drm core only supports a - * fixed gamma table size. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, - int gamma_size) -{ - crtc->gamma_size = gamma_size; - - crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3, - GFP_KERNEL); - if (!crtc->gamma_store) { - crtc->gamma_size = 0; - return -ENOMEM; - } - - return 0; -} -EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); - -/** - * drm_mode_gamma_set_ioctl - set the gamma table - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Set the gamma table of a CRTC to the one passed in by the user. Userspace can - * inquire the required gamma table size through drm_mode_gamma_get_ioctl. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_gamma_set_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_crtc_lut *crtc_lut = data; - struct drm_crtc *crtc; - void *r_base, *g_base, *b_base; - int size; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - crtc = drm_crtc_find(dev, crtc_lut->crtc_id); - if (!crtc) { - ret = -ENOENT; - goto out; - } - - if (crtc->funcs->gamma_set == NULL) { - ret = -ENOSYS; - goto out; - } - - /* memcpy into gamma store */ - if (crtc_lut->gamma_size != crtc->gamma_size) { - ret = -EINVAL; - goto out; - } - - size = crtc_lut->gamma_size * (sizeof(uint16_t)); - r_base = crtc->gamma_store; - if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { - ret = -EFAULT; - goto out; - } - - g_base = r_base + size; - if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { - ret = -EFAULT; - goto out; - } - - b_base = g_base + size; - if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { - ret = -EFAULT; - goto out; - } - - crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); - -out: - drm_modeset_unlock_all(dev); - return ret; - -} - -/** - * drm_mode_gamma_get_ioctl - get the gamma table - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Copy the current gamma table into the storage provided. This also provides - * the gamma table size the driver expects, which can be used to size the - * allocated storage. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_gamma_get_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_crtc_lut *crtc_lut = data; - struct drm_crtc *crtc; - void *r_base, *g_base, *b_base; - int size; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - crtc = drm_crtc_find(dev, crtc_lut->crtc_id); - if (!crtc) { - ret = -ENOENT; - goto out; - } - - /* memcpy into gamma store */ - if (crtc_lut->gamma_size != crtc->gamma_size) { - ret = -EINVAL; - goto out; - } - - size = crtc_lut->gamma_size * (sizeof(uint16_t)); - r_base = crtc->gamma_store; - if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { - ret = -EFAULT; - goto out; - } - - g_base = r_base + size; - if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { - ret = -EFAULT; - goto out; - } - - b_base = g_base + size; - if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { - ret = -EFAULT; - goto out; - } -out: - drm_modeset_unlock_all(dev); - return ret; -} - -/** - * drm_mode_page_flip_ioctl - schedule an asynchronous fb update - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This schedules an asynchronous update on a given CRTC, called page flip. - * Optionally a drm event is generated to signal the completion of the event. - * Generic drivers cannot assume that a pageflip with changed framebuffer - * properties (including driver specific metadata like tiling layout) will work, - * but some drivers support e.g. pixel format changes through the pageflip - * ioctl. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_page_flip_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_crtc_page_flip *page_flip = data; - struct drm_crtc *crtc; - struct drm_framebuffer *fb = NULL; - struct drm_pending_vblank_event *e = NULL; - unsigned long flags; - int ret = -EINVAL; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || - page_flip->reserved != 0) - return -EINVAL; - - if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) - return -EINVAL; - - crtc = drm_crtc_find(dev, page_flip->crtc_id); - if (!crtc) - return -ENOENT; - - drm_modeset_lock_crtc(crtc, crtc->primary); - if (crtc->primary->fb == NULL) { - /* The framebuffer is currently unbound, presumably - * due to a hotplug event, that userspace has not - * yet discovered. - */ - ret = -EBUSY; - goto out; - } - - if (crtc->funcs->page_flip == NULL) - goto out; - - fb = drm_framebuffer_lookup(dev, page_flip->fb_id); - if (!fb) { - ret = -ENOENT; - goto out; - } - - if (crtc->state) { - const struct drm_plane_state *state = crtc->primary->state; - - ret = check_src_coords(state->src_x, state->src_y, - state->src_w, state->src_h, fb); - } else { - ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); - } - if (ret) - goto out; - - if (crtc->primary->fb->pixel_format != fb->pixel_format) { - DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); - ret = -EINVAL; - goto out; - } - - if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { - ret = -ENOMEM; - spin_lock_irqsave(&dev->event_lock, flags); - if (file_priv->event_space < sizeof(e->event)) { - spin_unlock_irqrestore(&dev->event_lock, flags); - goto out; - } - file_priv->event_space -= sizeof(e->event); - spin_unlock_irqrestore(&dev->event_lock, flags); - - e = kzalloc(sizeof(*e), GFP_KERNEL); - if (e == NULL) { - spin_lock_irqsave(&dev->event_lock, flags); - file_priv->event_space += sizeof(e->event); - spin_unlock_irqrestore(&dev->event_lock, flags); - goto out; - } - - e->event.base.type = DRM_EVENT_FLIP_COMPLETE; - e->event.base.length = sizeof(e->event); - e->event.user_data = page_flip->user_data; - e->base.event = &e->event.base; - e->base.file_priv = file_priv; - e->base.destroy = - (void (*) (struct drm_pending_event *)) kfree; - } - - crtc->primary->old_fb = crtc->primary->fb; - ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags); - if (ret) { - if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { - spin_lock_irqsave(&dev->event_lock, flags); - file_priv->event_space += sizeof(e->event); - spin_unlock_irqrestore(&dev->event_lock, flags); - kfree(e); - } - /* Keep the old fb, don't unref it. */ - crtc->primary->old_fb = NULL; - } else { - crtc->primary->fb = fb; - /* Unref only the old framebuffer. */ - fb = NULL; - } - -out: - if (fb) - drm_framebuffer_unreference(fb); - if (crtc->primary->old_fb) - drm_framebuffer_unreference(crtc->primary->old_fb); - crtc->primary->old_fb = NULL; - drm_modeset_unlock_crtc(crtc); - - return ret; -} - -/** - * drm_mode_config_reset - call ->reset callbacks - * @dev: drm device - * - * This functions calls all the crtc's, encoder's and connector's ->reset - * callback. Drivers can use this in e.g. their driver load or resume code to - * reset hardware and software state. - */ -void drm_mode_config_reset(struct drm_device *dev) -{ - struct drm_crtc *crtc; - struct drm_plane *plane; - struct drm_encoder *encoder; - struct drm_connector *connector; - - drm_for_each_plane(plane, dev) - if (plane->funcs->reset) - plane->funcs->reset(plane); - - drm_for_each_crtc(crtc, dev) - if (crtc->funcs->reset) - crtc->funcs->reset(crtc); - - drm_for_each_encoder(encoder, dev) - if (encoder->funcs->reset) - encoder->funcs->reset(encoder); - - mutex_lock(&dev->mode_config.mutex); - drm_for_each_connector(connector, dev) - if (connector->funcs->reset) - connector->funcs->reset(connector); - mutex_unlock(&dev->mode_config.mutex); -} -EXPORT_SYMBOL(drm_mode_config_reset); - -/** - * drm_mode_create_dumb_ioctl - create a dumb backing storage buffer - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This creates a new dumb buffer in the driver's backing storage manager (GEM, - * TTM or something else entirely) and returns the resulting buffer handle. This - * handle can then be wrapped up into a framebuffer modeset object. - * - * Note that userspace is not allowed to use such objects for render - * acceleration - drivers must create their own private ioctls for such a use - * case. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_create_dumb_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_create_dumb *args = data; - u32 cpp, stride, size; - - if (!dev->driver->dumb_create) - return -ENOSYS; - if (!args->width || !args->height || !args->bpp) - return -EINVAL; - - /* overflow checks for 32bit size calculations */ - /* NOTE: DIV_ROUND_UP() can overflow */ - cpp = DIV_ROUND_UP(args->bpp, 8); - if (!cpp || cpp > 0xffffffffU / args->width) - return -EINVAL; - stride = cpp * args->width; - if (args->height > 0xffffffffU / stride) - return -EINVAL; - - /* test for wrap-around */ - size = args->height * stride; - if (PAGE_ALIGN(size) == 0) - return -EINVAL; - - /* - * handle, pitch and size are output parameters. Zero them out to - * prevent drivers from accidentally using uninitialized data. Since - * not all existing userspace is clearing these fields properly we - * cannot reject IOCTL with garbage in them. - */ - args->handle = 0; - args->pitch = 0; - args->size = 0; - - return dev->driver->dumb_create(file_priv, dev, args); -} - -/** - * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Allocate an offset in the drm device node's address space to be able to - * memory map a dumb buffer. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_map_dumb *args = data; - - /* call driver ioctl to get mmap offset */ - if (!dev->driver->dumb_map_offset) - return -ENOSYS; - - return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset); -} - -/** - * drm_mode_destroy_dumb_ioctl - destroy a dumb backing strage buffer - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This destroys the userspace handle for the given dumb backing storage buffer. - * Since buffer objects must be reference counted in the kernel a buffer object - * won't be immediately freed if a framebuffer modeset object still uses it. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_destroy_dumb *args = data; - - if (!dev->driver->dumb_destroy) - return -ENOSYS; - - return dev->driver->dumb_destroy(file_priv, dev, args->handle); -} - -/** - * drm_fb_get_bpp_depth - get the bpp/depth values for format - * @format: pixel format (DRM_FORMAT_*) - * @depth: storage for the depth value - * @bpp: storage for the bpp value - * - * This only supports RGB formats here for compat with code that doesn't use - * pixel formats directly yet. - */ -void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, - int *bpp) -{ - switch (format) { - case DRM_FORMAT_C8: - case DRM_FORMAT_RGB332: - case DRM_FORMAT_BGR233: - *depth = 8; - *bpp = 8; - break; - case DRM_FORMAT_XRGB1555: - case DRM_FORMAT_XBGR1555: - case DRM_FORMAT_RGBX5551: - case DRM_FORMAT_BGRX5551: - case DRM_FORMAT_ARGB1555: - case DRM_FORMAT_ABGR1555: - case DRM_FORMAT_RGBA5551: - case DRM_FORMAT_BGRA5551: - *depth = 15; - *bpp = 16; - break; - case DRM_FORMAT_RGB565: - case DRM_FORMAT_BGR565: - *depth = 16; - *bpp = 16; - break; - case DRM_FORMAT_RGB888: - case DRM_FORMAT_BGR888: - *depth = 24; - *bpp = 24; - break; - case DRM_FORMAT_XRGB8888: - case DRM_FORMAT_XBGR8888: - case DRM_FORMAT_RGBX8888: - case DRM_FORMAT_BGRX8888: - *depth = 24; - *bpp = 32; - break; - case DRM_FORMAT_XRGB2101010: - case DRM_FORMAT_XBGR2101010: - case DRM_FORMAT_RGBX1010102: - case DRM_FORMAT_BGRX1010102: - case DRM_FORMAT_ARGB2101010: - case DRM_FORMAT_ABGR2101010: - case DRM_FORMAT_RGBA1010102: - case DRM_FORMAT_BGRA1010102: - *depth = 30; - *bpp = 32; - break; - case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_ABGR8888: - case DRM_FORMAT_RGBA8888: - case DRM_FORMAT_BGRA8888: - *depth = 32; - *bpp = 32; - break; - default: - DRM_DEBUG_KMS("unsupported pixel format %s\n", - drm_get_format_name(format)); - *depth = 0; - *bpp = 0; - break; - } -} -EXPORT_SYMBOL(drm_fb_get_bpp_depth); - -/** - * drm_format_num_planes - get the number of planes for format - * @format: pixel format (DRM_FORMAT_*) - * - * Returns: - * The number of planes used by the specified pixel format. - */ -int drm_format_num_planes(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_YUV410: - case DRM_FORMAT_YVU410: - case DRM_FORMAT_YUV411: - case DRM_FORMAT_YVU411: - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YVU420: - case DRM_FORMAT_YUV422: - case DRM_FORMAT_YVU422: - case DRM_FORMAT_YUV444: - case DRM_FORMAT_YVU444: - return 3; - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV21: - case DRM_FORMAT_NV16: - case DRM_FORMAT_NV61: - case DRM_FORMAT_NV24: - case DRM_FORMAT_NV42: - return 2; - default: - return 1; - } -} -EXPORT_SYMBOL(drm_format_num_planes); - -/** - * drm_format_plane_cpp - determine the bytes per pixel value - * @format: pixel format (DRM_FORMAT_*) - * @plane: plane index - * - * Returns: - * The bytes per pixel value for the specified plane. - */ -int drm_format_plane_cpp(uint32_t format, int plane) -{ - unsigned int depth; - int bpp; - - if (plane >= drm_format_num_planes(format)) - return 0; - - switch (format) { - case DRM_FORMAT_YUYV: - case DRM_FORMAT_YVYU: - case DRM_FORMAT_UYVY: - case DRM_FORMAT_VYUY: - return 2; - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV21: - case DRM_FORMAT_NV16: - case DRM_FORMAT_NV61: - case DRM_FORMAT_NV24: - case DRM_FORMAT_NV42: - return plane ? 2 : 1; - case DRM_FORMAT_YUV410: - case DRM_FORMAT_YVU410: - case DRM_FORMAT_YUV411: - case DRM_FORMAT_YVU411: - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YVU420: - case DRM_FORMAT_YUV422: - case DRM_FORMAT_YVU422: - case DRM_FORMAT_YUV444: - case DRM_FORMAT_YVU444: - return 1; - default: - drm_fb_get_bpp_depth(format, &depth, &bpp); - return bpp >> 3; - } -} -EXPORT_SYMBOL(drm_format_plane_cpp); - -/** - * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor - * @format: pixel format (DRM_FORMAT_*) - * - * Returns: - * The horizontal chroma subsampling factor for the - * specified pixel format. - */ -int drm_format_horz_chroma_subsampling(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_YUV411: - case DRM_FORMAT_YVU411: - case DRM_FORMAT_YUV410: - case DRM_FORMAT_YVU410: - return 4; - case DRM_FORMAT_YUYV: - case DRM_FORMAT_YVYU: - case DRM_FORMAT_UYVY: - case DRM_FORMAT_VYUY: - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV21: - case DRM_FORMAT_NV16: - case DRM_FORMAT_NV61: - case DRM_FORMAT_YUV422: - case DRM_FORMAT_YVU422: - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YVU420: - return 2; - default: - return 1; - } -} -EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); - -/** - * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor - * @format: pixel format (DRM_FORMAT_*) - * - * Returns: - * The vertical chroma subsampling factor for the - * specified pixel format. - */ -int drm_format_vert_chroma_subsampling(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_YUV410: - case DRM_FORMAT_YVU410: - return 4; - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YVU420: - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV21: - return 2; - default: - return 1; - } -} -EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); - -/** - * drm_rotation_simplify() - Try to simplify the rotation - * @rotation: Rotation to be simplified - * @supported_rotations: Supported rotations - * - * Attempt to simplify the rotation to a form that is supported. - * Eg. if the hardware supports everything except DRM_REFLECT_X - * one could call this function like this: - * - * drm_rotation_simplify(rotation, BIT(DRM_ROTATE_0) | - * BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_180) | - * BIT(DRM_ROTATE_270) | BIT(DRM_REFLECT_Y)); - * - * to eliminate the DRM_ROTATE_X flag. Depending on what kind of - * transforms the hardware supports, this function may not - * be able to produce a supported transform, so the caller should - * check the result afterwards. - */ -unsigned int drm_rotation_simplify(unsigned int rotation, - unsigned int supported_rotations) -{ - if (rotation & ~supported_rotations) { - rotation ^= BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y); - rotation = (rotation & DRM_REFLECT_MASK) | - BIT((ffs(rotation & DRM_ROTATE_MASK) + 1) % 4); - } - - return rotation; -} -EXPORT_SYMBOL(drm_rotation_simplify); - -/** - * drm_mode_config_init - initialize DRM mode_configuration structure - * @dev: DRM device - * - * Initialize @dev's mode_config structure, used for tracking the graphics - * configuration of @dev. - * - * Since this initializes the modeset locks, no locking is possible. Which is no - * problem, since this should happen single threaded at init time. It is the - * driver's problem to ensure this guarantee. - * - */ -void drm_mode_config_init(struct drm_device *dev) -{ - mutex_init(&dev->mode_config.mutex); - drm_modeset_lock_init(&dev->mode_config.connection_mutex); - mutex_init(&dev->mode_config.idr_mutex); - mutex_init(&dev->mode_config.fb_lock); - mutex_init(&dev->mode_config.blob_lock); - INIT_LIST_HEAD(&dev->mode_config.fb_list); - INIT_LIST_HEAD(&dev->mode_config.crtc_list); - INIT_LIST_HEAD(&dev->mode_config.connector_list); - INIT_LIST_HEAD(&dev->mode_config.encoder_list); - INIT_LIST_HEAD(&dev->mode_config.property_list); - INIT_LIST_HEAD(&dev->mode_config.property_blob_list); - INIT_LIST_HEAD(&dev->mode_config.plane_list); - idr_init(&dev->mode_config.crtc_idr); - idr_init(&dev->mode_config.tile_idr); - - drm_modeset_lock_all(dev); - drm_mode_create_standard_properties(dev); - drm_modeset_unlock_all(dev); - - /* Just to be sure */ - dev->mode_config.num_fb = 0; - dev->mode_config.num_connector = 0; - dev->mode_config.num_crtc = 0; - dev->mode_config.num_encoder = 0; - dev->mode_config.num_overlay_plane = 0; - dev->mode_config.num_total_plane = 0; -} -EXPORT_SYMBOL(drm_mode_config_init); - -/** - * drm_mode_config_cleanup - free up DRM mode_config info - * @dev: DRM device - * - * Free up all the connectors and CRTCs associated with this DRM device, then - * free up the framebuffers and associated buffer objects. - * - * Note that since this /should/ happen single-threaded at driver/device - * teardown time, no locking is required. It's the driver's job to ensure that - * this guarantee actually holds true. - * - * FIXME: cleanup any dangling user buffer objects too - */ -void drm_mode_config_cleanup(struct drm_device *dev) -{ - struct drm_connector *connector, *ot; - struct drm_crtc *crtc, *ct; - struct drm_encoder *encoder, *enct; - struct drm_framebuffer *fb, *fbt; - struct drm_property *property, *pt; - struct drm_property_blob *blob, *bt; - struct drm_plane *plane, *plt; - - list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, - head) { - encoder->funcs->destroy(encoder); - } - - list_for_each_entry_safe(connector, ot, - &dev->mode_config.connector_list, head) { - connector->funcs->destroy(connector); - } - - list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, - head) { - drm_property_destroy(dev, property); - } - - list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list, - head_global) { - drm_property_unreference_blob(blob); - } - - /* - * Single-threaded teardown context, so it's not required to grab the - * fb_lock to protect against concurrent fb_list access. Contrary, it - * would actually deadlock with the drm_framebuffer_cleanup function. - * - * Also, if there are any framebuffers left, that's a driver leak now, - * so politely WARN about this. - */ - WARN_ON(!list_empty(&dev->mode_config.fb_list)); - list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { - drm_framebuffer_free(&fb->refcount); - } - - list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, - head) { - plane->funcs->destroy(plane); - } - - list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { - crtc->funcs->destroy(crtc); - } - - idr_destroy(&dev->mode_config.tile_idr); - idr_destroy(&dev->mode_config.crtc_idr); - drm_modeset_lock_fini(&dev->mode_config.connection_mutex); -} -EXPORT_SYMBOL(drm_mode_config_cleanup); - -struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, - unsigned int supported_rotations) -{ - static const struct drm_prop_enum_list props[] = { - { DRM_ROTATE_0, "rotate-0" }, - { DRM_ROTATE_90, "rotate-90" }, - { DRM_ROTATE_180, "rotate-180" }, - { DRM_ROTATE_270, "rotate-270" }, - { DRM_REFLECT_X, "reflect-x" }, - { DRM_REFLECT_Y, "reflect-y" }, - }; - - return drm_property_create_bitmask(dev, 0, "rotation", - props, ARRAY_SIZE(props), - supported_rotations); -} -EXPORT_SYMBOL(drm_mode_create_rotation_property); - -/** - * DOC: Tile group - * - * Tile groups are used to represent tiled monitors with a unique - * integer identifier. Tiled monitors using DisplayID v1.3 have - * a unique 8-byte handle, we store this in a tile group, so we - * have a common identifier for all tiles in a monitor group. - */ -static void drm_tile_group_free(struct kref *kref) -{ - struct drm_tile_group *tg = container_of(kref, struct drm_tile_group, refcount); - struct drm_device *dev = tg->dev; - mutex_lock(&dev->mode_config.idr_mutex); - idr_remove(&dev->mode_config.tile_idr, tg->id); - mutex_unlock(&dev->mode_config.idr_mutex); - kfree(tg); -} - -/** - * drm_mode_put_tile_group - drop a reference to a tile group. - * @dev: DRM device - * @tg: tile group to drop reference to. - * - * drop reference to tile group and free if 0. - */ -void drm_mode_put_tile_group(struct drm_device *dev, - struct drm_tile_group *tg) -{ - kref_put(&tg->refcount, drm_tile_group_free); -} - -/** - * drm_mode_get_tile_group - get a reference to an existing tile group - * @dev: DRM device - * @topology: 8-bytes unique per monitor. - * - * Use the unique bytes to get a reference to an existing tile group. - * - * RETURNS: - * tile group or NULL if not found. - */ -struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, - char topology[8]) -{ - struct drm_tile_group *tg; - int id; - mutex_lock(&dev->mode_config.idr_mutex); - idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) { - if (!memcmp(tg->group_data, topology, 8)) { - if (!kref_get_unless_zero(&tg->refcount)) - tg = NULL; - mutex_unlock(&dev->mode_config.idr_mutex); - return tg; - } - } - mutex_unlock(&dev->mode_config.idr_mutex); - return NULL; -} -EXPORT_SYMBOL(drm_mode_get_tile_group); - -/** - * drm_mode_create_tile_group - create a tile group from a displayid description - * @dev: DRM device - * @topology: 8-bytes unique per monitor. - * - * Create a tile group for the unique monitor, and get a unique - * identifier for the tile group. - * - * RETURNS: - * new tile group or error. - */ -struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, - char topology[8]) -{ - struct drm_tile_group *tg; - int ret; - - tg = kzalloc(sizeof(*tg), GFP_KERNEL); - if (!tg) - return ERR_PTR(-ENOMEM); - - kref_init(&tg->refcount); - memcpy(tg->group_data, topology, 8); - tg->dev = dev; - - mutex_lock(&dev->mode_config.idr_mutex); - ret = idr_alloc(&dev->mode_config.tile_idr, tg, 1, 0, GFP_KERNEL); - if (ret >= 0) { - tg->id = ret; - } else { - kfree(tg); - tg = ERR_PTR(ret); - } - - mutex_unlock(&dev->mode_config.idr_mutex); - return tg; -} -EXPORT_SYMBOL(drm_mode_create_tile_group); diff --git a/src/4.x/drivers/gpu/drm/drm_crtc_helper.c b/src/4.x/drivers/gpu/drm/drm_crtc_helper.c deleted file mode 100644 index ef534758a..000000000 --- a/src/4.x/drivers/gpu/drm/drm_crtc_helper.c +++ /dev/null @@ -1,1017 +0,0 @@ -/* - * Copyright (c) 2006-2008 Intel Corporation - * Copyright (c) 2007 Dave Airlie - * - * DRM core CRTC related functions - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that copyright - * notice and this permission notice appear in supporting documentation, and - * that the name of the copyright holders not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. The copyright holders make no representations - * about the suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - * - * Authors: - * Keith Packard - * Eric Anholt - * Dave Airlie - * Jesse Barnes - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * DOC: overview - * - * The CRTC modeset helper library provides a default set_config implementation - * in drm_crtc_helper_set_config(). Plus a few other convenience functions using - * the same callbacks which drivers can use to e.g. restore the modeset - * configuration on resume with drm_helper_resume_force_mode(). - * - * The driver callbacks are mostly compatible with the atomic modeset helpers, - * except for the handling of the primary plane: Atomic helpers require that the - * primary plane is implemented as a real standalone plane and not directly tied - * to the CRTC state. For easier transition this library provides functions to - * implement the old semantics required by the CRTC helpers using the new plane - * and atomic helper callbacks. - * - * Drivers are strongly urged to convert to the atomic helpers (by way of first - * converting to the plane helpers). New drivers must not use these functions - * but need to implement the atomic interface instead, potentially using the - * atomic helpers for that. - */ -MODULE_AUTHOR("David Airlie, Jesse Barnes"); -MODULE_DESCRIPTION("DRM KMS helper"); -MODULE_LICENSE("GPL and additional rights"); - -/** - * drm_helper_move_panel_connectors_to_head() - move panels to the front in the - * connector list - * @dev: drm device to operate on - * - * Some userspace presumes that the first connected connector is the main - * display, where it's supposed to display e.g. the login screen. For - * laptops, this should be the main panel. Use this function to sort all - * (eDP/LVDS) panels to the front of the connector list, instead of - * painstakingly trying to initialize them in the right order. - */ -void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) -{ - struct drm_connector *connector, *tmp; - struct list_head panel_list; - - INIT_LIST_HEAD(&panel_list); - - list_for_each_entry_safe(connector, tmp, - &dev->mode_config.connector_list, head) { - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || - connector->connector_type == DRM_MODE_CONNECTOR_eDP) - list_move_tail(&connector->head, &panel_list); - } - - list_splice(&panel_list, &dev->mode_config.connector_list); -} -EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head); - -/** - * drm_helper_encoder_in_use - check if a given encoder is in use - * @encoder: encoder to check - * - * Checks whether @encoder is with the current mode setting output configuration - * in use by any connector. This doesn't mean that it is actually enabled since - * the DPMS state is tracked separately. - * - * Returns: - * True if @encoder is used, false otherwise. - */ -bool drm_helper_encoder_in_use(struct drm_encoder *encoder) -{ - struct drm_connector *connector; - struct drm_device *dev = encoder->dev; - - /* - * We can expect this mutex to be locked if we are not panicking. - * Locking is currently fubar in the panic handler. - */ - if (!oops_in_progress) { - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); - WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); - } - - drm_for_each_connector(connector, dev) - if (connector->encoder == encoder) - return true; - return false; -} -EXPORT_SYMBOL(drm_helper_encoder_in_use); - -/** - * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config - * @crtc: CRTC to check - * - * Checks whether @crtc is with the current mode setting output configuration - * in use by any connector. This doesn't mean that it is actually enabled since - * the DPMS state is tracked separately. - * - * Returns: - * True if @crtc is used, false otherwise. - */ -bool drm_helper_crtc_in_use(struct drm_crtc *crtc) -{ - struct drm_encoder *encoder; - struct drm_device *dev = crtc->dev; - - /* - * We can expect this mutex to be locked if we are not panicking. - * Locking is currently fubar in the panic handler. - */ - if (!oops_in_progress) - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); - - drm_for_each_encoder(encoder, dev) - if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) - return true; - return false; -} -EXPORT_SYMBOL(drm_helper_crtc_in_use); - -static void -drm_encoder_disable(struct drm_encoder *encoder) -{ - const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; - - drm_bridge_disable(encoder->bridge); - - if (encoder_funcs->disable) - (*encoder_funcs->disable)(encoder); - else - (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); - - drm_bridge_post_disable(encoder->bridge); -} - -static void __drm_helper_disable_unused_functions(struct drm_device *dev) -{ - struct drm_encoder *encoder; - struct drm_crtc *crtc; - - drm_warn_on_modeset_not_all_locked(dev); - - drm_for_each_encoder(encoder, dev) { - if (!drm_helper_encoder_in_use(encoder)) { - drm_encoder_disable(encoder); - /* disconnect encoder from any connector */ - encoder->crtc = NULL; - } - } - - drm_for_each_crtc(crtc, dev) { - const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc->enabled = drm_helper_crtc_in_use(crtc); - if (!crtc->enabled) { - if (crtc_funcs->disable) - (*crtc_funcs->disable)(crtc); - else - (*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF); - crtc->primary->fb = NULL; - } - } -} - -/** - * drm_helper_disable_unused_functions - disable unused objects - * @dev: DRM device - * - * This function walks through the entire mode setting configuration of @dev. It - * will remove any crtc links of unused encoders and encoder links of - * disconnected connectors. Then it will disable all unused encoders and crtcs - * either by calling their disable callback if available or by calling their - * dpms callback with DRM_MODE_DPMS_OFF. - */ -void drm_helper_disable_unused_functions(struct drm_device *dev) -{ - drm_modeset_lock_all(dev); - __drm_helper_disable_unused_functions(dev); - drm_modeset_unlock_all(dev); -} -EXPORT_SYMBOL(drm_helper_disable_unused_functions); - -/* - * Check the CRTC we're going to map each output to vs. its current - * CRTC. If they don't match, we have to disable the output and the CRTC - * since the driver will have to re-route things. - */ -static void -drm_crtc_prepare_encoders(struct drm_device *dev) -{ - const struct drm_encoder_helper_funcs *encoder_funcs; - struct drm_encoder *encoder; - - drm_for_each_encoder(encoder, dev) { - encoder_funcs = encoder->helper_private; - /* Disable unused encoders */ - if (encoder->crtc == NULL) - drm_encoder_disable(encoder); - /* Disable encoders whose CRTC is about to change */ - if (encoder_funcs->get_crtc && - encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) - drm_encoder_disable(encoder); - } -} - -/** - * drm_crtc_helper_set_mode - internal helper to set a mode - * @crtc: CRTC to program - * @mode: mode to use - * @x: horizontal offset into the surface - * @y: vertical offset into the surface - * @old_fb: old framebuffer, for cleanup - * - * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance - * to fixup or reject the mode prior to trying to set it. This is an internal - * helper that drivers could e.g. use to update properties that require the - * entire output pipe to be disabled and re-enabled in a new configuration. For - * example for changing whether audio is enabled on a hdmi link or for changing - * panel fitter or dither attributes. It is also called by the - * drm_crtc_helper_set_config() helper function to drive the mode setting - * sequence. - * - * Returns: - * True if the mode was set successfully, false otherwise. - */ -bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, - struct drm_display_mode *mode, - int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; - const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - const struct drm_encoder_helper_funcs *encoder_funcs; - int saved_x, saved_y; - bool saved_enabled; - struct drm_encoder *encoder; - bool ret = true; - - drm_warn_on_modeset_not_all_locked(dev); - - saved_enabled = crtc->enabled; - crtc->enabled = drm_helper_crtc_in_use(crtc); - if (!crtc->enabled) - return true; - - adjusted_mode = drm_mode_duplicate(dev, mode); - if (!adjusted_mode) { - crtc->enabled = saved_enabled; - return false; - } - - saved_mode = crtc->mode; - saved_hwmode = crtc->hwmode; - saved_x = crtc->x; - saved_y = crtc->y; - - /* Update crtc values up front so the driver can rely on them for mode - * setting. - */ - crtc->mode = *mode; - crtc->x = x; - crtc->y = y; - - /* Pass our mode to the connectors and the CRTC to give them a chance to - * adjust it according to limitations or connector properties, and also - * a chance to reject the mode entirely. - */ - drm_for_each_encoder(encoder, dev) { - - if (encoder->crtc != crtc) - continue; - - ret = drm_bridge_mode_fixup(encoder->bridge, - mode, adjusted_mode); - if (!ret) { - DRM_DEBUG_KMS("Bridge fixup failed\n"); - goto done; - } - - encoder_funcs = encoder->helper_private; - if (!(ret = encoder_funcs->mode_fixup(encoder, mode, - adjusted_mode))) { - DRM_DEBUG_KMS("Encoder fixup failed\n"); - goto done; - } - } - - if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { - DRM_DEBUG_KMS("CRTC fixup failed\n"); - goto done; - } - DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); - - crtc->hwmode = *adjusted_mode; - - /* Prepare the encoders and CRTCs before setting the mode. */ - drm_for_each_encoder(encoder, dev) { - - if (encoder->crtc != crtc) - continue; - - drm_bridge_disable(encoder->bridge); - - encoder_funcs = encoder->helper_private; - /* Disable the encoders as the first thing we do. */ - encoder_funcs->prepare(encoder); - - drm_bridge_post_disable(encoder->bridge); - } - - drm_crtc_prepare_encoders(dev); - - crtc_funcs->prepare(crtc); - - /* Set up the DPLL and any encoders state that needs to adjust or depend - * on the DPLL. - */ - ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); - if (!ret) - goto done; - - drm_for_each_encoder(encoder, dev) { - - if (encoder->crtc != crtc) - continue; - - DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n", - encoder->base.id, encoder->name, - mode->base.id, mode->name); - encoder_funcs = encoder->helper_private; - encoder_funcs->mode_set(encoder, mode, adjusted_mode); - - drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode); - } - - /* Now enable the clocks, plane, pipe, and connectors that we set up. */ - crtc_funcs->commit(crtc); - - drm_for_each_encoder(encoder, dev) { - - if (encoder->crtc != crtc) - continue; - - drm_bridge_pre_enable(encoder->bridge); - - encoder_funcs = encoder->helper_private; - encoder_funcs->commit(encoder); - - drm_bridge_enable(encoder->bridge); - } - - /* Calculate and store various constants which - * are later needed by vblank and swap-completion - * timestamping. They are derived from true hwmode. - */ - drm_calc_timestamping_constants(crtc, &crtc->hwmode); - - /* FIXME: add subpixel order */ -done: - drm_mode_destroy(dev, adjusted_mode); - if (!ret) { - crtc->enabled = saved_enabled; - crtc->mode = saved_mode; - crtc->hwmode = saved_hwmode; - crtc->x = saved_x; - crtc->y = saved_y; - } - - return ret; -} -EXPORT_SYMBOL(drm_crtc_helper_set_mode); - -static void -drm_crtc_helper_disable(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_connector *connector; - struct drm_encoder *encoder; - - /* Decouple all encoders and their attached connectors from this crtc */ - drm_for_each_encoder(encoder, dev) { - if (encoder->crtc != crtc) - continue; - - drm_for_each_connector(connector, dev) { - if (connector->encoder != encoder) - continue; - - connector->encoder = NULL; - - /* - * drm_helper_disable_unused_functions() ought to be - * doing this, but since we've decoupled the encoder - * from the connector above, the required connection - * between them is henceforth no longer available. - */ - connector->dpms = DRM_MODE_DPMS_OFF; - } - } - - __drm_helper_disable_unused_functions(dev); -} - -/** - * drm_crtc_helper_set_config - set a new config from userspace - * @set: mode set configuration - * - * Setup a new configuration, provided by the upper layers (either an ioctl call - * from userspace or internally e.g. from the fbdev support code) in @set, and - * enable it. This is the main helper functions for drivers that implement - * kernel mode setting with the crtc helper functions and the assorted - * ->prepare(), ->modeset() and ->commit() helper callbacks. - * - * Returns: - * Returns 0 on success, negative errno numbers on failure. - */ -int drm_crtc_helper_set_config(struct drm_mode_set *set) -{ - struct drm_device *dev; - struct drm_crtc *new_crtc; - struct drm_encoder *save_encoders, *new_encoder, *encoder; - bool mode_changed = false; /* if true do a full mode set */ - bool fb_changed = false; /* if true and !mode_changed just do a flip */ - struct drm_connector *save_connectors, *connector; - int count = 0, ro, fail = 0; - const struct drm_crtc_helper_funcs *crtc_funcs; - struct drm_mode_set save_set; - int ret; - int i; - - DRM_DEBUG_KMS("\n"); - - BUG_ON(!set); - BUG_ON(!set->crtc); - BUG_ON(!set->crtc->helper_private); - - /* Enforce sane interface api - has been abused by the fb helper. */ - BUG_ON(!set->mode && set->fb); - BUG_ON(set->fb && set->num_connectors == 0); - - crtc_funcs = set->crtc->helper_private; - - if (!set->mode) - set->fb = NULL; - - if (set->fb) { - DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", - set->crtc->base.id, set->fb->base.id, - (int)set->num_connectors, set->x, set->y); - } else { - DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); - drm_crtc_helper_disable(set->crtc); - return 0; - } - - dev = set->crtc->dev; - - drm_warn_on_modeset_not_all_locked(dev); - - /* - * Allocate space for the backup of all (non-pointer) encoder and - * connector data. - */ - save_encoders = kzalloc(dev->mode_config.num_encoder * - sizeof(struct drm_encoder), GFP_KERNEL); - if (!save_encoders) - return -ENOMEM; - - save_connectors = kzalloc(dev->mode_config.num_connector * - sizeof(struct drm_connector), GFP_KERNEL); - if (!save_connectors) { - kfree(save_encoders); - return -ENOMEM; - } - - /* - * Copy data. Note that driver private data is not affected. - * Should anything bad happen only the expected state is - * restored, not the drivers personal bookkeeping. - */ - count = 0; - drm_for_each_encoder(encoder, dev) { - save_encoders[count++] = *encoder; - } - - count = 0; - drm_for_each_connector(connector, dev) { - save_connectors[count++] = *connector; - } - - save_set.crtc = set->crtc; - save_set.mode = &set->crtc->mode; - save_set.x = set->crtc->x; - save_set.y = set->crtc->y; - save_set.fb = set->crtc->primary->fb; - - /* We should be able to check here if the fb has the same properties - * and then just flip_or_move it */ - if (set->crtc->primary->fb != set->fb) { - /* If we have no fb then treat it as a full mode set */ - if (set->crtc->primary->fb == NULL) { - DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); - mode_changed = true; - } else if (set->fb == NULL) { - mode_changed = true; - } else if (set->fb->pixel_format != - set->crtc->primary->fb->pixel_format) { - mode_changed = true; - } else - fb_changed = true; - } - - if (set->x != set->crtc->x || set->y != set->crtc->y) - fb_changed = true; - - if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { - DRM_DEBUG_KMS("modes are different, full mode set\n"); - drm_mode_debug_printmodeline(&set->crtc->mode); - drm_mode_debug_printmodeline(set->mode); - mode_changed = true; - } - - /* a) traverse passed in connector list and get encoders for them */ - count = 0; - drm_for_each_connector(connector, dev) { - const struct drm_connector_helper_funcs *connector_funcs = - connector->helper_private; - new_encoder = connector->encoder; - for (ro = 0; ro < set->num_connectors; ro++) { - if (set->connectors[ro] == connector) { - new_encoder = connector_funcs->best_encoder(connector); - /* if we can't get an encoder for a connector - we are setting now - then fail */ - if (new_encoder == NULL) - /* don't break so fail path works correct */ - fail = 1; - - if (connector->dpms != DRM_MODE_DPMS_ON) { - DRM_DEBUG_KMS("connector dpms not on, full mode switch\n"); - mode_changed = true; - } - - break; - } - } - - if (new_encoder != connector->encoder) { - DRM_DEBUG_KMS("encoder changed, full mode switch\n"); - mode_changed = true; - /* If the encoder is reused for another connector, then - * the appropriate crtc will be set later. - */ - if (connector->encoder) - connector->encoder->crtc = NULL; - connector->encoder = new_encoder; - } - } - - if (fail) { - ret = -EINVAL; - goto fail; - } - - count = 0; - drm_for_each_connector(connector, dev) { - if (!connector->encoder) - continue; - - if (connector->encoder->crtc == set->crtc) - new_crtc = NULL; - else - new_crtc = connector->encoder->crtc; - - for (ro = 0; ro < set->num_connectors; ro++) { - if (set->connectors[ro] == connector) - new_crtc = set->crtc; - } - - /* Make sure the new CRTC will work with the encoder */ - if (new_crtc && - !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { - ret = -EINVAL; - goto fail; - } - if (new_crtc != connector->encoder->crtc) { - DRM_DEBUG_KMS("crtc changed, full mode switch\n"); - mode_changed = true; - connector->encoder->crtc = new_crtc; - } - if (new_crtc) { - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", - connector->base.id, connector->name, - new_crtc->base.id); - } else { - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", - connector->base.id, connector->name); - } - } - - /* mode_set_base is not a required function */ - if (fb_changed && !crtc_funcs->mode_set_base) - mode_changed = true; - - if (mode_changed) { - if (drm_helper_crtc_in_use(set->crtc)) { - DRM_DEBUG_KMS("attempting to set mode from" - " userspace\n"); - drm_mode_debug_printmodeline(set->mode); - set->crtc->primary->fb = set->fb; - if (!drm_crtc_helper_set_mode(set->crtc, set->mode, - set->x, set->y, - save_set.fb)) { - DRM_ERROR("failed to set mode on [CRTC:%d]\n", - set->crtc->base.id); - set->crtc->primary->fb = save_set.fb; - ret = -EINVAL; - goto fail; - } - DRM_DEBUG_KMS("Setting connector DPMS state to on\n"); - for (i = 0; i < set->num_connectors; i++) { - DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id, - set->connectors[i]->name); - set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON); - } - } - __drm_helper_disable_unused_functions(dev); - } else if (fb_changed) { - set->crtc->x = set->x; - set->crtc->y = set->y; - set->crtc->primary->fb = set->fb; - ret = crtc_funcs->mode_set_base(set->crtc, - set->x, set->y, save_set.fb); - if (ret != 0) { - set->crtc->x = save_set.x; - set->crtc->y = save_set.y; - set->crtc->primary->fb = save_set.fb; - goto fail; - } - } - - kfree(save_connectors); - kfree(save_encoders); - return 0; - -fail: - /* Restore all previous data. */ - count = 0; - drm_for_each_encoder(encoder, dev) { - *encoder = save_encoders[count++]; - } - - count = 0; - drm_for_each_connector(connector, dev) { - *connector = save_connectors[count++]; - } - - /* Try to restore the config */ - if (mode_changed && - !drm_crtc_helper_set_mode(save_set.crtc, save_set.mode, save_set.x, - save_set.y, save_set.fb)) - DRM_ERROR("failed to restore config after modeset failure\n"); - - kfree(save_connectors); - kfree(save_encoders); - return ret; -} -EXPORT_SYMBOL(drm_crtc_helper_set_config); - -static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) -{ - int dpms = DRM_MODE_DPMS_OFF; - struct drm_connector *connector; - struct drm_device *dev = encoder->dev; - - drm_for_each_connector(connector, dev) - if (connector->encoder == encoder) - if (connector->dpms < dpms) - dpms = connector->dpms; - return dpms; -} - -/* Helper which handles bridge ordering around encoder dpms */ -static void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode) -{ - struct drm_bridge *bridge = encoder->bridge; - const struct drm_encoder_helper_funcs *encoder_funcs; - - if (mode == DRM_MODE_DPMS_ON) - drm_bridge_pre_enable(bridge); - else - drm_bridge_disable(bridge); - - encoder_funcs = encoder->helper_private; - if (encoder_funcs->dpms) - encoder_funcs->dpms(encoder, mode); - - if (mode == DRM_MODE_DPMS_ON) - drm_bridge_enable(bridge); - else - drm_bridge_post_disable(bridge); -} - -static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) -{ - int dpms = DRM_MODE_DPMS_OFF; - struct drm_connector *connector; - struct drm_device *dev = crtc->dev; - - drm_for_each_connector(connector, dev) - if (connector->encoder && connector->encoder->crtc == crtc) - if (connector->dpms < dpms) - dpms = connector->dpms; - return dpms; -} - -/** - * drm_helper_connector_dpms() - connector dpms helper implementation - * @connector: affected connector - * @mode: DPMS mode - * - * This is the main helper function provided by the crtc helper framework for - * implementing the DPMS connector attribute. It computes the new desired DPMS - * state for all encoders and crtcs in the output mesh and calls the ->dpms() - * callback provided by the driver appropriately. - * - * Returns: - * Always returns 0. - */ -int drm_helper_connector_dpms(struct drm_connector *connector, int mode) -{ - struct drm_encoder *encoder = connector->encoder; - struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; - int old_dpms, encoder_dpms = DRM_MODE_DPMS_OFF; - - if (mode == connector->dpms) - return 0; - - old_dpms = connector->dpms; - connector->dpms = mode; - - if (encoder) - encoder_dpms = drm_helper_choose_encoder_dpms(encoder); - - /* from off to on, do crtc then encoder */ - if (mode < old_dpms) { - if (crtc) { - const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - if (crtc_funcs->dpms) - (*crtc_funcs->dpms) (crtc, - drm_helper_choose_crtc_dpms(crtc)); - } - if (encoder) - drm_helper_encoder_dpms(encoder, encoder_dpms); - } - - /* from on to off, do encoder then crtc */ - if (mode > old_dpms) { - if (encoder) - drm_helper_encoder_dpms(encoder, encoder_dpms); - if (crtc) { - const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - if (crtc_funcs->dpms) - (*crtc_funcs->dpms) (crtc, - drm_helper_choose_crtc_dpms(crtc)); - } - } - - return 0; -} -EXPORT_SYMBOL(drm_helper_connector_dpms); - -/** - * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata - * @fb: drm_framebuffer object to fill out - * @mode_cmd: metadata from the userspace fb creation request - * - * This helper can be used in a drivers fb_create callback to pre-fill the fb's - * metadata fields. - */ -void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, - struct drm_mode_fb_cmd2 *mode_cmd) -{ - int i; - - fb->width = mode_cmd->width; - fb->height = mode_cmd->height; - for (i = 0; i < 4; i++) { - fb->pitches[i] = mode_cmd->pitches[i]; - fb->offsets[i] = mode_cmd->offsets[i]; - fb->modifier[i] = mode_cmd->modifier[i]; - } - drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth, - &fb->bits_per_pixel); - fb->pixel_format = mode_cmd->pixel_format; - fb->flags = mode_cmd->flags; -} -EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); - -/** - * drm_helper_resume_force_mode - force-restore mode setting configuration - * @dev: drm_device which should be restored - * - * Drivers which use the mode setting helpers can use this function to - * force-restore the mode setting configuration e.g. on resume or when something - * else might have trampled over the hw state (like some overzealous old BIOSen - * tended to do). - * - * This helper doesn't provide a error return value since restoring the old - * config should never fail due to resource allocation issues since the driver - * has successfully set the restored configuration already. Hence this should - * boil down to the equivalent of a few dpms on calls, which also don't provide - * an error code. - * - * Drivers where simply restoring an old configuration again might fail (e.g. - * due to slight differences in allocating shared resources when the - * configuration is restored in a different order than when userspace set it up) - * need to use their own restore logic. - */ -void drm_helper_resume_force_mode(struct drm_device *dev) -{ - struct drm_crtc *crtc; - struct drm_encoder *encoder; - const struct drm_crtc_helper_funcs *crtc_funcs; - int encoder_dpms; - bool ret; - - drm_modeset_lock_all(dev); - drm_for_each_crtc(crtc, dev) { - - if (!crtc->enabled) - continue; - - ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->primary->fb); - - /* Restoring the old config should never fail! */ - if (ret == false) - DRM_ERROR("failed to set mode on crtc %p\n", crtc); - - /* Turn off outputs that were already powered off */ - if (drm_helper_choose_crtc_dpms(crtc)) { - drm_for_each_encoder(encoder, dev) { - - if(encoder->crtc != crtc) - continue; - - encoder_dpms = drm_helper_choose_encoder_dpms( - encoder); - - drm_helper_encoder_dpms(encoder, encoder_dpms); - } - - crtc_funcs = crtc->helper_private; - if (crtc_funcs->dpms) - (*crtc_funcs->dpms) (crtc, - drm_helper_choose_crtc_dpms(crtc)); - } - } - - /* disable the unused connectors while restoring the modesetting */ - __drm_helper_disable_unused_functions(dev); - drm_modeset_unlock_all(dev); -} -EXPORT_SYMBOL(drm_helper_resume_force_mode); - -/** - * drm_helper_crtc_mode_set - mode_set implementation for atomic plane helpers - * @crtc: DRM CRTC - * @mode: DRM display mode which userspace requested - * @adjusted_mode: DRM display mode adjusted by ->mode_fixup callbacks - * @x: x offset of the CRTC scanout area on the underlying framebuffer - * @y: y offset of the CRTC scanout area on the underlying framebuffer - * @old_fb: previous framebuffer - * - * This function implements a callback useable as the ->mode_set callback - * required by the crtc helpers. Besides the atomic plane helper functions for - * the primary plane the driver must also provide the ->mode_set_nofb callback - * to set up the crtc. - * - * This is a transitional helper useful for converting drivers to the atomic - * interfaces. - */ -int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_crtc_state *crtc_state; - const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - int ret; - - if (crtc->funcs->atomic_duplicate_state) - crtc_state = crtc->funcs->atomic_duplicate_state(crtc); - else { - if (!crtc->state) - drm_atomic_helper_crtc_reset(crtc); - - crtc_state = drm_atomic_helper_crtc_duplicate_state(crtc); - } - - if (!crtc_state) - return -ENOMEM; - - crtc_state->planes_changed = true; - crtc_state->mode_changed = true; - ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); - if (ret) - goto out; - drm_mode_copy(&crtc_state->adjusted_mode, adjusted_mode); - - if (crtc_funcs->atomic_check) { - ret = crtc_funcs->atomic_check(crtc, crtc_state); - if (ret) - goto out; - } - - swap(crtc->state, crtc_state); - - crtc_funcs->mode_set_nofb(crtc); - - ret = drm_helper_crtc_mode_set_base(crtc, x, y, old_fb); - -out: - if (crtc_state) { - if (crtc->funcs->atomic_destroy_state) - crtc->funcs->atomic_destroy_state(crtc, crtc_state); - else - drm_atomic_helper_crtc_destroy_state(crtc, crtc_state); - } - - return ret; -} -EXPORT_SYMBOL(drm_helper_crtc_mode_set); - -/** - * drm_helper_crtc_mode_set_base - mode_set_base implementation for atomic plane helpers - * @crtc: DRM CRTC - * @x: x offset of the CRTC scanout area on the underlying framebuffer - * @y: y offset of the CRTC scanout area on the underlying framebuffer - * @old_fb: previous framebuffer - * - * This function implements a callback useable as the ->mode_set_base used - * required by the crtc helpers. The driver must provide the atomic plane helper - * functions for the primary plane. - * - * This is a transitional helper useful for converting drivers to the atomic - * interfaces. - */ -int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_plane_state *plane_state; - struct drm_plane *plane = crtc->primary; - - if (plane->funcs->atomic_duplicate_state) - plane_state = plane->funcs->atomic_duplicate_state(plane); - else if (plane->state) - plane_state = drm_atomic_helper_plane_duplicate_state(plane); - else - plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL); - if (!plane_state) - return -ENOMEM; - plane_state->plane = plane; - - plane_state->crtc = crtc; - drm_atomic_set_fb_for_plane(plane_state, crtc->primary->fb); - plane_state->crtc_x = 0; - plane_state->crtc_y = 0; - plane_state->crtc_h = crtc->mode.vdisplay; - plane_state->crtc_w = crtc->mode.hdisplay; - plane_state->src_x = x << 16; - plane_state->src_y = y << 16; - plane_state->src_h = crtc->mode.vdisplay << 16; - plane_state->src_w = crtc->mode.hdisplay << 16; - - return drm_plane_helper_commit(plane, plane_state, old_fb); -} -EXPORT_SYMBOL(drm_helper_crtc_mode_set_base); diff --git a/src/4.x/drivers/gpu/drm/drm_crtc_internal.h b/src/4.x/drivers/gpu/drm/drm_crtc_internal.h deleted file mode 100644 index 247dc8b62..000000000 --- a/src/4.x/drivers/gpu/drm/drm_crtc_internal.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2006 Keith Packard - * Copyright © 2007-2008 Dave Airlie - * Copyright © 2007-2008 Intel Corporation - * Jesse Barnes - * Copyright © 2014 Intel Corporation - * Daniel Vetter - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * This header file contains mode setting related functions and definitions - * which are only used within the drm module as internal implementation details - * and are not exported to drivers. - */ - -int drm_mode_object_get(struct drm_device *dev, - struct drm_mode_object *obj, uint32_t obj_type); -void drm_mode_object_put(struct drm_device *dev, - struct drm_mode_object *object); - -/* drm_atomic.c */ -int drm_atomic_get_property(struct drm_mode_object *obj, - struct drm_property *property, uint64_t *val); -int drm_mode_atomic_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv); - diff --git a/src/4.x/drivers/gpu/drm/drm_debugfs.c b/src/4.x/drivers/gpu/drm/drm_debugfs.c deleted file mode 100644 index 3bcf8e6a8..000000000 --- a/src/4.x/drivers/gpu/drm/drm_debugfs.c +++ /dev/null @@ -1,422 +0,0 @@ -/** - * \file drm_debugfs.c - * debugfs support for DRM - * - * \author Ben Gamari - */ - -/* - * Created: Sun Dec 21 13:08:50 2008 by bgamari@gmail.com - * - * Copyright 2008 Ben Gamari - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include "drm_internal.h" - -#if defined(CONFIG_DEBUG_FS) - -/*************************************************** - * Initialization, etc. - **************************************************/ - -static const struct drm_info_list drm_debugfs_list[] = { - {"name", drm_name_info, 0}, - {"vm", drm_vm_info, 0}, - {"clients", drm_clients_info, 0}, - {"bufs", drm_bufs_info, 0}, - {"gem_names", drm_gem_name_info, DRIVER_GEM}, - {"vma", drm_vma_info, 0}, -}; -#define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list) - - -static int drm_debugfs_open(struct inode *inode, struct file *file) -{ - struct drm_info_node *node = inode->i_private; - - return single_open(file, node->info_ent->show, node); -} - - -static const struct file_operations drm_debugfs_fops = { - .owner = THIS_MODULE, - .open = drm_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - - -/** - * Initialize a given set of debugfs files for a device - * - * \param files The array of files to create - * \param count The number of files given - * \param root DRI debugfs dir entry. - * \param minor device minor number - * \return Zero on success, non-zero on failure - * - * Create a given set of debugfs files represented by an array of - * gdm_debugfs_lists in the given root directory. - */ -int drm_debugfs_create_files(const struct drm_info_list *files, int count, - struct dentry *root, struct drm_minor *minor) -{ - struct drm_device *dev = minor->dev; - struct dentry *ent; - struct drm_info_node *tmp; - int i, ret; - - for (i = 0; i < count; i++) { - u32 features = files[i].driver_features; - - if (features != 0 && - (dev->driver->driver_features & features) != features) - continue; - - tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL); - if (tmp == NULL) { - ret = -1; - goto fail; - } - ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO, - root, tmp, &drm_debugfs_fops); - if (!ent) { - DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/%s\n", - root->d_name.name, files[i].name); - kfree(tmp); - ret = -1; - goto fail; - } - - tmp->minor = minor; - tmp->dent = ent; - tmp->info_ent = &files[i]; - - mutex_lock(&minor->debugfs_lock); - list_add(&tmp->list, &minor->debugfs_list); - mutex_unlock(&minor->debugfs_lock); - } - return 0; - -fail: - drm_debugfs_remove_files(files, count, minor); - return ret; -} -EXPORT_SYMBOL(drm_debugfs_create_files); - -/** - * Initialize the DRI debugfs filesystem for a device - * - * \param dev DRM device - * \param minor device minor number - * \param root DRI debugfs dir entry. - * - * Create the DRI debugfs root entry "/sys/kernel/debug/dri", the device debugfs root entry - * "/sys/kernel/debug/dri/%minor%/", and each entry in debugfs_list as - * "/sys/kernel/debug/dri/%minor%/%name%". - */ -int drm_debugfs_init(struct drm_minor *minor, int minor_id, - struct dentry *root) -{ - struct drm_device *dev = minor->dev; - char name[64]; - int ret; - - INIT_LIST_HEAD(&minor->debugfs_list); - mutex_init(&minor->debugfs_lock); - sprintf(name, "%d", minor_id); - minor->debugfs_root = debugfs_create_dir(name, root); - if (!minor->debugfs_root) { - DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s\n", name); - return -1; - } - - ret = drm_debugfs_create_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES, - minor->debugfs_root, minor); - if (ret) { - debugfs_remove(minor->debugfs_root); - minor->debugfs_root = NULL; - DRM_ERROR("Failed to create core drm debugfs files\n"); - return ret; - } - - if (dev->driver->debugfs_init) { - ret = dev->driver->debugfs_init(minor); - if (ret) { - DRM_ERROR("DRM: Driver failed to initialize " - "/sys/kernel/debug/dri.\n"); - return ret; - } - } - return 0; -} - - -/** - * Remove a list of debugfs files - * - * \param files The list of files - * \param count The number of files - * \param minor The minor of which we should remove the files - * \return always zero. - * - * Remove all debugfs entries created by debugfs_init(). - */ -int drm_debugfs_remove_files(const struct drm_info_list *files, int count, - struct drm_minor *minor) -{ - struct list_head *pos, *q; - struct drm_info_node *tmp; - int i; - - mutex_lock(&minor->debugfs_lock); - for (i = 0; i < count; i++) { - list_for_each_safe(pos, q, &minor->debugfs_list) { - tmp = list_entry(pos, struct drm_info_node, list); - if (tmp->info_ent == &files[i]) { - debugfs_remove(tmp->dent); - list_del(pos); - kfree(tmp); - } - } - } - mutex_unlock(&minor->debugfs_lock); - return 0; -} -EXPORT_SYMBOL(drm_debugfs_remove_files); - -/** - * Cleanup the debugfs filesystem resources. - * - * \param minor device minor number. - * \return always zero. - * - * Remove all debugfs entries created by debugfs_init(). - */ -int drm_debugfs_cleanup(struct drm_minor *minor) -{ - struct drm_device *dev = minor->dev; - - if (!minor->debugfs_root) - return 0; - - if (dev->driver->debugfs_cleanup) - dev->driver->debugfs_cleanup(minor); - - drm_debugfs_remove_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES, minor); - - debugfs_remove(minor->debugfs_root); - minor->debugfs_root = NULL; - - return 0; -} - -static int connector_show(struct seq_file *m, void *data) -{ - struct drm_connector *connector = m->private; - const char *status; - - switch (connector->force) { - case DRM_FORCE_ON: - status = "on\n"; - break; - - case DRM_FORCE_ON_DIGITAL: - status = "digital\n"; - break; - - case DRM_FORCE_OFF: - status = "off\n"; - break; - - case DRM_FORCE_UNSPECIFIED: - status = "unspecified\n"; - break; - - default: - return 0; - } - - seq_puts(m, status); - - return 0; -} - -static int connector_open(struct inode *inode, struct file *file) -{ - struct drm_connector *dev = inode->i_private; - - return single_open(file, connector_show, dev); -} - -static ssize_t connector_write(struct file *file, const char __user *ubuf, - size_t len, loff_t *offp) -{ - struct seq_file *m = file->private_data; - struct drm_connector *connector = m->private; - char buf[12]; - - if (len > sizeof(buf) - 1) - return -EINVAL; - - if (copy_from_user(buf, ubuf, len)) - return -EFAULT; - - buf[len] = '\0'; - - if (!strcmp(buf, "on")) - connector->force = DRM_FORCE_ON; - else if (!strcmp(buf, "digital")) - connector->force = DRM_FORCE_ON_DIGITAL; - else if (!strcmp(buf, "off")) - connector->force = DRM_FORCE_OFF; - else if (!strcmp(buf, "unspecified")) - connector->force = DRM_FORCE_UNSPECIFIED; - else - return -EINVAL; - - return len; -} - -static int edid_show(struct seq_file *m, void *data) -{ - struct drm_connector *connector = m->private; - struct drm_property_blob *edid = connector->edid_blob_ptr; - - if (connector->override_edid && edid) - seq_write(m, edid->data, edid->length); - - return 0; -} - -static int edid_open(struct inode *inode, struct file *file) -{ - struct drm_connector *dev = inode->i_private; - - return single_open(file, edid_show, dev); -} - -static ssize_t edid_write(struct file *file, const char __user *ubuf, - size_t len, loff_t *offp) -{ - struct seq_file *m = file->private_data; - struct drm_connector *connector = m->private; - char *buf; - struct edid *edid; - int ret; - - buf = memdup_user(ubuf, len); - if (IS_ERR(buf)) - return PTR_ERR(buf); - - edid = (struct edid *) buf; - - if (len == 5 && !strncmp(buf, "reset", 5)) { - connector->override_edid = false; - ret = drm_mode_connector_update_edid_property(connector, NULL); - } else if (len < EDID_LENGTH || - EDID_LENGTH * (1 + edid->extensions) > len) - ret = -EINVAL; - else { - connector->override_edid = false; - ret = drm_mode_connector_update_edid_property(connector, edid); - if (!ret) - connector->override_edid = true; - } - - kfree(buf); - - return (ret) ? ret : len; -} - -static const struct file_operations drm_edid_fops = { - .owner = THIS_MODULE, - .open = edid_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = edid_write -}; - - -static const struct file_operations drm_connector_fops = { - .owner = THIS_MODULE, - .open = connector_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = connector_write -}; - -int drm_debugfs_connector_add(struct drm_connector *connector) -{ - struct drm_minor *minor = connector->dev->primary; - struct dentry *root, *ent; - - if (!minor->debugfs_root) - return -1; - - root = debugfs_create_dir(connector->name, minor->debugfs_root); - if (!root) - return -ENOMEM; - - connector->debugfs_entry = root; - - /* force */ - ent = debugfs_create_file("force", S_IRUGO | S_IWUSR, root, connector, - &drm_connector_fops); - if (!ent) - goto error; - - /* edid */ - ent = debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root, - connector, &drm_edid_fops); - if (!ent) - goto error; - - return 0; - -error: - debugfs_remove_recursive(connector->debugfs_entry); - connector->debugfs_entry = NULL; - return -ENOMEM; -} - -void drm_debugfs_connector_remove(struct drm_connector *connector) -{ - if (!connector->debugfs_entry) - return; - - debugfs_remove_recursive(connector->debugfs_entry); - - connector->debugfs_entry = NULL; -} - -#endif /* CONFIG_DEBUG_FS */ - diff --git a/src/4.x/drivers/gpu/drm/drm_dma.c b/src/4.x/drivers/gpu/drm/drm_dma.c deleted file mode 100644 index ea481800e..000000000 --- a/src/4.x/drivers/gpu/drm/drm_dma.c +++ /dev/null @@ -1,169 +0,0 @@ -/** - * \file drm_dma.c - * DMA IOCTL and function support - * - * \author Rickard E. (Rik) Faith - * \author Gareth Hughes - */ - -/* - * Created: Fri Mar 19 14:30:16 1999 by faith@valinux.com - * - * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include "drm_legacy.h" - -/** - * Initialize the DMA data. - * - * \param dev DRM device. - * \return zero on success or a negative value on failure. - * - * Allocate and initialize a drm_device_dma structure. - */ -int drm_legacy_dma_setup(struct drm_device *dev) -{ - int i; - - if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA) || - drm_core_check_feature(dev, DRIVER_MODESET)) { - return 0; - } - - dev->buf_use = 0; - atomic_set(&dev->buf_alloc, 0); - - dev->dma = kzalloc(sizeof(*dev->dma), GFP_KERNEL); - if (!dev->dma) - return -ENOMEM; - - for (i = 0; i <= DRM_MAX_ORDER; i++) - memset(&dev->dma->bufs[i], 0, sizeof(dev->dma->bufs[0])); - - return 0; -} - -/** - * Cleanup the DMA resources. - * - * \param dev DRM device. - * - * Free all pages associated with DMA buffers, the buffers and pages lists, and - * finally the drm_device::dma structure itself. - */ -void drm_legacy_dma_takedown(struct drm_device *dev) -{ - struct drm_device_dma *dma = dev->dma; - int i, j; - - if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA) || - drm_core_check_feature(dev, DRIVER_MODESET)) { - return; - } - - if (!dma) - return; - - /* Clear dma buffers */ - for (i = 0; i <= DRM_MAX_ORDER; i++) { - if (dma->bufs[i].seg_count) { - DRM_DEBUG("order %d: buf_count = %d," - " seg_count = %d\n", - i, - dma->bufs[i].buf_count, - dma->bufs[i].seg_count); - for (j = 0; j < dma->bufs[i].seg_count; j++) { - if (dma->bufs[i].seglist[j]) { - drm_pci_free(dev, dma->bufs[i].seglist[j]); - } - } - kfree(dma->bufs[i].seglist); - } - if (dma->bufs[i].buf_count) { - for (j = 0; j < dma->bufs[i].buf_count; j++) { - kfree(dma->bufs[i].buflist[j].dev_private); - } - kfree(dma->bufs[i].buflist); - } - } - - kfree(dma->buflist); - kfree(dma->pagelist); - kfree(dev->dma); - dev->dma = NULL; -} - -/** - * Free a buffer. - * - * \param dev DRM device. - * \param buf buffer to free. - * - * Resets the fields of \p buf. - */ -void drm_legacy_free_buffer(struct drm_device *dev, struct drm_buf * buf) -{ - if (!buf) - return; - - buf->waiting = 0; - buf->pending = 0; - buf->file_priv = NULL; - buf->used = 0; -} - -/** - * Reclaim the buffers. - * - * \param file_priv DRM file private. - * - * Frees each buffer associated with \p file_priv not already on the hardware. - */ -void drm_legacy_reclaim_buffers(struct drm_device *dev, - struct drm_file *file_priv) -{ - struct drm_device_dma *dma = dev->dma; - int i; - - if (!dma) - return; - for (i = 0; i < dma->buf_count; i++) { - if (dma->buflist[i]->file_priv == file_priv) { - switch (dma->buflist[i]->list) { - case DRM_LIST_NONE: - drm_legacy_free_buffer(dev, dma->buflist[i]); - break; - case DRM_LIST_WAIT: - dma->buflist[i]->list = DRM_LIST_RECLAIM; - break; - default: - /* Buffer already on hardware. */ - break; - } - } - } -} diff --git a/src/4.x/drivers/gpu/drm/drm_dp_helper.c b/src/4.x/drivers/gpu/drm/drm_dp_helper.c deleted file mode 100644 index 7e5a97204..000000000 --- a/src/4.x/drivers/gpu/drm/drm_dp_helper.c +++ /dev/null @@ -1,790 +0,0 @@ -/* - * Copyright © 2009 Keith Packard - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that copyright - * notice and this permission notice appear in supporting documentation, and - * that the name of the copyright holders not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. The copyright holders make no representations - * about the suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * DOC: dp helpers - * - * These functions contain some common logic and helpers at various abstraction - * levels to deal with Display Port sink devices and related things like DP aux - * channel transfers, EDID reading over DP aux channels, decoding certain DPCD - * blocks, ... - */ - -/* Helpers for DP link training */ -static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r) -{ - return link_status[r - DP_LANE0_1_STATUS]; -} - -static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE], - int lane) -{ - int i = DP_LANE0_1_STATUS + (lane >> 1); - int s = (lane & 1) * 4; - u8 l = dp_link_status(link_status, i); - return (l >> s) & 0xf; -} - -bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE], - int lane_count) -{ - u8 lane_align; - u8 lane_status; - int lane; - - lane_align = dp_link_status(link_status, - DP_LANE_ALIGN_STATUS_UPDATED); - if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) - return false; - for (lane = 0; lane < lane_count; lane++) { - lane_status = dp_get_lane_status(link_status, lane); - if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) - return false; - } - return true; -} -EXPORT_SYMBOL(drm_dp_channel_eq_ok); - -bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE], - int lane_count) -{ - int lane; - u8 lane_status; - - for (lane = 0; lane < lane_count; lane++) { - lane_status = dp_get_lane_status(link_status, lane); - if ((lane_status & DP_LANE_CR_DONE) == 0) - return false; - } - return true; -} -EXPORT_SYMBOL(drm_dp_clock_recovery_ok); - -u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE], - int lane) -{ - int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); - int s = ((lane & 1) ? - DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : - DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); - u8 l = dp_link_status(link_status, i); - - return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; -} -EXPORT_SYMBOL(drm_dp_get_adjust_request_voltage); - -u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE], - int lane) -{ - int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); - int s = ((lane & 1) ? - DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : - DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); - u8 l = dp_link_status(link_status, i); - - return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; -} -EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis); - -void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { - if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) - udelay(100); - else - mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); -} -EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay); - -void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { - if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) - udelay(400); - else - mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); -} -EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay); - -u8 drm_dp_link_rate_to_bw_code(int link_rate) -{ - switch (link_rate) { - case 162000: - default: - return DP_LINK_BW_1_62; - case 270000: - return DP_LINK_BW_2_7; - case 540000: - return DP_LINK_BW_5_4; - } -} -EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code); - -int drm_dp_bw_code_to_link_rate(u8 link_bw) -{ - switch (link_bw) { - case DP_LINK_BW_1_62: - default: - return 162000; - case DP_LINK_BW_2_7: - return 270000; - case DP_LINK_BW_5_4: - return 540000; - } -} -EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); - -#define AUX_RETRY_INTERVAL 500 /* us */ - -/** - * DOC: dp helpers - * - * The DisplayPort AUX channel is an abstraction to allow generic, driver- - * independent access to AUX functionality. Drivers can take advantage of - * this by filling in the fields of the drm_dp_aux structure. - * - * Transactions are described using a hardware-independent drm_dp_aux_msg - * structure, which is passed into a driver's .transfer() implementation. - * Both native and I2C-over-AUX transactions are supported. - */ - -static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, - unsigned int offset, void *buffer, size_t size) -{ - struct drm_dp_aux_msg msg; - unsigned int retry; - int err = 0; - - memset(&msg, 0, sizeof(msg)); - msg.address = offset; - msg.request = request; - msg.buffer = buffer; - msg.size = size; - - mutex_lock(&aux->hw_mutex); - - /* - * The specification doesn't give any recommendation on how often to - * retry native transactions. We used to retry 7 times like for - * aux i2c transactions but real world devices this wasn't - * sufficient, bump to 32 which makes Dell 4k monitors happier. - */ - for (retry = 0; retry < 32; retry++) { - - err = aux->transfer(aux, &msg); - if (err < 0) { - if (err == -EBUSY) - continue; - - goto unlock; - } - - - switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) { - case DP_AUX_NATIVE_REPLY_ACK: - if (err < size) - err = -EPROTO; - goto unlock; - - case DP_AUX_NATIVE_REPLY_NACK: - err = -EIO; - goto unlock; - - case DP_AUX_NATIVE_REPLY_DEFER: - usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100); - break; - } - } - - DRM_DEBUG_KMS("too many retries, giving up\n"); - err = -EIO; - -unlock: - mutex_unlock(&aux->hw_mutex); - return err; -} - -/** - * drm_dp_dpcd_read() - read a series of bytes from the DPCD - * @aux: DisplayPort AUX channel - * @offset: address of the (first) register to read - * @buffer: buffer to store the register values - * @size: number of bytes in @buffer - * - * Returns the number of bytes transferred on success, or a negative error - * code on failure. -EIO is returned if the request was NAKed by the sink or - * if the retry count was exceeded. If not all bytes were transferred, this - * function returns -EPROTO. Errors from the underlying AUX channel transfer - * function, with the exception of -EBUSY (which causes the transaction to - * be retried), are propagated to the caller. - */ -ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, - void *buffer, size_t size) -{ - return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, buffer, - size); -} -EXPORT_SYMBOL(drm_dp_dpcd_read); - -/** - * drm_dp_dpcd_write() - write a series of bytes to the DPCD - * @aux: DisplayPort AUX channel - * @offset: address of the (first) register to write - * @buffer: buffer containing the values to write - * @size: number of bytes in @buffer - * - * Returns the number of bytes transferred on success, or a negative error - * code on failure. -EIO is returned if the request was NAKed by the sink or - * if the retry count was exceeded. If not all bytes were transferred, this - * function returns -EPROTO. Errors from the underlying AUX channel transfer - * function, with the exception of -EBUSY (which causes the transaction to - * be retried), are propagated to the caller. - */ -ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, - void *buffer, size_t size) -{ - return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, - size); -} -EXPORT_SYMBOL(drm_dp_dpcd_write); - -/** - * drm_dp_dpcd_read_link_status() - read DPCD link status (bytes 0x202-0x207) - * @aux: DisplayPort AUX channel - * @status: buffer to store the link status in (must be at least 6 bytes) - * - * Returns the number of bytes transferred on success or a negative error - * code on failure. - */ -int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, - u8 status[DP_LINK_STATUS_SIZE]) -{ - return drm_dp_dpcd_read(aux, DP_LANE0_1_STATUS, status, - DP_LINK_STATUS_SIZE); -} -EXPORT_SYMBOL(drm_dp_dpcd_read_link_status); - -/** - * drm_dp_link_probe() - probe a DisplayPort link for capabilities - * @aux: DisplayPort AUX channel - * @link: pointer to structure in which to return link capabilities - * - * The structure filled in by this function can usually be passed directly - * into drm_dp_link_power_up() and drm_dp_link_configure() to power up and - * configure the link based on the link's capabilities. - * - * Returns 0 on success or a negative error code on failure. - */ -int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) -{ - u8 values[3]; - int err; - - memset(link, 0, sizeof(*link)); - - err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values)); - if (err < 0) - return err; - - link->revision = values[0]; - link->rate = drm_dp_bw_code_to_link_rate(values[1]); - link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK; - - if (values[2] & DP_ENHANCED_FRAME_CAP) - link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING; - - return 0; -} -EXPORT_SYMBOL(drm_dp_link_probe); - -/** - * drm_dp_link_power_up() - power up a DisplayPort link - * @aux: DisplayPort AUX channel - * @link: pointer to a structure containing the link configuration - * - * Returns 0 on success or a negative error code on failure. - */ -int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link) -{ - u8 value; - int err; - - /* DP_SET_POWER register is only available on DPCD v1.1 and later */ - if (link->revision < 0x11) - return 0; - - err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); - if (err < 0) - return err; - - value &= ~DP_SET_POWER_MASK; - value |= DP_SET_POWER_D0; - - err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); - if (err < 0) - return err; - - /* - * According to the DP 1.1 specification, a "Sink Device must exit the - * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink - * Control Field" (register 0x600). - */ - usleep_range(1000, 2000); - - return 0; -} -EXPORT_SYMBOL(drm_dp_link_power_up); - -/** - * drm_dp_link_power_down() - power down a DisplayPort link - * @aux: DisplayPort AUX channel - * @link: pointer to a structure containing the link configuration - * - * Returns 0 on success or a negative error code on failure. - */ -int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link) -{ - u8 value; - int err; - - /* DP_SET_POWER register is only available on DPCD v1.1 and later */ - if (link->revision < 0x11) - return 0; - - err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); - if (err < 0) - return err; - - value &= ~DP_SET_POWER_MASK; - value |= DP_SET_POWER_D3; - - err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(drm_dp_link_power_down); - -/** - * drm_dp_link_configure() - configure a DisplayPort link - * @aux: DisplayPort AUX channel - * @link: pointer to a structure containing the link configuration - * - * Returns 0 on success or a negative error code on failure. - */ -int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) -{ - u8 values[2]; - int err; - - values[0] = drm_dp_link_rate_to_bw_code(link->rate); - values[1] = link->num_lanes; - - if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) - values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; - - err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values)); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(drm_dp_link_configure); - -/* - * I2C-over-AUX implementation - */ - -static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | - I2C_FUNC_SMBUS_READ_BLOCK_DATA | - I2C_FUNC_SMBUS_BLOCK_PROC_CALL | - I2C_FUNC_10BIT_ADDR; -} - -static void drm_dp_i2c_msg_write_status_update(struct drm_dp_aux_msg *msg) -{ - /* - * In case of i2c defer or short i2c ack reply to a write, - * we need to switch to WRITE_STATUS_UPDATE to drain the - * rest of the message - */ - if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_WRITE) { - msg->request &= DP_AUX_I2C_MOT; - msg->request |= DP_AUX_I2C_WRITE_STATUS_UPDATE; - } -} - -#define AUX_PRECHARGE_LEN 10 /* 10 to 16 */ -#define AUX_SYNC_LEN (16 + 4) /* preamble + AUX_SYNC_END */ -#define AUX_STOP_LEN 4 -#define AUX_CMD_LEN 4 -#define AUX_ADDRESS_LEN 20 -#define AUX_REPLY_PAD_LEN 4 -#define AUX_LENGTH_LEN 8 - -/* - * Calculate the duration of the AUX request/reply in usec. Gives the - * "best" case estimate, ie. successful while as short as possible. - */ -static int drm_dp_aux_req_duration(const struct drm_dp_aux_msg *msg) -{ - int len = AUX_PRECHARGE_LEN + AUX_SYNC_LEN + AUX_STOP_LEN + - AUX_CMD_LEN + AUX_ADDRESS_LEN + AUX_LENGTH_LEN; - - if ((msg->request & DP_AUX_I2C_READ) == 0) - len += msg->size * 8; - - return len; -} - -static int drm_dp_aux_reply_duration(const struct drm_dp_aux_msg *msg) -{ - int len = AUX_PRECHARGE_LEN + AUX_SYNC_LEN + AUX_STOP_LEN + - AUX_CMD_LEN + AUX_REPLY_PAD_LEN; - - /* - * For read we expect what was asked. For writes there will - * be 0 or 1 data bytes. Assume 0 for the "best" case. - */ - if (msg->request & DP_AUX_I2C_READ) - len += msg->size * 8; - - return len; -} - -#define I2C_START_LEN 1 -#define I2C_STOP_LEN 1 -#define I2C_ADDR_LEN 9 /* ADDRESS + R/W + ACK/NACK */ -#define I2C_DATA_LEN 9 /* DATA + ACK/NACK */ - -/* - * Calculate the length of the i2c transfer in usec, assuming - * the i2c bus speed is as specified. Gives the the "worst" - * case estimate, ie. successful while as long as possible. - * Doesn't account the the "MOT" bit, and instead assumes each - * message includes a START, ADDRESS and STOP. Neither does it - * account for additional random variables such as clock stretching. - */ -static int drm_dp_i2c_msg_duration(const struct drm_dp_aux_msg *msg, - int i2c_speed_khz) -{ - /* AUX bitrate is 1MHz, i2c bitrate as specified */ - return DIV_ROUND_UP((I2C_START_LEN + I2C_ADDR_LEN + - msg->size * I2C_DATA_LEN + - I2C_STOP_LEN) * 1000, i2c_speed_khz); -} - -/* - * Deterine how many retries should be attempted to successfully transfer - * the specified message, based on the estimated durations of the - * i2c and AUX transfers. - */ -static int drm_dp_i2c_retry_count(const struct drm_dp_aux_msg *msg, - int i2c_speed_khz) -{ - int aux_time_us = drm_dp_aux_req_duration(msg) + - drm_dp_aux_reply_duration(msg); - int i2c_time_us = drm_dp_i2c_msg_duration(msg, i2c_speed_khz); - - return DIV_ROUND_UP(i2c_time_us, aux_time_us + AUX_RETRY_INTERVAL); -} - -/* - * FIXME currently assumes 10 kHz as some real world devices seem - * to require it. We should query/set the speed via DPCD if supported. - */ -static int dp_aux_i2c_speed_khz __read_mostly = 10; -module_param_unsafe(dp_aux_i2c_speed_khz, int, 0644); -MODULE_PARM_DESC(dp_aux_i2c_speed_khz, - "Assumed speed of the i2c bus in kHz, (1-400, default 10)"); - -/* - * Transfer a single I2C-over-AUX message and handle various error conditions, - * retrying the transaction as appropriate. It is assumed that the - * aux->transfer function does not modify anything in the msg other than the - * reply field. - * - * Returns bytes transferred on success, or a negative error code on failure. - */ -static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) -{ - unsigned int retry, defer_i2c; - int ret; - /* - * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device - * is required to retry at least seven times upon receiving AUX_DEFER - * before giving up the AUX transaction. - * - * We also try to account for the i2c bus speed. - */ - int max_retries = max(7, drm_dp_i2c_retry_count(msg, dp_aux_i2c_speed_khz)); - - for (retry = 0, defer_i2c = 0; retry < (max_retries + defer_i2c); retry++) { - ret = aux->transfer(aux, msg); - if (ret < 0) { - if (ret == -EBUSY) - continue; - - DRM_DEBUG_KMS("transaction failed: %d\n", ret); - return ret; - } - - - switch (msg->reply & DP_AUX_NATIVE_REPLY_MASK) { - case DP_AUX_NATIVE_REPLY_ACK: - /* - * For I2C-over-AUX transactions this isn't enough, we - * need to check for the I2C ACK reply. - */ - break; - - case DP_AUX_NATIVE_REPLY_NACK: - DRM_DEBUG_KMS("native nack (result=%d, size=%zu)\n", ret, msg->size); - return -EREMOTEIO; - - case DP_AUX_NATIVE_REPLY_DEFER: - DRM_DEBUG_KMS("native defer\n"); - /* - * We could check for I2C bit rate capabilities and if - * available adjust this interval. We could also be - * more careful with DP-to-legacy adapters where a - * long legacy cable may force very low I2C bit rates. - * - * For now just defer for long enough to hopefully be - * safe for all use-cases. - */ - usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100); - continue; - - default: - DRM_ERROR("invalid native reply %#04x\n", msg->reply); - return -EREMOTEIO; - } - - switch (msg->reply & DP_AUX_I2C_REPLY_MASK) { - case DP_AUX_I2C_REPLY_ACK: - /* - * Both native ACK and I2C ACK replies received. We - * can assume the transfer was successful. - */ - if (ret != msg->size) - drm_dp_i2c_msg_write_status_update(msg); - return ret; - - case DP_AUX_I2C_REPLY_NACK: - DRM_DEBUG_KMS("I2C nack (result=%d, size=%zu\n", ret, msg->size); - aux->i2c_nack_count++; - return -EREMOTEIO; - - case DP_AUX_I2C_REPLY_DEFER: - DRM_DEBUG_KMS("I2C defer\n"); - /* DP Compliance Test 4.2.2.5 Requirement: - * Must have at least 7 retries for I2C defers on the - * transaction to pass this test - */ - aux->i2c_defer_count++; - if (defer_i2c < 7) - defer_i2c++; - usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100); - drm_dp_i2c_msg_write_status_update(msg); - - continue; - - default: - DRM_ERROR("invalid I2C reply %#04x\n", msg->reply); - return -EREMOTEIO; - } - } - - DRM_DEBUG_KMS("too many retries, giving up\n"); - return -EREMOTEIO; -} - -static void drm_dp_i2c_msg_set_request(struct drm_dp_aux_msg *msg, - const struct i2c_msg *i2c_msg) -{ - msg->request = (i2c_msg->flags & I2C_M_RD) ? - DP_AUX_I2C_READ : DP_AUX_I2C_WRITE; - msg->request |= DP_AUX_I2C_MOT; -} - -/* - * Keep retrying drm_dp_i2c_do_msg until all data has been transferred. - * - * Returns an error code on failure, or a recommended transfer size on success. - */ -static int drm_dp_i2c_drain_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *orig_msg) -{ - int err, ret = orig_msg->size; - struct drm_dp_aux_msg msg = *orig_msg; - - while (msg.size > 0) { - err = drm_dp_i2c_do_msg(aux, &msg); - if (err <= 0) - return err == 0 ? -EPROTO : err; - - if (err < msg.size && err < ret) { - DRM_DEBUG_KMS("Partial I2C reply: requested %zu bytes got %d bytes\n", - msg.size, err); - ret = err; - } - - msg.size -= err; - msg.buffer += err; - } - - return ret; -} - -/* - * Bizlink designed DP->DVI-D Dual Link adapters require the I2C over AUX - * packets to be as large as possible. If not, the I2C transactions never - * succeed. Hence the default is maximum. - */ -static int dp_aux_i2c_transfer_size __read_mostly = DP_AUX_MAX_PAYLOAD_BYTES; -module_param_unsafe(dp_aux_i2c_transfer_size, int, 0644); -MODULE_PARM_DESC(dp_aux_i2c_transfer_size, - "Number of bytes to transfer in a single I2C over DP AUX CH message, (1-16, default 16)"); - -static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, - int num) -{ - struct drm_dp_aux *aux = adapter->algo_data; - unsigned int i, j; - unsigned transfer_size; - struct drm_dp_aux_msg msg; - int err = 0; - - dp_aux_i2c_transfer_size = clamp(dp_aux_i2c_transfer_size, 1, DP_AUX_MAX_PAYLOAD_BYTES); - - memset(&msg, 0, sizeof(msg)); - - mutex_lock(&aux->hw_mutex); - - for (i = 0; i < num; i++) { - msg.address = msgs[i].addr; - drm_dp_i2c_msg_set_request(&msg, &msgs[i]); - /* Send a bare address packet to start the transaction. - * Zero sized messages specify an address only (bare - * address) transaction. - */ - msg.buffer = NULL; - msg.size = 0; - err = drm_dp_i2c_do_msg(aux, &msg); - - /* - * Reset msg.request in case in case it got - * changed into a WRITE_STATUS_UPDATE. - */ - drm_dp_i2c_msg_set_request(&msg, &msgs[i]); - - if (err < 0) - break; - /* We want each transaction to be as large as possible, but - * we'll go to smaller sizes if the hardware gives us a - * short reply. - */ - transfer_size = dp_aux_i2c_transfer_size; - for (j = 0; j < msgs[i].len; j += msg.size) { - msg.buffer = msgs[i].buf + j; - msg.size = min(transfer_size, msgs[i].len - j); - - err = drm_dp_i2c_drain_msg(aux, &msg); - - /* - * Reset msg.request in case in case it got - * changed into a WRITE_STATUS_UPDATE. - */ - drm_dp_i2c_msg_set_request(&msg, &msgs[i]); - - if (err < 0) - break; - transfer_size = err; - } - if (err < 0) - break; - } - if (err >= 0) - err = num; - /* Send a bare address packet to close out the transaction. - * Zero sized messages specify an address only (bare - * address) transaction. - */ - msg.request &= ~DP_AUX_I2C_MOT; - msg.buffer = NULL; - msg.size = 0; - (void)drm_dp_i2c_do_msg(aux, &msg); - - mutex_unlock(&aux->hw_mutex); - - return err; -} - -static const struct i2c_algorithm drm_dp_i2c_algo = { - .functionality = drm_dp_i2c_functionality, - .master_xfer = drm_dp_i2c_xfer, -}; - -/** - * drm_dp_aux_register() - initialise and register aux channel - * @aux: DisplayPort AUX channel - * - * Returns 0 on success or a negative error code on failure. - */ -int drm_dp_aux_register(struct drm_dp_aux *aux) -{ - mutex_init(&aux->hw_mutex); - - aux->ddc.algo = &drm_dp_i2c_algo; - aux->ddc.algo_data = aux; - aux->ddc.retries = 3; - - aux->ddc.class = I2C_CLASS_DDC; - aux->ddc.owner = THIS_MODULE; - aux->ddc.dev.parent = aux->dev; - aux->ddc.dev.of_node = aux->dev->of_node; - - strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), - sizeof(aux->ddc.name)); - - return i2c_add_adapter(&aux->ddc); -} -EXPORT_SYMBOL(drm_dp_aux_register); - -/** - * drm_dp_aux_unregister() - unregister an AUX adapter - * @aux: DisplayPort AUX channel - */ -void drm_dp_aux_unregister(struct drm_dp_aux *aux) -{ - i2c_del_adapter(&aux->ddc); -} -EXPORT_SYMBOL(drm_dp_aux_unregister); diff --git a/src/4.x/drivers/gpu/drm/drm_dp_mst_topology.c b/src/4.x/drivers/gpu/drm/drm_dp_mst_topology.c deleted file mode 100644 index ff12d926e..000000000 --- a/src/4.x/drivers/gpu/drm/drm_dp_mst_topology.c +++ /dev/null @@ -1,3101 +0,0 @@ -/* - * Copyright © 2014 Red Hat - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that copyright - * notice and this permission notice appear in supporting documentation, and - * that the name of the copyright holders not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. The copyright holders make no representations - * about the suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/** - * DOC: dp mst helper - * - * These functions contain parts of the DisplayPort 1.2a MultiStream Transport - * protocol. The helpers contain a topology manager and bandwidth manager. - * The helpers encapsulate the sending and received of sideband msgs. - */ -static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr, - char *buf); -static int test_calc_pbn_mode(void); - -static void drm_dp_put_port(struct drm_dp_mst_port *port); - -static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, - int id, - struct drm_dp_payload *payload); - -static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, - int offset, int size, u8 *bytes); - -static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_branch *mstb); -static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_branch *mstb, - struct drm_dp_mst_port *port); -static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, - u8 *guid); - -static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux); -static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux); -static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr); -/* sideband msg handling */ -static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles) -{ - u8 bitmask = 0x80; - u8 bitshift = 7; - u8 array_index = 0; - int number_of_bits = num_nibbles * 4; - u8 remainder = 0; - - while (number_of_bits != 0) { - number_of_bits--; - remainder <<= 1; - remainder |= (data[array_index] & bitmask) >> bitshift; - bitmask >>= 1; - bitshift--; - if (bitmask == 0) { - bitmask = 0x80; - bitshift = 7; - array_index++; - } - if ((remainder & 0x10) == 0x10) - remainder ^= 0x13; - } - - number_of_bits = 4; - while (number_of_bits != 0) { - number_of_bits--; - remainder <<= 1; - if ((remainder & 0x10) != 0) - remainder ^= 0x13; - } - - return remainder; -} - -static u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes) -{ - u8 bitmask = 0x80; - u8 bitshift = 7; - u8 array_index = 0; - int number_of_bits = number_of_bytes * 8; - u16 remainder = 0; - - while (number_of_bits != 0) { - number_of_bits--; - remainder <<= 1; - remainder |= (data[array_index] & bitmask) >> bitshift; - bitmask >>= 1; - bitshift--; - if (bitmask == 0) { - bitmask = 0x80; - bitshift = 7; - array_index++; - } - if ((remainder & 0x100) == 0x100) - remainder ^= 0xd5; - } - - number_of_bits = 8; - while (number_of_bits != 0) { - number_of_bits--; - remainder <<= 1; - if ((remainder & 0x100) != 0) - remainder ^= 0xd5; - } - - return remainder & 0xff; -} -static inline u8 drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr *hdr) -{ - u8 size = 3; - size += (hdr->lct / 2); - return size; -} - -static void drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr, - u8 *buf, int *len) -{ - int idx = 0; - int i; - u8 crc4; - buf[idx++] = ((hdr->lct & 0xf) << 4) | (hdr->lcr & 0xf); - for (i = 0; i < (hdr->lct / 2); i++) - buf[idx++] = hdr->rad[i]; - buf[idx++] = (hdr->broadcast << 7) | (hdr->path_msg << 6) | - (hdr->msg_len & 0x3f); - buf[idx++] = (hdr->somt << 7) | (hdr->eomt << 6) | (hdr->seqno << 4); - - crc4 = drm_dp_msg_header_crc4(buf, (idx * 2) - 1); - buf[idx - 1] |= (crc4 & 0xf); - - *len = idx; -} - -static bool drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr, - u8 *buf, int buflen, u8 *hdrlen) -{ - u8 crc4; - u8 len; - int i; - u8 idx; - if (buf[0] == 0) - return false; - len = 3; - len += ((buf[0] & 0xf0) >> 4) / 2; - if (len > buflen) - return false; - crc4 = drm_dp_msg_header_crc4(buf, (len * 2) - 1); - - if ((crc4 & 0xf) != (buf[len - 1] & 0xf)) { - DRM_DEBUG_KMS("crc4 mismatch 0x%x 0x%x\n", crc4, buf[len - 1]); - return false; - } - - hdr->lct = (buf[0] & 0xf0) >> 4; - hdr->lcr = (buf[0] & 0xf); - idx = 1; - for (i = 0; i < (hdr->lct / 2); i++) - hdr->rad[i] = buf[idx++]; - hdr->broadcast = (buf[idx] >> 7) & 0x1; - hdr->path_msg = (buf[idx] >> 6) & 0x1; - hdr->msg_len = buf[idx] & 0x3f; - idx++; - hdr->somt = (buf[idx] >> 7) & 0x1; - hdr->eomt = (buf[idx] >> 6) & 0x1; - hdr->seqno = (buf[idx] >> 4) & 0x1; - idx++; - *hdrlen = idx; - return true; -} - -static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req, - struct drm_dp_sideband_msg_tx *raw) -{ - int idx = 0; - int i; - u8 *buf = raw->msg; - buf[idx++] = req->req_type & 0x7f; - - switch (req->req_type) { - case DP_ENUM_PATH_RESOURCES: - buf[idx] = (req->u.port_num.port_number & 0xf) << 4; - idx++; - break; - case DP_ALLOCATE_PAYLOAD: - buf[idx] = (req->u.allocate_payload.port_number & 0xf) << 4 | - (req->u.allocate_payload.number_sdp_streams & 0xf); - idx++; - buf[idx] = (req->u.allocate_payload.vcpi & 0x7f); - idx++; - buf[idx] = (req->u.allocate_payload.pbn >> 8); - idx++; - buf[idx] = (req->u.allocate_payload.pbn & 0xff); - idx++; - for (i = 0; i < req->u.allocate_payload.number_sdp_streams / 2; i++) { - buf[idx] = ((req->u.allocate_payload.sdp_stream_sink[i * 2] & 0xf) << 4) | - (req->u.allocate_payload.sdp_stream_sink[i * 2 + 1] & 0xf); - idx++; - } - if (req->u.allocate_payload.number_sdp_streams & 1) { - i = req->u.allocate_payload.number_sdp_streams - 1; - buf[idx] = (req->u.allocate_payload.sdp_stream_sink[i] & 0xf) << 4; - idx++; - } - break; - case DP_QUERY_PAYLOAD: - buf[idx] = (req->u.query_payload.port_number & 0xf) << 4; - idx++; - buf[idx] = (req->u.query_payload.vcpi & 0x7f); - idx++; - break; - case DP_REMOTE_DPCD_READ: - buf[idx] = (req->u.dpcd_read.port_number & 0xf) << 4; - buf[idx] |= ((req->u.dpcd_read.dpcd_address & 0xf0000) >> 16) & 0xf; - idx++; - buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff00) >> 8; - idx++; - buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff); - idx++; - buf[idx] = (req->u.dpcd_read.num_bytes); - idx++; - break; - - case DP_REMOTE_DPCD_WRITE: - buf[idx] = (req->u.dpcd_write.port_number & 0xf) << 4; - buf[idx] |= ((req->u.dpcd_write.dpcd_address & 0xf0000) >> 16) & 0xf; - idx++; - buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff00) >> 8; - idx++; - buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff); - idx++; - buf[idx] = (req->u.dpcd_write.num_bytes); - idx++; - memcpy(&buf[idx], req->u.dpcd_write.bytes, req->u.dpcd_write.num_bytes); - idx += req->u.dpcd_write.num_bytes; - break; - case DP_REMOTE_I2C_READ: - buf[idx] = (req->u.i2c_read.port_number & 0xf) << 4; - buf[idx] |= (req->u.i2c_read.num_transactions & 0x3); - idx++; - for (i = 0; i < (req->u.i2c_read.num_transactions & 0x3); i++) { - buf[idx] = req->u.i2c_read.transactions[i].i2c_dev_id & 0x7f; - idx++; - buf[idx] = req->u.i2c_read.transactions[i].num_bytes; - idx++; - memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes); - idx += req->u.i2c_read.transactions[i].num_bytes; - - buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 5; - buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf); - idx++; - } - buf[idx] = (req->u.i2c_read.read_i2c_device_id) & 0x7f; - idx++; - buf[idx] = (req->u.i2c_read.num_bytes_read); - idx++; - break; - - case DP_REMOTE_I2C_WRITE: - buf[idx] = (req->u.i2c_write.port_number & 0xf) << 4; - idx++; - buf[idx] = (req->u.i2c_write.write_i2c_device_id) & 0x7f; - idx++; - buf[idx] = (req->u.i2c_write.num_bytes); - idx++; - memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes); - idx += req->u.i2c_write.num_bytes; - break; - } - raw->cur_len = idx; -} - -static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len) -{ - u8 crc4; - crc4 = drm_dp_msg_data_crc4(msg, len); - msg[len] = crc4; -} - -static void drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body *rep, - struct drm_dp_sideband_msg_tx *raw) -{ - int idx = 0; - u8 *buf = raw->msg; - - buf[idx++] = (rep->reply_type & 0x1) << 7 | (rep->req_type & 0x7f); - - raw->cur_len = idx; -} - -/* this adds a chunk of msg to the builder to get the final msg */ -static bool drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx *msg, - u8 *replybuf, u8 replybuflen, bool hdr) -{ - int ret; - u8 crc4; - - if (hdr) { - u8 hdrlen; - struct drm_dp_sideband_msg_hdr recv_hdr; - ret = drm_dp_decode_sideband_msg_hdr(&recv_hdr, replybuf, replybuflen, &hdrlen); - if (ret == false) { - print_hex_dump(KERN_DEBUG, "failed hdr", DUMP_PREFIX_NONE, 16, 1, replybuf, replybuflen, false); - return false; - } - - /* - * ignore out-of-order messages or messages that are part of a - * failed transaction - */ - if (!recv_hdr.somt && !msg->have_somt) - return false; - - /* get length contained in this portion */ - msg->curchunk_len = recv_hdr.msg_len; - msg->curchunk_hdrlen = hdrlen; - - /* we have already gotten an somt - don't bother parsing */ - if (recv_hdr.somt && msg->have_somt) - return false; - - if (recv_hdr.somt) { - memcpy(&msg->initial_hdr, &recv_hdr, sizeof(struct drm_dp_sideband_msg_hdr)); - msg->have_somt = true; - } - if (recv_hdr.eomt) - msg->have_eomt = true; - - /* copy the bytes for the remainder of this header chunk */ - msg->curchunk_idx = min(msg->curchunk_len, (u8)(replybuflen - hdrlen)); - memcpy(&msg->chunk[0], replybuf + hdrlen, msg->curchunk_idx); - } else { - memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen); - msg->curchunk_idx += replybuflen; - } - - if (msg->curchunk_idx >= msg->curchunk_len) { - /* do CRC */ - crc4 = drm_dp_msg_data_crc4(msg->chunk, msg->curchunk_len - 1); - /* copy chunk into bigger msg */ - memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1); - msg->curlen += msg->curchunk_len - 1; - } - return true; -} - -static bool drm_dp_sideband_parse_link_address(struct drm_dp_sideband_msg_rx *raw, - struct drm_dp_sideband_msg_reply_body *repmsg) -{ - int idx = 1; - int i; - memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16); - idx += 16; - repmsg->u.link_addr.nports = raw->msg[idx] & 0xf; - idx++; - if (idx > raw->curlen) - goto fail_len; - for (i = 0; i < repmsg->u.link_addr.nports; i++) { - if (raw->msg[idx] & 0x80) - repmsg->u.link_addr.ports[i].input_port = 1; - - repmsg->u.link_addr.ports[i].peer_device_type = (raw->msg[idx] >> 4) & 0x7; - repmsg->u.link_addr.ports[i].port_number = (raw->msg[idx] & 0xf); - - idx++; - if (idx > raw->curlen) - goto fail_len; - repmsg->u.link_addr.ports[i].mcs = (raw->msg[idx] >> 7) & 0x1; - repmsg->u.link_addr.ports[i].ddps = (raw->msg[idx] >> 6) & 0x1; - if (repmsg->u.link_addr.ports[i].input_port == 0) - repmsg->u.link_addr.ports[i].legacy_device_plug_status = (raw->msg[idx] >> 5) & 0x1; - idx++; - if (idx > raw->curlen) - goto fail_len; - if (repmsg->u.link_addr.ports[i].input_port == 0) { - repmsg->u.link_addr.ports[i].dpcd_revision = (raw->msg[idx]); - idx++; - if (idx > raw->curlen) - goto fail_len; - memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16); - idx += 16; - if (idx > raw->curlen) - goto fail_len; - repmsg->u.link_addr.ports[i].num_sdp_streams = (raw->msg[idx] >> 4) & 0xf; - repmsg->u.link_addr.ports[i].num_sdp_stream_sinks = (raw->msg[idx] & 0xf); - idx++; - - } - if (idx > raw->curlen) - goto fail_len; - } - - return true; -fail_len: - DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen); - return false; -} - -static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx *raw, - struct drm_dp_sideband_msg_reply_body *repmsg) -{ - int idx = 1; - repmsg->u.remote_dpcd_read_ack.port_number = raw->msg[idx] & 0xf; - idx++; - if (idx > raw->curlen) - goto fail_len; - repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx]; - if (idx > raw->curlen) - goto fail_len; - - memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes); - return true; -fail_len: - DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen); - return false; -} - -static bool drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_rx *raw, - struct drm_dp_sideband_msg_reply_body *repmsg) -{ - int idx = 1; - repmsg->u.remote_dpcd_write_ack.port_number = raw->msg[idx] & 0xf; - idx++; - if (idx > raw->curlen) - goto fail_len; - return true; -fail_len: - DRM_DEBUG_KMS("parse length fail %d %d\n", idx, raw->curlen); - return false; -} - -static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg_rx *raw, - struct drm_dp_sideband_msg_reply_body *repmsg) -{ - int idx = 1; - - repmsg->u.remote_i2c_read_ack.port_number = (raw->msg[idx] & 0xf); - idx++; - if (idx > raw->curlen) - goto fail_len; - repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx]; - idx++; - /* TODO check */ - memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes); - return true; -fail_len: - DRM_DEBUG_KMS("remote i2c reply parse length fail %d %d\n", idx, raw->curlen); - return false; -} - -static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband_msg_rx *raw, - struct drm_dp_sideband_msg_reply_body *repmsg) -{ - int idx = 1; - repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf; - idx++; - if (idx > raw->curlen) - goto fail_len; - repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]); - idx += 2; - if (idx > raw->curlen) - goto fail_len; - repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]); - idx += 2; - if (idx > raw->curlen) - goto fail_len; - return true; -fail_len: - DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen); - return false; -} - -static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_msg_rx *raw, - struct drm_dp_sideband_msg_reply_body *repmsg) -{ - int idx = 1; - repmsg->u.allocate_payload.port_number = (raw->msg[idx] >> 4) & 0xf; - idx++; - if (idx > raw->curlen) - goto fail_len; - repmsg->u.allocate_payload.vcpi = raw->msg[idx]; - idx++; - if (idx > raw->curlen) - goto fail_len; - repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]); - idx += 2; - if (idx > raw->curlen) - goto fail_len; - return true; -fail_len: - DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen); - return false; -} - -static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_rx *raw, - struct drm_dp_sideband_msg_reply_body *repmsg) -{ - int idx = 1; - repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf; - idx++; - if (idx > raw->curlen) - goto fail_len; - repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]); - idx += 2; - if (idx > raw->curlen) - goto fail_len; - return true; -fail_len: - DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen); - return false; -} - -static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw, - struct drm_dp_sideband_msg_reply_body *msg) -{ - memset(msg, 0, sizeof(*msg)); - msg->reply_type = (raw->msg[0] & 0x80) >> 7; - msg->req_type = (raw->msg[0] & 0x7f); - - if (msg->reply_type) { - memcpy(msg->u.nak.guid, &raw->msg[1], 16); - msg->u.nak.reason = raw->msg[17]; - msg->u.nak.nak_data = raw->msg[18]; - return false; - } - - switch (msg->req_type) { - case DP_LINK_ADDRESS: - return drm_dp_sideband_parse_link_address(raw, msg); - case DP_QUERY_PAYLOAD: - return drm_dp_sideband_parse_query_payload_ack(raw, msg); - case DP_REMOTE_DPCD_READ: - return drm_dp_sideband_parse_remote_dpcd_read(raw, msg); - case DP_REMOTE_DPCD_WRITE: - return drm_dp_sideband_parse_remote_dpcd_write(raw, msg); - case DP_REMOTE_I2C_READ: - return drm_dp_sideband_parse_remote_i2c_read_ack(raw, msg); - case DP_ENUM_PATH_RESOURCES: - return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg); - case DP_ALLOCATE_PAYLOAD: - return drm_dp_sideband_parse_allocate_payload_ack(raw, msg); - default: - DRM_ERROR("Got unknown reply 0x%02x\n", msg->req_type); - return false; - } -} - -static bool drm_dp_sideband_parse_connection_status_notify(struct drm_dp_sideband_msg_rx *raw, - struct drm_dp_sideband_msg_req_body *msg) -{ - int idx = 1; - - msg->u.conn_stat.port_number = (raw->msg[idx] & 0xf0) >> 4; - idx++; - if (idx > raw->curlen) - goto fail_len; - - memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16); - idx += 16; - if (idx > raw->curlen) - goto fail_len; - - msg->u.conn_stat.legacy_device_plug_status = (raw->msg[idx] >> 6) & 0x1; - msg->u.conn_stat.displayport_device_plug_status = (raw->msg[idx] >> 5) & 0x1; - msg->u.conn_stat.message_capability_status = (raw->msg[idx] >> 4) & 0x1; - msg->u.conn_stat.input_port = (raw->msg[idx] >> 3) & 0x1; - msg->u.conn_stat.peer_device_type = (raw->msg[idx] & 0x7); - idx++; - return true; -fail_len: - DRM_DEBUG_KMS("connection status reply parse length fail %d %d\n", idx, raw->curlen); - return false; -} - -static bool drm_dp_sideband_parse_resource_status_notify(struct drm_dp_sideband_msg_rx *raw, - struct drm_dp_sideband_msg_req_body *msg) -{ - int idx = 1; - - msg->u.resource_stat.port_number = (raw->msg[idx] & 0xf0) >> 4; - idx++; - if (idx > raw->curlen) - goto fail_len; - - memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16); - idx += 16; - if (idx > raw->curlen) - goto fail_len; - - msg->u.resource_stat.available_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]); - idx++; - return true; -fail_len: - DRM_DEBUG_KMS("resource status reply parse length fail %d %d\n", idx, raw->curlen); - return false; -} - -static bool drm_dp_sideband_parse_req(struct drm_dp_sideband_msg_rx *raw, - struct drm_dp_sideband_msg_req_body *msg) -{ - memset(msg, 0, sizeof(*msg)); - msg->req_type = (raw->msg[0] & 0x7f); - - switch (msg->req_type) { - case DP_CONNECTION_STATUS_NOTIFY: - return drm_dp_sideband_parse_connection_status_notify(raw, msg); - case DP_RESOURCE_STATUS_NOTIFY: - return drm_dp_sideband_parse_resource_status_notify(raw, msg); - default: - DRM_ERROR("Got unknown request 0x%02x\n", msg->req_type); - return false; - } -} - -static int build_dpcd_write(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes, u8 *bytes) -{ - struct drm_dp_sideband_msg_req_body req; - - req.req_type = DP_REMOTE_DPCD_WRITE; - req.u.dpcd_write.port_number = port_num; - req.u.dpcd_write.dpcd_address = offset; - req.u.dpcd_write.num_bytes = num_bytes; - req.u.dpcd_write.bytes = bytes; - drm_dp_encode_sideband_req(&req, msg); - - return 0; -} - -static int build_link_address(struct drm_dp_sideband_msg_tx *msg) -{ - struct drm_dp_sideband_msg_req_body req; - - req.req_type = DP_LINK_ADDRESS; - drm_dp_encode_sideband_req(&req, msg); - return 0; -} - -static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg, int port_num) -{ - struct drm_dp_sideband_msg_req_body req; - - req.req_type = DP_ENUM_PATH_RESOURCES; - req.u.port_num.port_number = port_num; - drm_dp_encode_sideband_req(&req, msg); - msg->path_msg = true; - return 0; -} - -static int build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, int port_num, - u8 vcpi, uint16_t pbn) -{ - struct drm_dp_sideband_msg_req_body req; - memset(&req, 0, sizeof(req)); - req.req_type = DP_ALLOCATE_PAYLOAD; - req.u.allocate_payload.port_number = port_num; - req.u.allocate_payload.vcpi = vcpi; - req.u.allocate_payload.pbn = pbn; - drm_dp_encode_sideband_req(&req, msg); - msg->path_msg = true; - return 0; -} - -static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_vcpi *vcpi) -{ - int ret, vcpi_ret; - - mutex_lock(&mgr->payload_lock); - ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1); - if (ret > mgr->max_payloads) { - ret = -EINVAL; - DRM_DEBUG_KMS("out of payload ids %d\n", ret); - goto out_unlock; - } - - vcpi_ret = find_first_zero_bit(&mgr->vcpi_mask, mgr->max_payloads + 1); - if (vcpi_ret > mgr->max_payloads) { - ret = -EINVAL; - DRM_DEBUG_KMS("out of vcpi ids %d\n", ret); - goto out_unlock; - } - - set_bit(ret, &mgr->payload_mask); - set_bit(vcpi_ret, &mgr->vcpi_mask); - vcpi->vcpi = vcpi_ret + 1; - mgr->proposed_vcpis[ret - 1] = vcpi; -out_unlock: - mutex_unlock(&mgr->payload_lock); - return ret; -} - -static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr, - int vcpi) -{ - int i; - if (vcpi == 0) - return; - - mutex_lock(&mgr->payload_lock); - DRM_DEBUG_KMS("putting payload %d\n", vcpi); - clear_bit(vcpi - 1, &mgr->vcpi_mask); - - for (i = 0; i < mgr->max_payloads; i++) { - if (mgr->proposed_vcpis[i]) - if (mgr->proposed_vcpis[i]->vcpi == vcpi) { - mgr->proposed_vcpis[i] = NULL; - clear_bit(i + 1, &mgr->payload_mask); - } - } - mutex_unlock(&mgr->payload_lock); -} - -static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_sideband_msg_tx *txmsg) -{ - bool ret; - - /* - * All updates to txmsg->state are protected by mgr->qlock, and the two - * cases we check here are terminal states. For those the barriers - * provided by the wake_up/wait_event pair are enough. - */ - ret = (txmsg->state == DRM_DP_SIDEBAND_TX_RX || - txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT); - return ret; -} - -static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, - struct drm_dp_sideband_msg_tx *txmsg) -{ - struct drm_dp_mst_topology_mgr *mgr = mstb->mgr; - int ret; - - ret = wait_event_timeout(mgr->tx_waitq, - check_txmsg_state(mgr, txmsg), - (4 * HZ)); - mutex_lock(&mstb->mgr->qlock); - if (ret > 0) { - if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) { - ret = -EIO; - goto out; - } - } else { - DRM_DEBUG_KMS("timedout msg send %p %d %d\n", txmsg, txmsg->state, txmsg->seqno); - - /* dump some state */ - ret = -EIO; - - /* remove from q */ - if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED || - txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND) { - list_del(&txmsg->next); - } - - if (txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND || - txmsg->state == DRM_DP_SIDEBAND_TX_SENT) { - mstb->tx_slots[txmsg->seqno] = NULL; - } - } -out: - mutex_unlock(&mgr->qlock); - - return ret; -} - -static struct drm_dp_mst_branch *drm_dp_add_mst_branch_device(u8 lct, u8 *rad) -{ - struct drm_dp_mst_branch *mstb; - - mstb = kzalloc(sizeof(*mstb), GFP_KERNEL); - if (!mstb) - return NULL; - - mstb->lct = lct; - if (lct > 1) - memcpy(mstb->rad, rad, lct / 2); - INIT_LIST_HEAD(&mstb->ports); - kref_init(&mstb->kref); - return mstb; -} - -static void drm_dp_free_mst_port(struct kref *kref); - -static void drm_dp_free_mst_branch_device(struct kref *kref) -{ - struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref); - if (mstb->port_parent) { - if (list_empty(&mstb->port_parent->next)) - kref_put(&mstb->port_parent->kref, drm_dp_free_mst_port); - } - kfree(mstb); -} - -static void drm_dp_destroy_mst_branch_device(struct kref *kref) -{ - struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref); - struct drm_dp_mst_port *port, *tmp; - bool wake_tx = false; - - /* - * init kref again to be used by ports to remove mst branch when it is - * not needed anymore - */ - kref_init(kref); - - if (mstb->port_parent && list_empty(&mstb->port_parent->next)) - kref_get(&mstb->port_parent->kref); - - /* - * destroy all ports - don't need lock - * as there are no more references to the mst branch - * device at this point. - */ - list_for_each_entry_safe(port, tmp, &mstb->ports, next) { - list_del(&port->next); - drm_dp_put_port(port); - } - - /* drop any tx slots msg */ - mutex_lock(&mstb->mgr->qlock); - if (mstb->tx_slots[0]) { - mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT; - mstb->tx_slots[0] = NULL; - wake_tx = true; - } - if (mstb->tx_slots[1]) { - mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT; - mstb->tx_slots[1] = NULL; - wake_tx = true; - } - mutex_unlock(&mstb->mgr->qlock); - - if (wake_tx) - wake_up(&mstb->mgr->tx_waitq); - - kref_put(kref, drm_dp_free_mst_branch_device); -} - -static void drm_dp_put_mst_branch_device(struct drm_dp_mst_branch *mstb) -{ - kref_put(&mstb->kref, drm_dp_destroy_mst_branch_device); -} - - -static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt) -{ - struct drm_dp_mst_branch *mstb; - - switch (old_pdt) { - case DP_PEER_DEVICE_DP_LEGACY_CONV: - case DP_PEER_DEVICE_SST_SINK: - /* remove i2c over sideband */ - drm_dp_mst_unregister_i2c_bus(&port->aux); - break; - case DP_PEER_DEVICE_MST_BRANCHING: - mstb = port->mstb; - port->mstb = NULL; - drm_dp_put_mst_branch_device(mstb); - break; - } -} - -static void drm_dp_destroy_port(struct kref *kref) -{ - struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref); - struct drm_dp_mst_topology_mgr *mgr = port->mgr; - - if (!port->input) { - port->vcpi.num_slots = 0; - - kfree(port->cached_edid); - - /* - * The only time we don't have a connector - * on an output port is if the connector init - * fails. - */ - if (port->connector) { - /* we can't destroy the connector here, as - * we might be holding the mode_config.mutex - * from an EDID retrieval */ - - mutex_lock(&mgr->destroy_connector_lock); - kref_get(&port->parent->kref); - list_add(&port->next, &mgr->destroy_connector_list); - mutex_unlock(&mgr->destroy_connector_lock); - schedule_work(&mgr->destroy_connector_work); - return; - } - /* no need to clean up vcpi - * as if we have no connector we never setup a vcpi */ - drm_dp_port_teardown_pdt(port, port->pdt); - port->pdt = DP_PEER_DEVICE_NONE; - } - kfree(port); -} - -static void drm_dp_put_port(struct drm_dp_mst_port *port) -{ - kref_put(&port->kref, drm_dp_destroy_port); -} - -static struct drm_dp_mst_branch *drm_dp_mst_get_validated_mstb_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_branch *to_find) -{ - struct drm_dp_mst_port *port; - struct drm_dp_mst_branch *rmstb; - if (to_find == mstb) { - kref_get(&mstb->kref); - return mstb; - } - list_for_each_entry(port, &mstb->ports, next) { - if (port->mstb) { - rmstb = drm_dp_mst_get_validated_mstb_ref_locked(port->mstb, to_find); - if (rmstb) - return rmstb; - } - } - return NULL; -} - -static struct drm_dp_mst_branch *drm_dp_get_validated_mstb_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb) -{ - struct drm_dp_mst_branch *rmstb = NULL; - mutex_lock(&mgr->lock); - if (mgr->mst_primary) - rmstb = drm_dp_mst_get_validated_mstb_ref_locked(mgr->mst_primary, mstb); - mutex_unlock(&mgr->lock); - return rmstb; -} - -static struct drm_dp_mst_port *drm_dp_mst_get_port_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_port *to_find) -{ - struct drm_dp_mst_port *port, *mport; - - list_for_each_entry(port, &mstb->ports, next) { - if (port == to_find) { - kref_get(&port->kref); - return port; - } - if (port->mstb) { - mport = drm_dp_mst_get_port_ref_locked(port->mstb, to_find); - if (mport) - return mport; - } - } - return NULL; -} - -static struct drm_dp_mst_port *drm_dp_get_validated_port_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) -{ - struct drm_dp_mst_port *rport = NULL; - mutex_lock(&mgr->lock); - if (mgr->mst_primary) - rport = drm_dp_mst_get_port_ref_locked(mgr->mst_primary, port); - mutex_unlock(&mgr->lock); - return rport; -} - -static struct drm_dp_mst_port *drm_dp_get_port(struct drm_dp_mst_branch *mstb, u8 port_num) -{ - struct drm_dp_mst_port *port; - - list_for_each_entry(port, &mstb->ports, next) { - if (port->port_num == port_num) { - kref_get(&port->kref); - return port; - } - } - - return NULL; -} - -/* - * calculate a new RAD for this MST branch device - * if parent has an LCT of 2 then it has 1 nibble of RAD, - * if parent has an LCT of 3 then it has 2 nibbles of RAD, - */ -static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port, - u8 *rad) -{ - int parent_lct = port->parent->lct; - int shift = 4; - int idx = (parent_lct - 1) / 2; - if (parent_lct > 1) { - memcpy(rad, port->parent->rad, idx + 1); - shift = (parent_lct % 2) ? 4 : 0; - } else - rad[0] = 0; - - rad[idx] |= port->port_num << shift; - return parent_lct + 1; -} - -/* - * return sends link address for new mstb - */ -static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port) -{ - int ret; - u8 rad[6], lct; - bool send_link = false; - switch (port->pdt) { - case DP_PEER_DEVICE_DP_LEGACY_CONV: - case DP_PEER_DEVICE_SST_SINK: - /* add i2c over sideband */ - ret = drm_dp_mst_register_i2c_bus(&port->aux); - break; - case DP_PEER_DEVICE_MST_BRANCHING: - lct = drm_dp_calculate_rad(port, rad); - - port->mstb = drm_dp_add_mst_branch_device(lct, rad); - port->mstb->mgr = port->mgr; - port->mstb->port_parent = port; - - send_link = true; - break; - } - return send_link; -} - -static void drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, u8 *guid) -{ - int ret; - - memcpy(mstb->guid, guid, 16); - - if (!drm_dp_validate_guid(mstb->mgr, mstb->guid)) { - if (mstb->port_parent) { - ret = drm_dp_send_dpcd_write( - mstb->mgr, - mstb->port_parent, - DP_GUID, - 16, - mstb->guid); - } else { - - ret = drm_dp_dpcd_write( - mstb->mgr->aux, - DP_GUID, - mstb->guid, - 16); - } - } -} - -static void build_mst_prop_path(const struct drm_dp_mst_branch *mstb, - int pnum, - char *proppath, - size_t proppath_size) -{ - int i; - char temp[8]; - snprintf(proppath, proppath_size, "mst:%d", mstb->mgr->conn_base_id); - for (i = 0; i < (mstb->lct - 1); i++) { - int shift = (i % 2) ? 0 : 4; - int port_num = (mstb->rad[i / 2] >> shift) & 0xf; - snprintf(temp, sizeof(temp), "-%d", port_num); - strlcat(proppath, temp, proppath_size); - } - snprintf(temp, sizeof(temp), "-%d", pnum); - strlcat(proppath, temp, proppath_size); -} - -static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, - struct device *dev, - struct drm_dp_link_addr_reply_port *port_msg) -{ - struct drm_dp_mst_port *port; - bool ret; - bool created = false; - int old_pdt = 0; - int old_ddps = 0; - port = drm_dp_get_port(mstb, port_msg->port_number); - if (!port) { - port = kzalloc(sizeof(*port), GFP_KERNEL); - if (!port) - return; - kref_init(&port->kref); - port->parent = mstb; - port->port_num = port_msg->port_number; - port->mgr = mstb->mgr; - port->aux.name = "DPMST"; - port->aux.dev = dev; - created = true; - } else { - old_pdt = port->pdt; - old_ddps = port->ddps; - } - - port->pdt = port_msg->peer_device_type; - port->input = port_msg->input_port; - port->mcs = port_msg->mcs; - port->ddps = port_msg->ddps; - port->ldps = port_msg->legacy_device_plug_status; - port->dpcd_rev = port_msg->dpcd_revision; - port->num_sdp_streams = port_msg->num_sdp_streams; - port->num_sdp_stream_sinks = port_msg->num_sdp_stream_sinks; - - /* manage mstb port lists with mgr lock - take a reference - for this list */ - if (created) { - mutex_lock(&mstb->mgr->lock); - kref_get(&port->kref); - list_add(&port->next, &mstb->ports); - mutex_unlock(&mstb->mgr->lock); - } - - if (old_ddps != port->ddps) { - if (port->ddps) { - if (!port->input) - drm_dp_send_enum_path_resources(mstb->mgr, mstb, port); - } else { - port->available_pbn = 0; - } - } - - if (old_pdt != port->pdt && !port->input) { - drm_dp_port_teardown_pdt(port, old_pdt); - - ret = drm_dp_port_setup_pdt(port); - if (ret == true) - drm_dp_send_link_address(mstb->mgr, port->mstb); - } - - if (created && !port->input) { - char proppath[255]; - - build_mst_prop_path(mstb, port->port_num, proppath, sizeof(proppath)); - port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath); - if (!port->connector) { - /* remove it from the port list */ - mutex_lock(&mstb->mgr->lock); - list_del(&port->next); - mutex_unlock(&mstb->mgr->lock); - /* drop port list reference */ - drm_dp_put_port(port); - goto out; - } - if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV || - port->pdt == DP_PEER_DEVICE_SST_SINK) && - port->port_num >= DP_MST_LOGICAL_PORT_0) { - port->cached_edid = drm_get_edid(port->connector, &port->aux.ddc); - drm_mode_connector_set_tile_property(port->connector); - } - (*mstb->mgr->cbs->register_connector)(port->connector); - } - -out: - /* put reference to this port */ - drm_dp_put_port(port); -} - -static void drm_dp_update_port(struct drm_dp_mst_branch *mstb, - struct drm_dp_connection_status_notify *conn_stat) -{ - struct drm_dp_mst_port *port; - int old_pdt; - int old_ddps; - bool dowork = false; - port = drm_dp_get_port(mstb, conn_stat->port_number); - if (!port) - return; - - old_ddps = port->ddps; - old_pdt = port->pdt; - port->pdt = conn_stat->peer_device_type; - port->mcs = conn_stat->message_capability_status; - port->ldps = conn_stat->legacy_device_plug_status; - port->ddps = conn_stat->displayport_device_plug_status; - - if (old_ddps != port->ddps) { - if (port->ddps) { - dowork = true; - } else { - port->available_pbn = 0; - } - } - if (old_pdt != port->pdt && !port->input) { - drm_dp_port_teardown_pdt(port, old_pdt); - - if (drm_dp_port_setup_pdt(port)) - dowork = true; - } - - drm_dp_put_port(port); - if (dowork) - queue_work(system_long_wq, &mstb->mgr->work); - -} - -static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr, - u8 lct, u8 *rad) -{ - struct drm_dp_mst_branch *mstb; - struct drm_dp_mst_port *port; - int i; - /* find the port by iterating down */ - - mutex_lock(&mgr->lock); - mstb = mgr->mst_primary; - - if (!mstb) - goto out; - - for (i = 0; i < lct - 1; i++) { - int shift = (i % 2) ? 0 : 4; - int port_num = (rad[i / 2] >> shift) & 0xf; - - list_for_each_entry(port, &mstb->ports, next) { - if (port->port_num == port_num) { - mstb = port->mstb; - if (!mstb) { - DRM_ERROR("failed to lookup MSTB with lct %d, rad %02x\n", lct, rad[0]); - goto out; - } - - break; - } - } - } - kref_get(&mstb->kref); -out: - mutex_unlock(&mgr->lock); - return mstb; -} - -static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper( - struct drm_dp_mst_branch *mstb, - uint8_t *guid) -{ - struct drm_dp_mst_branch *found_mstb; - struct drm_dp_mst_port *port; - - if (memcmp(mstb->guid, guid, 16) == 0) - return mstb; - - - list_for_each_entry(port, &mstb->ports, next) { - if (!port->mstb) - continue; - - found_mstb = get_mst_branch_device_by_guid_helper(port->mstb, guid); - - if (found_mstb) - return found_mstb; - } - - return NULL; -} - -static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device_by_guid( - struct drm_dp_mst_topology_mgr *mgr, - uint8_t *guid) -{ - struct drm_dp_mst_branch *mstb; - - /* find the port by iterating down */ - mutex_lock(&mgr->lock); - - mstb = get_mst_branch_device_by_guid_helper(mgr->mst_primary, guid); - - if (mstb) - kref_get(&mstb->kref); - - mutex_unlock(&mgr->lock); - return mstb; -} - -static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_branch *mstb) -{ - struct drm_dp_mst_port *port; - struct drm_dp_mst_branch *mstb_child; - if (!mstb->link_address_sent) - drm_dp_send_link_address(mgr, mstb); - - list_for_each_entry(port, &mstb->ports, next) { - if (port->input) - continue; - - if (!port->ddps) - continue; - - if (!port->available_pbn) - drm_dp_send_enum_path_resources(mgr, mstb, port); - - if (port->mstb) { - mstb_child = drm_dp_get_validated_mstb_ref(mgr, port->mstb); - if (mstb_child) { - drm_dp_check_and_send_link_address(mgr, mstb_child); - drm_dp_put_mst_branch_device(mstb_child); - } - } - } -} - -static void drm_dp_mst_link_probe_work(struct work_struct *work) -{ - struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work); - struct drm_dp_mst_branch *mstb; - - mutex_lock(&mgr->lock); - mstb = mgr->mst_primary; - if (mstb) { - kref_get(&mstb->kref); - } - mutex_unlock(&mgr->lock); - if (mstb) { - drm_dp_check_and_send_link_address(mgr, mstb); - drm_dp_put_mst_branch_device(mstb); - } -} - -static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, - u8 *guid) -{ - static u8 zero_guid[16]; - - if (!memcmp(guid, zero_guid, 16)) { - u64 salt = get_jiffies_64(); - memcpy(&guid[0], &salt, sizeof(u64)); - memcpy(&guid[8], &salt, sizeof(u64)); - return false; - } - return true; -} - -#if 0 -static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes) -{ - struct drm_dp_sideband_msg_req_body req; - - req.req_type = DP_REMOTE_DPCD_READ; - req.u.dpcd_read.port_number = port_num; - req.u.dpcd_read.dpcd_address = offset; - req.u.dpcd_read.num_bytes = num_bytes; - drm_dp_encode_sideband_req(&req, msg); - - return 0; -} -#endif - -static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr, - bool up, u8 *msg, int len) -{ - int ret; - int regbase = up ? DP_SIDEBAND_MSG_UP_REP_BASE : DP_SIDEBAND_MSG_DOWN_REQ_BASE; - int tosend, total, offset; - int retries = 0; - -retry: - total = len; - offset = 0; - do { - tosend = min3(mgr->max_dpcd_transaction_bytes, 16, total); - - ret = drm_dp_dpcd_write(mgr->aux, regbase + offset, - &msg[offset], - tosend); - if (ret != tosend) { - if (ret == -EIO && retries < 5) { - retries++; - goto retry; - } - DRM_DEBUG_KMS("failed to dpcd write %d %d\n", tosend, ret); - - return -EIO; - } - offset += tosend; - total -= tosend; - } while (total > 0); - return 0; -} - -static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr, - struct drm_dp_sideband_msg_tx *txmsg) -{ - struct drm_dp_mst_branch *mstb = txmsg->dst; - u8 req_type; - - /* both msg slots are full */ - if (txmsg->seqno == -1) { - if (mstb->tx_slots[0] && mstb->tx_slots[1]) { - DRM_DEBUG_KMS("%s: failed to find slot\n", __func__); - return -EAGAIN; - } - if (mstb->tx_slots[0] == NULL && mstb->tx_slots[1] == NULL) { - txmsg->seqno = mstb->last_seqno; - mstb->last_seqno ^= 1; - } else if (mstb->tx_slots[0] == NULL) - txmsg->seqno = 0; - else - txmsg->seqno = 1; - mstb->tx_slots[txmsg->seqno] = txmsg; - } - - req_type = txmsg->msg[0] & 0x7f; - if (req_type == DP_CONNECTION_STATUS_NOTIFY || - req_type == DP_RESOURCE_STATUS_NOTIFY) - hdr->broadcast = 1; - else - hdr->broadcast = 0; - hdr->path_msg = txmsg->path_msg; - hdr->lct = mstb->lct; - hdr->lcr = mstb->lct - 1; - if (mstb->lct > 1) - memcpy(hdr->rad, mstb->rad, mstb->lct / 2); - hdr->seqno = txmsg->seqno; - return 0; -} -/* - * process a single block of the next message in the sideband queue - */ -static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_sideband_msg_tx *txmsg, - bool up) -{ - u8 chunk[48]; - struct drm_dp_sideband_msg_hdr hdr; - int len, space, idx, tosend; - int ret; - - memset(&hdr, 0, sizeof(struct drm_dp_sideband_msg_hdr)); - - if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED) { - txmsg->seqno = -1; - txmsg->state = DRM_DP_SIDEBAND_TX_START_SEND; - } - - /* make hdr from dst mst - for replies use seqno - otherwise assign one */ - ret = set_hdr_from_dst_qlock(&hdr, txmsg); - if (ret < 0) - return ret; - - /* amount left to send in this message */ - len = txmsg->cur_len - txmsg->cur_offset; - - /* 48 - sideband msg size - 1 byte for data CRC, x header bytes */ - space = 48 - 1 - drm_dp_calc_sb_hdr_size(&hdr); - - tosend = min(len, space); - if (len == txmsg->cur_len) - hdr.somt = 1; - if (space >= len) - hdr.eomt = 1; - - - hdr.msg_len = tosend + 1; - drm_dp_encode_sideband_msg_hdr(&hdr, chunk, &idx); - memcpy(&chunk[idx], &txmsg->msg[txmsg->cur_offset], tosend); - /* add crc at end */ - drm_dp_crc_sideband_chunk_req(&chunk[idx], tosend); - idx += tosend + 1; - - ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx); - if (ret) { - DRM_DEBUG_KMS("sideband msg failed to send\n"); - return ret; - } - - txmsg->cur_offset += tosend; - if (txmsg->cur_offset == txmsg->cur_len) { - txmsg->state = DRM_DP_SIDEBAND_TX_SENT; - return 1; - } - return 0; -} - -static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr) -{ - struct drm_dp_sideband_msg_tx *txmsg; - int ret; - - WARN_ON(!mutex_is_locked(&mgr->qlock)); - - /* construct a chunk from the first msg in the tx_msg queue */ - if (list_empty(&mgr->tx_msg_downq)) { - mgr->tx_down_in_progress = false; - return; - } - mgr->tx_down_in_progress = true; - - txmsg = list_first_entry(&mgr->tx_msg_downq, struct drm_dp_sideband_msg_tx, next); - ret = process_single_tx_qlock(mgr, txmsg, false); - if (ret == 1) { - /* txmsg is sent it should be in the slots now */ - list_del(&txmsg->next); - } else if (ret) { - DRM_DEBUG_KMS("failed to send msg in q %d\n", ret); - list_del(&txmsg->next); - if (txmsg->seqno != -1) - txmsg->dst->tx_slots[txmsg->seqno] = NULL; - txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT; - wake_up(&mgr->tx_waitq); - } - if (list_empty(&mgr->tx_msg_downq)) { - mgr->tx_down_in_progress = false; - return; - } -} - -/* called holding qlock */ -static void process_single_up_tx_qlock(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_sideband_msg_tx *txmsg) -{ - int ret; - - /* construct a chunk from the first msg in the tx_msg queue */ - ret = process_single_tx_qlock(mgr, txmsg, true); - - if (ret != 1) - DRM_DEBUG_KMS("failed to send msg in q %d\n", ret); - - txmsg->dst->tx_slots[txmsg->seqno] = NULL; -} - -static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_sideband_msg_tx *txmsg) -{ - mutex_lock(&mgr->qlock); - list_add_tail(&txmsg->next, &mgr->tx_msg_downq); - if (!mgr->tx_down_in_progress) - process_single_down_tx_qlock(mgr); - mutex_unlock(&mgr->qlock); -} - -static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_branch *mstb) -{ - int len; - struct drm_dp_sideband_msg_tx *txmsg; - int ret; - - txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); - if (!txmsg) - return; - - txmsg->dst = mstb; - len = build_link_address(txmsg); - - mstb->link_address_sent = true; - drm_dp_queue_down_tx(mgr, txmsg); - - ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); - if (ret > 0) { - int i; - - if (txmsg->reply.reply_type == 1) - DRM_DEBUG_KMS("link address nak received\n"); - else { - DRM_DEBUG_KMS("link address reply: %d\n", txmsg->reply.u.link_addr.nports); - for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) { - DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n", i, - txmsg->reply.u.link_addr.ports[i].input_port, - txmsg->reply.u.link_addr.ports[i].peer_device_type, - txmsg->reply.u.link_addr.ports[i].port_number, - txmsg->reply.u.link_addr.ports[i].dpcd_revision, - txmsg->reply.u.link_addr.ports[i].mcs, - txmsg->reply.u.link_addr.ports[i].ddps, - txmsg->reply.u.link_addr.ports[i].legacy_device_plug_status, - txmsg->reply.u.link_addr.ports[i].num_sdp_streams, - txmsg->reply.u.link_addr.ports[i].num_sdp_stream_sinks); - } - - drm_dp_check_mstb_guid(mstb, txmsg->reply.u.link_addr.guid); - - for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) { - drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]); - } - (*mgr->cbs->hotplug)(mgr); - } - } else { - mstb->link_address_sent = false; - DRM_DEBUG_KMS("link address failed %d\n", ret); - } - - kfree(txmsg); -} - -static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_branch *mstb, - struct drm_dp_mst_port *port) -{ - int len; - struct drm_dp_sideband_msg_tx *txmsg; - int ret; - - txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); - if (!txmsg) - return -ENOMEM; - - txmsg->dst = mstb; - len = build_enum_path_resources(txmsg, port->port_num); - - drm_dp_queue_down_tx(mgr, txmsg); - - ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); - if (ret > 0) { - if (txmsg->reply.reply_type == 1) - DRM_DEBUG_KMS("enum path resources nak received\n"); - else { - if (port->port_num != txmsg->reply.u.path_resources.port_number) - DRM_ERROR("got incorrect port in response\n"); - DRM_DEBUG_KMS("enum path resources %d: %d %d\n", txmsg->reply.u.path_resources.port_number, txmsg->reply.u.path_resources.full_payload_bw_number, - txmsg->reply.u.path_resources.avail_payload_bw_number); - port->available_pbn = txmsg->reply.u.path_resources.avail_payload_bw_number; - } - } - - kfree(txmsg); - return 0; -} - -static struct drm_dp_mst_port *drm_dp_get_last_connected_port_to_mstb(struct drm_dp_mst_branch *mstb) -{ - if (!mstb->port_parent) - return NULL; - - if (mstb->port_parent->mstb != mstb) - return mstb->port_parent; - - return drm_dp_get_last_connected_port_to_mstb(mstb->port_parent->parent); -} - -static struct drm_dp_mst_branch *drm_dp_get_last_connected_port_and_mstb(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_branch *mstb, - int *port_num) -{ - struct drm_dp_mst_branch *rmstb = NULL; - struct drm_dp_mst_port *found_port; - mutex_lock(&mgr->lock); - if (mgr->mst_primary) { - found_port = drm_dp_get_last_connected_port_to_mstb(mstb); - - if (found_port) { - rmstb = found_port->parent; - kref_get(&rmstb->kref); - *port_num = found_port->port_num; - } - } - mutex_unlock(&mgr->lock); - return rmstb; -} - -static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, - int id, - int pbn) -{ - struct drm_dp_sideband_msg_tx *txmsg; - struct drm_dp_mst_branch *mstb; - int len, ret, port_num; - - port = drm_dp_get_validated_port_ref(mgr, port); - if (!port) - return -EINVAL; - - port_num = port->port_num; - mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent); - if (!mstb) { - mstb = drm_dp_get_last_connected_port_and_mstb(mgr, port->parent, &port_num); - - if (!mstb) { - drm_dp_put_port(port); - return -EINVAL; - } - } - - txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); - if (!txmsg) { - ret = -ENOMEM; - goto fail_put; - } - - txmsg->dst = mstb; - len = build_allocate_payload(txmsg, port_num, - id, - pbn); - - drm_dp_queue_down_tx(mgr, txmsg); - - ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); - if (ret > 0) { - if (txmsg->reply.reply_type == 1) { - ret = -EINVAL; - } else - ret = 0; - } - kfree(txmsg); -fail_put: - drm_dp_put_mst_branch_device(mstb); - drm_dp_put_port(port); - return ret; -} - -static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr, - int id, - struct drm_dp_payload *payload) -{ - int ret; - - ret = drm_dp_dpcd_write_payload(mgr, id, payload); - if (ret < 0) { - payload->payload_state = 0; - return ret; - } - payload->payload_state = DP_PAYLOAD_LOCAL; - return 0; -} - -static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, - int id, - struct drm_dp_payload *payload) -{ - int ret; - ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn); - if (ret < 0) - return ret; - payload->payload_state = DP_PAYLOAD_REMOTE; - return ret; -} - -static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, - int id, - struct drm_dp_payload *payload) -{ - DRM_DEBUG_KMS("\n"); - /* its okay for these to fail */ - if (port) { - drm_dp_payload_send_msg(mgr, port, id, 0); - } - - drm_dp_dpcd_write_payload(mgr, id, payload); - payload->payload_state = DP_PAYLOAD_DELETE_LOCAL; - return 0; -} - -static int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr, - int id, - struct drm_dp_payload *payload) -{ - payload->payload_state = 0; - return 0; -} - -/** - * drm_dp_update_payload_part1() - Execute payload update part 1 - * @mgr: manager to use. - * - * This iterates over all proposed virtual channels, and tries to - * allocate space in the link for them. For 0->slots transitions, - * this step just writes the VCPI to the MST device. For slots->0 - * transitions, this writes the updated VCPIs and removes the - * remote VC payloads. - * - * after calling this the driver should generate ACT and payload - * packets. - */ -int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) -{ - int i, j; - int cur_slots = 1; - struct drm_dp_payload req_payload; - struct drm_dp_mst_port *port; - - mutex_lock(&mgr->payload_lock); - for (i = 0; i < mgr->max_payloads; i++) { - /* solve the current payloads - compare to the hw ones - - update the hw view */ - req_payload.start_slot = cur_slots; - if (mgr->proposed_vcpis[i]) { - port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); - port = drm_dp_get_validated_port_ref(mgr, port); - if (!port) { - mutex_unlock(&mgr->payload_lock); - return -EINVAL; - } - req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots; - req_payload.vcpi = mgr->proposed_vcpis[i]->vcpi; - } else { - port = NULL; - req_payload.num_slots = 0; - } - - if (mgr->payloads[i].start_slot != req_payload.start_slot) { - mgr->payloads[i].start_slot = req_payload.start_slot; - } - /* work out what is required to happen with this payload */ - if (mgr->payloads[i].num_slots != req_payload.num_slots) { - - /* need to push an update for this payload */ - if (req_payload.num_slots) { - drm_dp_create_payload_step1(mgr, mgr->proposed_vcpis[i]->vcpi, &req_payload); - mgr->payloads[i].num_slots = req_payload.num_slots; - mgr->payloads[i].vcpi = req_payload.vcpi; - } else if (mgr->payloads[i].num_slots) { - mgr->payloads[i].num_slots = 0; - drm_dp_destroy_payload_step1(mgr, port, mgr->payloads[i].vcpi, &mgr->payloads[i]); - req_payload.payload_state = mgr->payloads[i].payload_state; - mgr->payloads[i].start_slot = 0; - } - mgr->payloads[i].payload_state = req_payload.payload_state; - } - cur_slots += req_payload.num_slots; - - if (port) - drm_dp_put_port(port); - } - - for (i = 0; i < mgr->max_payloads; i++) { - if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) { - DRM_DEBUG_KMS("removing payload %d\n", i); - for (j = i; j < mgr->max_payloads - 1; j++) { - memcpy(&mgr->payloads[j], &mgr->payloads[j + 1], sizeof(struct drm_dp_payload)); - mgr->proposed_vcpis[j] = mgr->proposed_vcpis[j + 1]; - if (mgr->proposed_vcpis[j] && mgr->proposed_vcpis[j]->num_slots) { - set_bit(j + 1, &mgr->payload_mask); - } else { - clear_bit(j + 1, &mgr->payload_mask); - } - } - memset(&mgr->payloads[mgr->max_payloads - 1], 0, sizeof(struct drm_dp_payload)); - mgr->proposed_vcpis[mgr->max_payloads - 1] = NULL; - clear_bit(mgr->max_payloads, &mgr->payload_mask); - - } - } - mutex_unlock(&mgr->payload_lock); - - return 0; -} -EXPORT_SYMBOL(drm_dp_update_payload_part1); - -/** - * drm_dp_update_payload_part2() - Execute payload update part 2 - * @mgr: manager to use. - * - * This iterates over all proposed virtual channels, and tries to - * allocate space in the link for them. For 0->slots transitions, - * this step writes the remote VC payload commands. For slots->0 - * this just resets some internal state. - */ -int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr) -{ - struct drm_dp_mst_port *port; - int i; - int ret = 0; - mutex_lock(&mgr->payload_lock); - for (i = 0; i < mgr->max_payloads; i++) { - - if (!mgr->proposed_vcpis[i]) - continue; - - port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); - - DRM_DEBUG_KMS("payload %d %d\n", i, mgr->payloads[i].payload_state); - if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) { - ret = drm_dp_create_payload_step2(mgr, port, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]); - } else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) { - ret = drm_dp_destroy_payload_step2(mgr, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]); - } - if (ret) { - mutex_unlock(&mgr->payload_lock); - return ret; - } - } - mutex_unlock(&mgr->payload_lock); - return 0; -} -EXPORT_SYMBOL(drm_dp_update_payload_part2); - -#if 0 /* unused as of yet */ -static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, - int offset, int size) -{ - int len; - struct drm_dp_sideband_msg_tx *txmsg; - - txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); - if (!txmsg) - return -ENOMEM; - - len = build_dpcd_read(txmsg, port->port_num, 0, 8); - txmsg->dst = port->parent; - - drm_dp_queue_down_tx(mgr, txmsg); - - return 0; -} -#endif - -static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, - int offset, int size, u8 *bytes) -{ - int len; - int ret; - struct drm_dp_sideband_msg_tx *txmsg; - struct drm_dp_mst_branch *mstb; - - mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent); - if (!mstb) - return -EINVAL; - - txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); - if (!txmsg) { - ret = -ENOMEM; - goto fail_put; - } - - len = build_dpcd_write(txmsg, port->port_num, offset, size, bytes); - txmsg->dst = mstb; - - drm_dp_queue_down_tx(mgr, txmsg); - - ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); - if (ret > 0) { - if (txmsg->reply.reply_type == 1) { - ret = -EINVAL; - } else - ret = 0; - } - kfree(txmsg); -fail_put: - drm_dp_put_mst_branch_device(mstb); - return ret; -} - -static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req_type) -{ - struct drm_dp_sideband_msg_reply_body reply; - - reply.reply_type = 1; - reply.req_type = req_type; - drm_dp_encode_sideband_reply(&reply, msg); - return 0; -} - -static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_branch *mstb, - int req_type, int seqno, bool broadcast) -{ - struct drm_dp_sideband_msg_tx *txmsg; - - txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); - if (!txmsg) - return -ENOMEM; - - txmsg->dst = mstb; - txmsg->seqno = seqno; - drm_dp_encode_up_ack_reply(txmsg, req_type); - - mutex_lock(&mgr->qlock); - - process_single_up_tx_qlock(mgr, txmsg); - - mutex_unlock(&mgr->qlock); - - kfree(txmsg); - return 0; -} - -static bool drm_dp_get_vc_payload_bw(int dp_link_bw, - int dp_link_count, - int *out) -{ - switch (dp_link_bw) { - default: - DRM_DEBUG_KMS("invalid link bandwidth in DPCD: %x (link count: %d)\n", - dp_link_bw, dp_link_count); - return false; - - case DP_LINK_BW_1_62: - *out = 3 * dp_link_count; - break; - case DP_LINK_BW_2_7: - *out = 5 * dp_link_count; - break; - case DP_LINK_BW_5_4: - *out = 10 * dp_link_count; - break; - } - return true; -} - -/** - * drm_dp_mst_topology_mgr_set_mst() - Set the MST state for a topology manager - * @mgr: manager to set state for - * @mst_state: true to enable MST on this connector - false to disable. - * - * This is called by the driver when it detects an MST capable device plugged - * into a DP MST capable port, or when a DP MST capable device is unplugged. - */ -int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state) -{ - int ret = 0; - struct drm_dp_mst_branch *mstb = NULL; - - mutex_lock(&mgr->lock); - if (mst_state == mgr->mst_state) - goto out_unlock; - - mgr->mst_state = mst_state; - /* set the device into MST mode */ - if (mst_state) { - WARN_ON(mgr->mst_primary); - - /* get dpcd info */ - ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE); - if (ret != DP_RECEIVER_CAP_SIZE) { - DRM_DEBUG_KMS("failed to read DPCD\n"); - goto out_unlock; - } - - if (!drm_dp_get_vc_payload_bw(mgr->dpcd[1], - mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK, - &mgr->pbn_div)) { - ret = -EINVAL; - goto out_unlock; - } - - mgr->total_pbn = 2560; - mgr->total_slots = DIV_ROUND_UP(mgr->total_pbn, mgr->pbn_div); - mgr->avail_slots = mgr->total_slots; - - /* add initial branch device at LCT 1 */ - mstb = drm_dp_add_mst_branch_device(1, NULL); - if (mstb == NULL) { - ret = -ENOMEM; - goto out_unlock; - } - mstb->mgr = mgr; - - /* give this the main reference */ - mgr->mst_primary = mstb; - kref_get(&mgr->mst_primary->kref); - - ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, - DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC); - if (ret < 0) { - goto out_unlock; - } - - { - struct drm_dp_payload reset_pay; - reset_pay.start_slot = 0; - reset_pay.num_slots = 0x3f; - drm_dp_dpcd_write_payload(mgr, 0, &reset_pay); - } - - queue_work(system_long_wq, &mgr->work); - - ret = 0; - } else { - /* disable MST on the device */ - mstb = mgr->mst_primary; - mgr->mst_primary = NULL; - /* this can fail if the device is gone */ - drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0); - ret = 0; - memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload)); - mgr->payload_mask = 0; - set_bit(0, &mgr->payload_mask); - mgr->vcpi_mask = 0; - } - -out_unlock: - mutex_unlock(&mgr->lock); - if (mstb) - drm_dp_put_mst_branch_device(mstb); - return ret; - -} -EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst); - -/** - * drm_dp_mst_topology_mgr_suspend() - suspend the MST manager - * @mgr: manager to suspend - * - * This function tells the MST device that we can't handle UP messages - * anymore. This should stop it from sending any since we are suspended. - */ -void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr) -{ - mutex_lock(&mgr->lock); - drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, - DP_MST_EN | DP_UPSTREAM_IS_SRC); - mutex_unlock(&mgr->lock); - flush_work(&mgr->work); - flush_work(&mgr->destroy_connector_work); -} -EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend); - -/** - * drm_dp_mst_topology_mgr_resume() - resume the MST manager - * @mgr: manager to resume - * - * This will fetch DPCD and see if the device is still there, - * if it is, it will rewrite the MSTM control bits, and return. - * - * if the device fails this returns -1, and the driver should do - * a full MST reprobe, in case we were undocked. - */ -int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr) -{ - int ret = 0; - - mutex_lock(&mgr->lock); - - if (mgr->mst_primary) { - int sret; - u8 guid[16]; - - sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE); - if (sret != DP_RECEIVER_CAP_SIZE) { - DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n"); - ret = -1; - goto out_unlock; - } - - ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, - DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC); - if (ret < 0) { - DRM_DEBUG_KMS("mst write failed - undocked during suspend?\n"); - ret = -1; - goto out_unlock; - } - - /* Some hubs forget their guids after they resume */ - sret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16); - if (sret != 16) { - DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n"); - ret = -1; - goto out_unlock; - } - drm_dp_check_mstb_guid(mgr->mst_primary, guid); - - ret = 0; - } else - ret = -1; - -out_unlock: - mutex_unlock(&mgr->lock); - return ret; -} -EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume); - -static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) -{ - int len; - u8 replyblock[32]; - int replylen, origlen, curreply; - int ret; - struct drm_dp_sideband_msg_rx *msg; - int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE; - msg = up ? &mgr->up_req_recv : &mgr->down_rep_recv; - - len = min(mgr->max_dpcd_transaction_bytes, 16); - ret = drm_dp_dpcd_read(mgr->aux, basereg, - replyblock, len); - if (ret != len) { - DRM_DEBUG_KMS("failed to read DPCD down rep %d %d\n", len, ret); - return false; - } - ret = drm_dp_sideband_msg_build(msg, replyblock, len, true); - if (!ret) { - DRM_DEBUG_KMS("sideband msg build failed %d\n", replyblock[0]); - return false; - } - replylen = msg->curchunk_len + msg->curchunk_hdrlen; - - origlen = replylen; - replylen -= len; - curreply = len; - while (replylen > 0) { - len = min3(replylen, mgr->max_dpcd_transaction_bytes, 16); - ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply, - replyblock, len); - if (ret != len) { - DRM_DEBUG_KMS("failed to read a chunk (len %d, ret %d)\n", - len, ret); - return false; - } - - ret = drm_dp_sideband_msg_build(msg, replyblock, len, false); - if (!ret) { - DRM_DEBUG_KMS("failed to build sideband msg\n"); - return false; - } - - curreply += len; - replylen -= len; - } - return true; -} - -static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) -{ - int ret = 0; - - if (!drm_dp_get_one_sb_msg(mgr, false)) { - memset(&mgr->down_rep_recv, 0, - sizeof(struct drm_dp_sideband_msg_rx)); - return 0; - } - - if (mgr->down_rep_recv.have_eomt) { - struct drm_dp_sideband_msg_tx *txmsg; - struct drm_dp_mst_branch *mstb; - int slot = -1; - mstb = drm_dp_get_mst_branch_device(mgr, - mgr->down_rep_recv.initial_hdr.lct, - mgr->down_rep_recv.initial_hdr.rad); - - if (!mstb) { - DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->down_rep_recv.initial_hdr.lct); - memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); - return 0; - } - - /* find the message */ - slot = mgr->down_rep_recv.initial_hdr.seqno; - mutex_lock(&mgr->qlock); - txmsg = mstb->tx_slots[slot]; - /* remove from slots */ - mutex_unlock(&mgr->qlock); - - if (!txmsg) { - DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n", - mstb, - mgr->down_rep_recv.initial_hdr.seqno, - mgr->down_rep_recv.initial_hdr.lct, - mgr->down_rep_recv.initial_hdr.rad[0], - mgr->down_rep_recv.msg[0]); - drm_dp_put_mst_branch_device(mstb); - memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); - return 0; - } - - drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply); - if (txmsg->reply.reply_type == 1) { - DRM_DEBUG_KMS("Got NAK reply: req 0x%02x, reason 0x%02x, nak data 0x%02x\n", txmsg->reply.req_type, txmsg->reply.u.nak.reason, txmsg->reply.u.nak.nak_data); - } - - memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); - drm_dp_put_mst_branch_device(mstb); - - mutex_lock(&mgr->qlock); - txmsg->state = DRM_DP_SIDEBAND_TX_RX; - mstb->tx_slots[slot] = NULL; - mutex_unlock(&mgr->qlock); - - wake_up(&mgr->tx_waitq); - } - return ret; -} - -static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) -{ - int ret = 0; - - if (!drm_dp_get_one_sb_msg(mgr, true)) { - memset(&mgr->up_req_recv, 0, - sizeof(struct drm_dp_sideband_msg_rx)); - return 0; - } - - if (mgr->up_req_recv.have_eomt) { - struct drm_dp_sideband_msg_req_body msg; - struct drm_dp_mst_branch *mstb = NULL; - bool seqno; - - if (!mgr->up_req_recv.initial_hdr.broadcast) { - mstb = drm_dp_get_mst_branch_device(mgr, - mgr->up_req_recv.initial_hdr.lct, - mgr->up_req_recv.initial_hdr.rad); - if (!mstb) { - DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct); - memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); - return 0; - } - } - - seqno = mgr->up_req_recv.initial_hdr.seqno; - drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg); - - if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) { - drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false); - - if (!mstb) - mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.conn_stat.guid); - - if (!mstb) { - DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct); - memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); - return 0; - } - - drm_dp_update_port(mstb, &msg.u.conn_stat); - - DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type); - (*mgr->cbs->hotplug)(mgr); - - } else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) { - drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false); - if (!mstb) - mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.resource_stat.guid); - - if (!mstb) { - DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct); - memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); - return 0; - } - - DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn); - } - - if (mstb) - drm_dp_put_mst_branch_device(mstb); - - memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); - } - return ret; -} - -/** - * drm_dp_mst_hpd_irq() - MST hotplug IRQ notify - * @mgr: manager to notify irq for. - * @esi: 4 bytes from SINK_COUNT_ESI - * @handled: whether the hpd interrupt was consumed or not - * - * This should be called from the driver when it detects a short IRQ, - * along with the value of the DEVICE_SERVICE_IRQ_VECTOR_ESI0. The - * topology manager will process the sideband messages received as a result - * of this. - */ -int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled) -{ - int ret = 0; - int sc; - *handled = false; - sc = esi[0] & 0x3f; - - if (sc != mgr->sink_count) { - mgr->sink_count = sc; - *handled = true; - } - - if (esi[1] & DP_DOWN_REP_MSG_RDY) { - ret = drm_dp_mst_handle_down_rep(mgr); - *handled = true; - } - - if (esi[1] & DP_UP_REQ_MSG_RDY) { - ret |= drm_dp_mst_handle_up_req(mgr); - *handled = true; - } - - drm_dp_mst_kick_tx(mgr); - return ret; -} -EXPORT_SYMBOL(drm_dp_mst_hpd_irq); - -/** - * drm_dp_mst_detect_port() - get connection status for an MST port - * @mgr: manager for this port - * @port: unverified pointer to a port - * - * This returns the current connection state for a port. It validates the - * port pointer still exists so the caller doesn't require a reference - */ -enum drm_connector_status drm_dp_mst_detect_port(struct drm_connector *connector, - struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) -{ - enum drm_connector_status status = connector_status_disconnected; - - /* we need to search for the port in the mgr in case its gone */ - port = drm_dp_get_validated_port_ref(mgr, port); - if (!port) - return connector_status_disconnected; - - if (!port->ddps) - goto out; - - switch (port->pdt) { - case DP_PEER_DEVICE_NONE: - case DP_PEER_DEVICE_MST_BRANCHING: - break; - - case DP_PEER_DEVICE_SST_SINK: - status = connector_status_connected; - /* for logical ports - cache the EDID */ - if (port->port_num >= 8 && !port->cached_edid) { - port->cached_edid = drm_get_edid(connector, &port->aux.ddc); - } - break; - case DP_PEER_DEVICE_DP_LEGACY_CONV: - if (port->ldps) - status = connector_status_connected; - break; - } -out: - drm_dp_put_port(port); - return status; -} -EXPORT_SYMBOL(drm_dp_mst_detect_port); - -/** - * drm_dp_mst_get_edid() - get EDID for an MST port - * @connector: toplevel connector to get EDID for - * @mgr: manager for this port - * @port: unverified pointer to a port. - * - * This returns an EDID for the port connected to a connector, - * It validates the pointer still exists so the caller doesn't require a - * reference. - */ -struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) -{ - struct edid *edid = NULL; - - /* we need to search for the port in the mgr in case its gone */ - port = drm_dp_get_validated_port_ref(mgr, port); - if (!port) - return NULL; - - if (port->cached_edid) - edid = drm_edid_duplicate(port->cached_edid); - else { - edid = drm_get_edid(connector, &port->aux.ddc); - drm_mode_connector_set_tile_property(connector); - } - drm_dp_put_port(port); - return edid; -} -EXPORT_SYMBOL(drm_dp_mst_get_edid); - -/** - * drm_dp_find_vcpi_slots() - find slots for this PBN value - * @mgr: manager to use - * @pbn: payload bandwidth to convert into slots. - */ -int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, - int pbn) -{ - int num_slots; - - num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div); - - if (num_slots > mgr->avail_slots) - return -ENOSPC; - return num_slots; -} -EXPORT_SYMBOL(drm_dp_find_vcpi_slots); - -static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_vcpi *vcpi, int pbn) -{ - int num_slots; - int ret; - - num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div); - - if (num_slots > mgr->avail_slots) - return -ENOSPC; - - vcpi->pbn = pbn; - vcpi->aligned_pbn = num_slots * mgr->pbn_div; - vcpi->num_slots = num_slots; - - ret = drm_dp_mst_assign_payload_id(mgr, vcpi); - if (ret < 0) - return ret; - return 0; -} - -/** - * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel - * @mgr: manager for this port - * @port: port to allocate a virtual channel for. - * @pbn: payload bandwidth number to request - * @slots: returned number of slots for this PBN. - */ -bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn, int *slots) -{ - int ret; - - port = drm_dp_get_validated_port_ref(mgr, port); - if (!port) - return false; - - if (port->vcpi.vcpi > 0) { - DRM_DEBUG_KMS("payload: vcpi %d already allocated for pbn %d - requested pbn %d\n", port->vcpi.vcpi, port->vcpi.pbn, pbn); - if (pbn == port->vcpi.pbn) { - *slots = port->vcpi.num_slots; - drm_dp_put_port(port); - return true; - } - } - - ret = drm_dp_init_vcpi(mgr, &port->vcpi, pbn); - if (ret) { - DRM_DEBUG_KMS("failed to init vcpi %d %d %d\n", DIV_ROUND_UP(pbn, mgr->pbn_div), mgr->avail_slots, ret); - goto out; - } - DRM_DEBUG_KMS("initing vcpi for %d %d\n", pbn, port->vcpi.num_slots); - *slots = port->vcpi.num_slots; - - drm_dp_put_port(port); - return true; -out: - return false; -} -EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi); - -int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) -{ - int slots = 0; - port = drm_dp_get_validated_port_ref(mgr, port); - if (!port) - return slots; - - slots = port->vcpi.num_slots; - drm_dp_put_port(port); - return slots; -} -EXPORT_SYMBOL(drm_dp_mst_get_vcpi_slots); - -/** - * drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI - * @mgr: manager for this port - * @port: unverified pointer to a port. - * - * This just resets the number of slots for the ports VCPI for later programming. - */ -void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) -{ - port = drm_dp_get_validated_port_ref(mgr, port); - if (!port) - return; - port->vcpi.num_slots = 0; - drm_dp_put_port(port); -} -EXPORT_SYMBOL(drm_dp_mst_reset_vcpi_slots); - -/** - * drm_dp_mst_deallocate_vcpi() - deallocate a VCPI - * @mgr: manager for this port - * @port: unverified port to deallocate vcpi for - */ -void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) -{ - port = drm_dp_get_validated_port_ref(mgr, port); - if (!port) - return; - - drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi); - port->vcpi.num_slots = 0; - port->vcpi.pbn = 0; - port->vcpi.aligned_pbn = 0; - port->vcpi.vcpi = 0; - drm_dp_put_port(port); -} -EXPORT_SYMBOL(drm_dp_mst_deallocate_vcpi); - -static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, - int id, struct drm_dp_payload *payload) -{ - u8 payload_alloc[3], status; - int ret; - int retries = 0; - - drm_dp_dpcd_writeb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, - DP_PAYLOAD_TABLE_UPDATED); - - payload_alloc[0] = id; - payload_alloc[1] = payload->start_slot; - payload_alloc[2] = payload->num_slots; - - ret = drm_dp_dpcd_write(mgr->aux, DP_PAYLOAD_ALLOCATE_SET, payload_alloc, 3); - if (ret != 3) { - DRM_DEBUG_KMS("failed to write payload allocation %d\n", ret); - goto fail; - } - -retry: - ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status); - if (ret < 0) { - DRM_DEBUG_KMS("failed to read payload table status %d\n", ret); - goto fail; - } - - if (!(status & DP_PAYLOAD_TABLE_UPDATED)) { - retries++; - if (retries < 20) { - usleep_range(10000, 20000); - goto retry; - } - DRM_DEBUG_KMS("status not set after read payload table status %d\n", status); - ret = -EINVAL; - goto fail; - } - ret = 0; -fail: - return ret; -} - - -/** - * drm_dp_check_act_status() - Check ACT handled status. - * @mgr: manager to use - * - * Check the payload status bits in the DPCD for ACT handled completion. - */ -int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr) -{ - u8 status; - int ret; - int count = 0; - - do { - ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status); - - if (ret < 0) { - DRM_DEBUG_KMS("failed to read payload table status %d\n", ret); - goto fail; - } - - if (status & DP_PAYLOAD_ACT_HANDLED) - break; - count++; - udelay(100); - - } while (count < 30); - - if (!(status & DP_PAYLOAD_ACT_HANDLED)) { - DRM_DEBUG_KMS("failed to get ACT bit %d after %d retries\n", status, count); - ret = -EINVAL; - goto fail; - } - return 0; -fail: - return ret; -} -EXPORT_SYMBOL(drm_dp_check_act_status); - -/** - * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode. - * @clock: dot clock for the mode - * @bpp: bpp for the mode. - * - * This uses the formula in the spec to calculate the PBN value for a mode. - */ -int drm_dp_calc_pbn_mode(int clock, int bpp) -{ - u64 kbps; - s64 peak_kbps; - u32 numerator; - u32 denominator; - - kbps = clock * bpp; - - /* - * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006 - * The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on - * common multiplier to render an integer PBN for all link rate/lane - * counts combinations - * calculate - * peak_kbps *= (1006/1000) - * peak_kbps *= (64/54) - * peak_kbps *= 8 convert to bytes - */ - - numerator = 64 * 1006; - denominator = 54 * 8 * 1000 * 1000; - - kbps *= numerator; - peak_kbps = drm_fixp_from_fraction(kbps, denominator); - - return drm_fixp2int_ceil(peak_kbps); -} -EXPORT_SYMBOL(drm_dp_calc_pbn_mode); - -static int test_calc_pbn_mode(void) -{ - int ret; - ret = drm_dp_calc_pbn_mode(154000, 30); - if (ret != 689) { - DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n", - 154000, 30, 689, ret); - return -EINVAL; - } - ret = drm_dp_calc_pbn_mode(234000, 30); - if (ret != 1047) { - DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n", - 234000, 30, 1047, ret); - return -EINVAL; - } - ret = drm_dp_calc_pbn_mode(297000, 24); - if (ret != 1063) { - DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n", - 297000, 24, 1063, ret); - return -EINVAL; - } - return 0; -} - -/* we want to kick the TX after we've ack the up/down IRQs. */ -static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr) -{ - queue_work(system_long_wq, &mgr->tx_work); -} - -static void drm_dp_mst_dump_mstb(struct seq_file *m, - struct drm_dp_mst_branch *mstb) -{ - struct drm_dp_mst_port *port; - int tabs = mstb->lct; - char prefix[10]; - int i; - - for (i = 0; i < tabs; i++) - prefix[i] = '\t'; - prefix[i] = '\0'; - - seq_printf(m, "%smst: %p, %d\n", prefix, mstb, mstb->num_ports); - list_for_each_entry(port, &mstb->ports, next) { - seq_printf(m, "%sport: %d: ddps: %d ldps: %d, %p, conn: %p\n", prefix, port->port_num, port->ddps, port->ldps, port, port->connector); - if (port->mstb) - drm_dp_mst_dump_mstb(m, port->mstb); - } -} - -static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr, - char *buf) -{ - int ret; - int i; - for (i = 0; i < 4; i++) { - ret = drm_dp_dpcd_read(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS + (i * 16), &buf[i * 16], 16); - if (ret != 16) - break; - } - if (i == 4) - return true; - return false; -} - -/** - * drm_dp_mst_dump_topology(): dump topology to seq file. - * @m: seq_file to dump output to - * @mgr: manager to dump current topology for. - * - * helper to dump MST topology to a seq file for debugfs. - */ -void drm_dp_mst_dump_topology(struct seq_file *m, - struct drm_dp_mst_topology_mgr *mgr) -{ - int i; - struct drm_dp_mst_port *port; - mutex_lock(&mgr->lock); - if (mgr->mst_primary) - drm_dp_mst_dump_mstb(m, mgr->mst_primary); - - /* dump VCPIs */ - mutex_unlock(&mgr->lock); - - mutex_lock(&mgr->payload_lock); - seq_printf(m, "vcpi: %lx %lx\n", mgr->payload_mask, mgr->vcpi_mask); - - for (i = 0; i < mgr->max_payloads; i++) { - if (mgr->proposed_vcpis[i]) { - port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); - seq_printf(m, "vcpi %d: %d %d %d\n", i, port->port_num, port->vcpi.vcpi, port->vcpi.num_slots); - } else - seq_printf(m, "vcpi %d:unsed\n", i); - } - for (i = 0; i < mgr->max_payloads; i++) { - seq_printf(m, "payload %d: %d, %d, %d\n", - i, - mgr->payloads[i].payload_state, - mgr->payloads[i].start_slot, - mgr->payloads[i].num_slots); - - - } - mutex_unlock(&mgr->payload_lock); - - mutex_lock(&mgr->lock); - if (mgr->mst_primary) { - u8 buf[64]; - bool bret; - int ret; - ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE); - seq_printf(m, "dpcd: "); - for (i = 0; i < DP_RECEIVER_CAP_SIZE; i++) - seq_printf(m, "%02x ", buf[i]); - seq_printf(m, "\n"); - ret = drm_dp_dpcd_read(mgr->aux, DP_FAUX_CAP, buf, 2); - seq_printf(m, "faux/mst: "); - for (i = 0; i < 2; i++) - seq_printf(m, "%02x ", buf[i]); - seq_printf(m, "\n"); - ret = drm_dp_dpcd_read(mgr->aux, DP_MSTM_CTRL, buf, 1); - seq_printf(m, "mst ctrl: "); - for (i = 0; i < 1; i++) - seq_printf(m, "%02x ", buf[i]); - seq_printf(m, "\n"); - - /* dump the standard OUI branch header */ - ret = drm_dp_dpcd_read(mgr->aux, DP_BRANCH_OUI, buf, DP_BRANCH_OUI_HEADER_SIZE); - seq_printf(m, "branch oui: "); - for (i = 0; i < 0x3; i++) - seq_printf(m, "%02x", buf[i]); - seq_printf(m, " devid: "); - for (i = 0x3; i < 0x8; i++) - seq_printf(m, "%c", buf[i]); - seq_printf(m, " revision: hw: %x.%x sw: %x.%x", buf[0x9] >> 4, buf[0x9] & 0xf, buf[0xa], buf[0xb]); - seq_printf(m, "\n"); - bret = dump_dp_payload_table(mgr, buf); - if (bret == true) { - seq_printf(m, "payload table: "); - for (i = 0; i < 63; i++) - seq_printf(m, "%02x ", buf[i]); - seq_printf(m, "\n"); - } - - } - - mutex_unlock(&mgr->lock); - -} -EXPORT_SYMBOL(drm_dp_mst_dump_topology); - -static void drm_dp_tx_work(struct work_struct *work) -{ - struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, tx_work); - - mutex_lock(&mgr->qlock); - if (mgr->tx_down_in_progress) - process_single_down_tx_qlock(mgr); - mutex_unlock(&mgr->qlock); -} - -static void drm_dp_free_mst_port(struct kref *kref) -{ - struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref); - kref_put(&port->parent->kref, drm_dp_free_mst_branch_device); - kfree(port); -} - -static void drm_dp_destroy_connector_work(struct work_struct *work) -{ - struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, destroy_connector_work); - struct drm_dp_mst_port *port; - bool send_hotplug = false; - /* - * Not a regular list traverse as we have to drop the destroy - * connector lock before destroying the connector, to avoid AB->BA - * ordering between this lock and the config mutex. - */ - for (;;) { - mutex_lock(&mgr->destroy_connector_lock); - port = list_first_entry_or_null(&mgr->destroy_connector_list, struct drm_dp_mst_port, next); - if (!port) { - mutex_unlock(&mgr->destroy_connector_lock); - break; - } - list_del(&port->next); - mutex_unlock(&mgr->destroy_connector_lock); - - kref_init(&port->kref); - INIT_LIST_HEAD(&port->next); - - mgr->cbs->destroy_connector(mgr, port->connector); - - drm_dp_port_teardown_pdt(port, port->pdt); - port->pdt = DP_PEER_DEVICE_NONE; - - if (!port->input && port->vcpi.vcpi > 0) { - drm_dp_mst_reset_vcpi_slots(mgr, port); - drm_dp_update_payload_part1(mgr); - drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi); - } - - kref_put(&port->kref, drm_dp_free_mst_port); - send_hotplug = true; - } - if (send_hotplug) - (*mgr->cbs->hotplug)(mgr); -} - -/** - * drm_dp_mst_topology_mgr_init - initialise a topology manager - * @mgr: manager struct to initialise - * @dev: device providing this structure - for i2c addition. - * @aux: DP helper aux channel to talk to this device - * @max_dpcd_transaction_bytes: hw specific DPCD transaction limit - * @max_payloads: maximum number of payloads this GPU can source - * @conn_base_id: the connector object ID the MST device is connected to. - * - * Return 0 for success, or negative error code on failure - */ -int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, - struct device *dev, struct drm_dp_aux *aux, - int max_dpcd_transaction_bytes, - int max_payloads, int conn_base_id) -{ - mutex_init(&mgr->lock); - mutex_init(&mgr->qlock); - mutex_init(&mgr->payload_lock); - mutex_init(&mgr->destroy_connector_lock); - INIT_LIST_HEAD(&mgr->tx_msg_downq); - INIT_LIST_HEAD(&mgr->destroy_connector_list); - INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work); - INIT_WORK(&mgr->tx_work, drm_dp_tx_work); - INIT_WORK(&mgr->destroy_connector_work, drm_dp_destroy_connector_work); - init_waitqueue_head(&mgr->tx_waitq); - mgr->dev = dev; - mgr->aux = aux; - mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes; - mgr->max_payloads = max_payloads; - mgr->conn_base_id = conn_base_id; - mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL); - if (!mgr->payloads) - return -ENOMEM; - mgr->proposed_vcpis = kcalloc(max_payloads, sizeof(struct drm_dp_vcpi *), GFP_KERNEL); - if (!mgr->proposed_vcpis) - return -ENOMEM; - set_bit(0, &mgr->payload_mask); - test_calc_pbn_mode(); - return 0; -} -EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init); - -/** - * drm_dp_mst_topology_mgr_destroy() - destroy topology manager. - * @mgr: manager to destroy - */ -void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr) -{ - flush_work(&mgr->work); - flush_work(&mgr->destroy_connector_work); - mutex_lock(&mgr->payload_lock); - kfree(mgr->payloads); - mgr->payloads = NULL; - kfree(mgr->proposed_vcpis); - mgr->proposed_vcpis = NULL; - mutex_unlock(&mgr->payload_lock); - mgr->dev = NULL; - mgr->aux = NULL; -} -EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy); - -/* I2C device */ -static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, - int num) -{ - struct drm_dp_aux *aux = adapter->algo_data; - struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port, aux); - struct drm_dp_mst_branch *mstb; - struct drm_dp_mst_topology_mgr *mgr = port->mgr; - unsigned int i; - bool reading = false; - struct drm_dp_sideband_msg_req_body msg; - struct drm_dp_sideband_msg_tx *txmsg = NULL; - int ret; - - mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent); - if (!mstb) - return -EREMOTEIO; - - /* construct i2c msg */ - /* see if last msg is a read */ - if (msgs[num - 1].flags & I2C_M_RD) - reading = true; - - if (!reading || (num - 1 > DP_REMOTE_I2C_READ_MAX_TRANSACTIONS)) { - DRM_DEBUG_KMS("Unsupported I2C transaction for MST device\n"); - ret = -EIO; - goto out; - } - - memset(&msg, 0, sizeof(msg)); - msg.req_type = DP_REMOTE_I2C_READ; - msg.u.i2c_read.num_transactions = num - 1; - msg.u.i2c_read.port_number = port->port_num; - for (i = 0; i < num - 1; i++) { - msg.u.i2c_read.transactions[i].i2c_dev_id = msgs[i].addr; - msg.u.i2c_read.transactions[i].num_bytes = msgs[i].len; - msg.u.i2c_read.transactions[i].bytes = msgs[i].buf; - msg.u.i2c_read.transactions[i].no_stop_bit = !(msgs[i].flags & I2C_M_STOP); - } - msg.u.i2c_read.read_i2c_device_id = msgs[num - 1].addr; - msg.u.i2c_read.num_bytes_read = msgs[num - 1].len; - - txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); - if (!txmsg) { - ret = -ENOMEM; - goto out; - } - - txmsg->dst = mstb; - drm_dp_encode_sideband_req(&msg, txmsg); - - drm_dp_queue_down_tx(mgr, txmsg); - - ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); - if (ret > 0) { - - if (txmsg->reply.reply_type == 1) { /* got a NAK back */ - ret = -EREMOTEIO; - goto out; - } - if (txmsg->reply.u.remote_i2c_read_ack.num_bytes != msgs[num - 1].len) { - ret = -EIO; - goto out; - } - memcpy(msgs[num - 1].buf, txmsg->reply.u.remote_i2c_read_ack.bytes, msgs[num - 1].len); - ret = num; - } -out: - kfree(txmsg); - drm_dp_put_mst_branch_device(mstb); - return ret; -} - -static u32 drm_dp_mst_i2c_functionality(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | - I2C_FUNC_SMBUS_READ_BLOCK_DATA | - I2C_FUNC_SMBUS_BLOCK_PROC_CALL | - I2C_FUNC_10BIT_ADDR; -} - -static const struct i2c_algorithm drm_dp_mst_i2c_algo = { - .functionality = drm_dp_mst_i2c_functionality, - .master_xfer = drm_dp_mst_i2c_xfer, -}; - -/** - * drm_dp_mst_register_i2c_bus() - register an I2C adapter for I2C-over-AUX - * @aux: DisplayPort AUX channel - * - * Returns 0 on success or a negative error code on failure. - */ -static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux) -{ - aux->ddc.algo = &drm_dp_mst_i2c_algo; - aux->ddc.algo_data = aux; - aux->ddc.retries = 3; - - aux->ddc.class = I2C_CLASS_DDC; - aux->ddc.owner = THIS_MODULE; - aux->ddc.dev.parent = aux->dev; - aux->ddc.dev.of_node = aux->dev->of_node; - - strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), - sizeof(aux->ddc.name)); - - return i2c_add_adapter(&aux->ddc); -} - -/** - * drm_dp_mst_unregister_i2c_bus() - unregister an I2C-over-AUX adapter - * @aux: DisplayPort AUX channel - */ -static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux) -{ - i2c_del_adapter(&aux->ddc); -} diff --git a/src/4.x/drivers/gpu/drm/drm_drv.c b/src/4.x/drivers/gpu/drm/drm_drv.c deleted file mode 100644 index ccc2044af..000000000 --- a/src/4.x/drivers/gpu/drm/drm_drv.c +++ /dev/null @@ -1,931 +0,0 @@ -/* - * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org - * - * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Author Rickard E. (Rik) Faith - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "drm_legacy.h" -#include "drm_internal.h" - -unsigned int drm_debug = 0; /* bitmask of DRM_UT_x */ -EXPORT_SYMBOL(drm_debug); - -MODULE_AUTHOR(CORE_AUTHOR); -MODULE_DESCRIPTION(CORE_DESC); -MODULE_LICENSE("GPL and additional rights"); -MODULE_PARM_DESC(debug, "Enable debug output"); -MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)"); -MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); -MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); - -module_param_named(debug, drm_debug, int, 0600); - -static DEFINE_SPINLOCK(drm_minor_lock); -static struct idr drm_minors_idr; - -static struct dentry *drm_debugfs_root; - -void drm_err(const char *format, ...) -{ - struct va_format vaf; - va_list args; - - va_start(args, format); - - vaf.fmt = format; - vaf.va = &args; - - printk(KERN_ERR "[" DRM_NAME ":%ps] *ERROR* %pV", - __builtin_return_address(0), &vaf); - - va_end(args); -} -EXPORT_SYMBOL(drm_err); - -void drm_ut_debug_printk(const char *function_name, const char *format, ...) -{ - struct va_format vaf; - va_list args; - - va_start(args, format); - vaf.fmt = format; - vaf.va = &args; - - printk(KERN_DEBUG "[" DRM_NAME ":%s] %pV", function_name, &vaf); - - va_end(args); -} -EXPORT_SYMBOL(drm_ut_debug_printk); - -struct drm_master *drm_master_create(struct drm_minor *minor) -{ - struct drm_master *master; - - master = kzalloc(sizeof(*master), GFP_KERNEL); - if (!master) - return NULL; - - kref_init(&master->refcount); - spin_lock_init(&master->lock.spinlock); - init_waitqueue_head(&master->lock.lock_queue); - idr_init(&master->magic_map); - master->minor = minor; - - return master; -} - -struct drm_master *drm_master_get(struct drm_master *master) -{ - kref_get(&master->refcount); - return master; -} -EXPORT_SYMBOL(drm_master_get); - -static void drm_master_destroy(struct kref *kref) -{ - struct drm_master *master = container_of(kref, struct drm_master, refcount); - struct drm_device *dev = master->minor->dev; - struct drm_map_list *r_list, *list_temp; - - mutex_lock(&dev->struct_mutex); - if (dev->driver->master_destroy) - dev->driver->master_destroy(dev, master); - - list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) { - if (r_list->master == master) { - drm_legacy_rmmap_locked(dev, r_list->map); - r_list = NULL; - } - } - mutex_unlock(&dev->struct_mutex); - - idr_destroy(&master->magic_map); - kfree(master->unique); - kfree(master); -} - -void drm_master_put(struct drm_master **master) -{ - kref_put(&(*master)->refcount, drm_master_destroy); - *master = NULL; -} -EXPORT_SYMBOL(drm_master_put); - -int drm_setmaster_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - int ret = 0; - - mutex_lock(&dev->master_mutex); - if (file_priv->is_master) - goto out_unlock; - - if (file_priv->minor->master) { - ret = -EINVAL; - goto out_unlock; - } - - if (!file_priv->master) { - ret = -EINVAL; - goto out_unlock; - } - - if (!file_priv->allowed_master) { - ret = drm_new_set_master(dev, file_priv); - goto out_unlock; - } - - file_priv->minor->master = drm_master_get(file_priv->master); - file_priv->is_master = 1; - if (dev->driver->master_set) { - ret = dev->driver->master_set(dev, file_priv, false); - if (unlikely(ret != 0)) { - file_priv->is_master = 0; - drm_master_put(&file_priv->minor->master); - } - } - -out_unlock: - mutex_unlock(&dev->master_mutex); - return ret; -} - -int drm_dropmaster_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - int ret = -EINVAL; - - mutex_lock(&dev->master_mutex); - if (!file_priv->is_master) - goto out_unlock; - - if (!file_priv->minor->master) - goto out_unlock; - - ret = 0; - if (dev->driver->master_drop) - dev->driver->master_drop(dev, file_priv, false); - drm_master_put(&file_priv->minor->master); - file_priv->is_master = 0; - -out_unlock: - mutex_unlock(&dev->master_mutex); - return ret; -} - -/* - * DRM Minors - * A DRM device can provide several char-dev interfaces on the DRM-Major. Each - * of them is represented by a drm_minor object. Depending on the capabilities - * of the device-driver, different interfaces are registered. - * - * Minors can be accessed via dev->$minor_name. This pointer is either - * NULL or a valid drm_minor pointer and stays valid as long as the device is - * valid. This means, DRM minors have the same life-time as the underlying - * device. However, this doesn't mean that the minor is active. Minors are - * registered and unregistered dynamically according to device-state. - */ - -static struct drm_minor **drm_minor_get_slot(struct drm_device *dev, - unsigned int type) -{ - switch (type) { - case DRM_MINOR_LEGACY: - return &dev->primary; - case DRM_MINOR_RENDER: - return &dev->render; - case DRM_MINOR_CONTROL: - return &dev->control; - default: - return NULL; - } -} - -static int drm_minor_alloc(struct drm_device *dev, unsigned int type) -{ - struct drm_minor *minor; - unsigned long flags; - int r; - - minor = kzalloc(sizeof(*minor), GFP_KERNEL); - if (!minor) - return -ENOMEM; - - minor->type = type; - minor->dev = dev; - - idr_preload(GFP_KERNEL); - spin_lock_irqsave(&drm_minor_lock, flags); - r = idr_alloc(&drm_minors_idr, - NULL, - 64 * type, - 64 * (type + 1), - GFP_NOWAIT); - spin_unlock_irqrestore(&drm_minor_lock, flags); - idr_preload_end(); - - if (r < 0) - goto err_free; - - minor->index = r; - - minor->kdev = drm_sysfs_minor_alloc(minor); - if (IS_ERR(minor->kdev)) { - r = PTR_ERR(minor->kdev); - goto err_index; - } - - *drm_minor_get_slot(dev, type) = minor; - return 0; - -err_index: - spin_lock_irqsave(&drm_minor_lock, flags); - idr_remove(&drm_minors_idr, minor->index); - spin_unlock_irqrestore(&drm_minor_lock, flags); -err_free: - kfree(minor); - return r; -} - -static void drm_minor_free(struct drm_device *dev, unsigned int type) -{ - struct drm_minor **slot, *minor; - unsigned long flags; - - slot = drm_minor_get_slot(dev, type); - minor = *slot; - if (!minor) - return; - - put_device(minor->kdev); - - spin_lock_irqsave(&drm_minor_lock, flags); - idr_remove(&drm_minors_idr, minor->index); - spin_unlock_irqrestore(&drm_minor_lock, flags); - - kfree(minor); - *slot = NULL; -} - -static int drm_minor_register(struct drm_device *dev, unsigned int type) -{ - struct drm_minor *minor; - unsigned long flags; - int ret; - - DRM_DEBUG("\n"); - - minor = *drm_minor_get_slot(dev, type); - if (!minor) - return 0; - - ret = drm_debugfs_init(minor, minor->index, drm_debugfs_root); - if (ret) { - DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n"); - goto err_debugfs; - } - - ret = device_add(minor->kdev); - if (ret) - goto err_debugfs; - - /* replace NULL with @minor so lookups will succeed from now on */ - spin_lock_irqsave(&drm_minor_lock, flags); - idr_replace(&drm_minors_idr, minor, minor->index); - spin_unlock_irqrestore(&drm_minor_lock, flags); - - DRM_DEBUG("new minor registered %d\n", minor->index); - return 0; - -err_debugfs: - drm_debugfs_cleanup(minor); - return ret; -} - -static void drm_minor_unregister(struct drm_device *dev, unsigned int type) -{ - struct drm_minor *minor; - unsigned long flags; - - minor = *drm_minor_get_slot(dev, type); - if (!minor || !device_is_registered(minor->kdev)) - return; - - /* replace @minor with NULL so lookups will fail from now on */ - spin_lock_irqsave(&drm_minor_lock, flags); - idr_replace(&drm_minors_idr, NULL, minor->index); - spin_unlock_irqrestore(&drm_minor_lock, flags); - - device_del(minor->kdev); - dev_set_drvdata(minor->kdev, NULL); /* safety belt */ - drm_debugfs_cleanup(minor); -} - -/** - * drm_minor_acquire - Acquire a DRM minor - * @minor_id: Minor ID of the DRM-minor - * - * Looks up the given minor-ID and returns the respective DRM-minor object. The - * refence-count of the underlying device is increased so you must release this - * object with drm_minor_release(). - * - * As long as you hold this minor, it is guaranteed that the object and the - * minor->dev pointer will stay valid! However, the device may get unplugged and - * unregistered while you hold the minor. - * - * Returns: - * Pointer to minor-object with increased device-refcount, or PTR_ERR on - * failure. - */ -struct drm_minor *drm_minor_acquire(unsigned int minor_id) -{ - struct drm_minor *minor; - unsigned long flags; - - spin_lock_irqsave(&drm_minor_lock, flags); - minor = idr_find(&drm_minors_idr, minor_id); - if (minor) - drm_dev_ref(minor->dev); - spin_unlock_irqrestore(&drm_minor_lock, flags); - - if (!minor) { - return ERR_PTR(-ENODEV); - } else if (drm_device_is_unplugged(minor->dev)) { - drm_dev_unref(minor->dev); - return ERR_PTR(-ENODEV); - } - - return minor; -} - -/** - * drm_minor_release - Release DRM minor - * @minor: Pointer to DRM minor object - * - * Release a minor that was previously acquired via drm_minor_acquire(). - */ -void drm_minor_release(struct drm_minor *minor) -{ - drm_dev_unref(minor->dev); -} - -/** - * DOC: driver instance overview - * - * A device instance for a drm driver is represented by struct &drm_device. This - * is allocated with drm_dev_alloc(), usually from bus-specific ->probe() - * callbacks implemented by the driver. The driver then needs to initialize all - * the various subsystems for the drm device like memory management, vblank - * handling, modesetting support and intial output configuration plus obviously - * initialize all the corresponding hardware bits. An important part of this is - * also calling drm_dev_set_unique() to set the userspace-visible unique name of - * this device instance. Finally when everything is up and running and ready for - * userspace the device instance can be published using drm_dev_register(). - * - * There is also deprecated support for initalizing device instances using - * bus-specific helpers and the ->load() callback. But due to - * backwards-compatibility needs the device instance have to be published too - * early, which requires unpretty global locking to make safe and is therefore - * only support for existing drivers not yet converted to the new scheme. - * - * When cleaning up a device instance everything needs to be done in reverse: - * First unpublish the device instance with drm_dev_unregister(). Then clean up - * any other resources allocated at device initialization and drop the driver's - * reference to &drm_device using drm_dev_unref(). - * - * Note that the lifetime rules for &drm_device instance has still a lot of - * historical baggage. Hence use the reference counting provided by - * drm_dev_ref() and drm_dev_unref() only carefully. - * - * Also note that embedding of &drm_device is currently not (yet) supported (but - * it would be easy to add). Drivers can store driver-private data in the - * dev_priv field of &drm_device. - */ - -/** - * drm_put_dev - Unregister and release a DRM device - * @dev: DRM device - * - * Called at module unload time or when a PCI device is unplugged. - * - * Cleans up all DRM device, calling drm_lastclose(). - * - * Note: Use of this function is deprecated. It will eventually go away - * completely. Please use drm_dev_unregister() and drm_dev_unref() explicitly - * instead to make sure that the device isn't userspace accessible any more - * while teardown is in progress, ensuring that userspace can't access an - * inconsistent state. - */ -void drm_put_dev(struct drm_device *dev) -{ - DRM_DEBUG("\n"); - - if (!dev) { - DRM_ERROR("cleanup called no dev\n"); - return; - } - - drm_dev_unregister(dev); - drm_dev_unref(dev); -} -EXPORT_SYMBOL(drm_put_dev); - -void drm_unplug_dev(struct drm_device *dev) -{ - /* for a USB device */ - drm_minor_unregister(dev, DRM_MINOR_LEGACY); - drm_minor_unregister(dev, DRM_MINOR_RENDER); - drm_minor_unregister(dev, DRM_MINOR_CONTROL); - - mutex_lock(&drm_global_mutex); - - drm_device_set_unplugged(dev); - - if (dev->open_count == 0) { - drm_put_dev(dev); - } - mutex_unlock(&drm_global_mutex); -} -EXPORT_SYMBOL(drm_unplug_dev); - -/* - * DRM internal mount - * We want to be able to allocate our own "struct address_space" to control - * memory-mappings in VRAM (or stolen RAM, ...). However, core MM does not allow - * stand-alone address_space objects, so we need an underlying inode. As there - * is no way to allocate an independent inode easily, we need a fake internal - * VFS mount-point. - * - * The drm_fs_inode_new() function allocates a new inode, drm_fs_inode_free() - * frees it again. You are allowed to use iget() and iput() to get references to - * the inode. But each drm_fs_inode_new() call must be paired with exactly one - * drm_fs_inode_free() call (which does not have to be the last iput()). - * We use drm_fs_inode_*() to manage our internal VFS mount-point and share it - * between multiple inode-users. You could, technically, call - * iget() + drm_fs_inode_free() directly after alloc and sometime later do an - * iput(), but this way you'd end up with a new vfsmount for each inode. - */ - -static int drm_fs_cnt; -static struct vfsmount *drm_fs_mnt; - -static const struct dentry_operations drm_fs_dops = { - .d_dname = simple_dname, -}; - -static const struct super_operations drm_fs_sops = { - .statfs = simple_statfs, -}; - -static struct dentry *drm_fs_mount(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data) -{ - return mount_pseudo(fs_type, - "drm:", - &drm_fs_sops, - &drm_fs_dops, - 0x010203ff); -} - -static struct file_system_type drm_fs_type = { - .name = "drm", - .owner = THIS_MODULE, - .mount = drm_fs_mount, - .kill_sb = kill_anon_super, -}; - -static struct inode *drm_fs_inode_new(void) -{ - struct inode *inode; - int r; - - r = simple_pin_fs(&drm_fs_type, &drm_fs_mnt, &drm_fs_cnt); - if (r < 0) { - DRM_ERROR("Cannot mount pseudo fs: %d\n", r); - return ERR_PTR(r); - } - - inode = alloc_anon_inode(drm_fs_mnt->mnt_sb); - if (IS_ERR(inode)) - simple_release_fs(&drm_fs_mnt, &drm_fs_cnt); - - return inode; -} - -static void drm_fs_inode_free(struct inode *inode) -{ - if (inode) { - iput(inode); - simple_release_fs(&drm_fs_mnt, &drm_fs_cnt); - } -} - -/** - * drm_dev_alloc - Allocate new DRM device - * @driver: DRM driver to allocate device for - * @parent: Parent device object - * - * Allocate and initialize a new DRM device. No device registration is done. - * Call drm_dev_register() to advertice the device to user space and register it - * with other core subsystems. This should be done last in the device - * initialization sequence to make sure userspace can't access an inconsistent - * state. - * - * The initial ref-count of the object is 1. Use drm_dev_ref() and - * drm_dev_unref() to take and drop further ref-counts. - * - * Note that for purely virtual devices @parent can be NULL. - * - * RETURNS: - * Pointer to new DRM device, or NULL if out of memory. - */ -struct drm_device *drm_dev_alloc(struct drm_driver *driver, - struct device *parent) -{ - struct drm_device *dev; - int ret; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return NULL; - - kref_init(&dev->ref); - dev->dev = parent; - dev->driver = driver; - - INIT_LIST_HEAD(&dev->filelist); - INIT_LIST_HEAD(&dev->ctxlist); - INIT_LIST_HEAD(&dev->vmalist); - INIT_LIST_HEAD(&dev->maplist); - INIT_LIST_HEAD(&dev->vblank_event_list); - - spin_lock_init(&dev->buf_lock); - spin_lock_init(&dev->event_lock); - mutex_init(&dev->struct_mutex); - mutex_init(&dev->ctxlist_mutex); - mutex_init(&dev->master_mutex); - - dev->anon_inode = drm_fs_inode_new(); - if (IS_ERR(dev->anon_inode)) { - ret = PTR_ERR(dev->anon_inode); - DRM_ERROR("Cannot allocate anonymous inode: %d\n", ret); - goto err_free; - } - - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - ret = drm_minor_alloc(dev, DRM_MINOR_CONTROL); - if (ret) - goto err_minors; - - WARN_ON(driver->suspend || driver->resume); - } - - if (drm_core_check_feature(dev, DRIVER_RENDER)) { - ret = drm_minor_alloc(dev, DRM_MINOR_RENDER); - if (ret) - goto err_minors; - } - - ret = drm_minor_alloc(dev, DRM_MINOR_LEGACY); - if (ret) - goto err_minors; - - if (drm_ht_create(&dev->map_hash, 12)) - goto err_minors; - - drm_legacy_ctxbitmap_init(dev); - - if (drm_core_check_feature(dev, DRIVER_GEM)) { - ret = drm_gem_init(dev); - if (ret) { - DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n"); - goto err_ctxbitmap; - } - } - - return dev; - -err_ctxbitmap: - drm_legacy_ctxbitmap_cleanup(dev); - drm_ht_remove(&dev->map_hash); -err_minors: - drm_minor_free(dev, DRM_MINOR_LEGACY); - drm_minor_free(dev, DRM_MINOR_RENDER); - drm_minor_free(dev, DRM_MINOR_CONTROL); - drm_fs_inode_free(dev->anon_inode); -err_free: - mutex_destroy(&dev->master_mutex); - kfree(dev); - return NULL; -} -EXPORT_SYMBOL(drm_dev_alloc); - -static void drm_dev_release(struct kref *ref) -{ - struct drm_device *dev = container_of(ref, struct drm_device, ref); - - if (drm_core_check_feature(dev, DRIVER_GEM)) - drm_gem_destroy(dev); - - drm_legacy_ctxbitmap_cleanup(dev); - drm_ht_remove(&dev->map_hash); - drm_fs_inode_free(dev->anon_inode); - - drm_minor_free(dev, DRM_MINOR_LEGACY); - drm_minor_free(dev, DRM_MINOR_RENDER); - drm_minor_free(dev, DRM_MINOR_CONTROL); - - mutex_destroy(&dev->master_mutex); - kfree(dev->unique); - kfree(dev); -} - -/** - * drm_dev_ref - Take reference of a DRM device - * @dev: device to take reference of or NULL - * - * This increases the ref-count of @dev by one. You *must* already own a - * reference when calling this. Use drm_dev_unref() to drop this reference - * again. - * - * This function never fails. However, this function does not provide *any* - * guarantee whether the device is alive or running. It only provides a - * reference to the object and the memory associated with it. - */ -void drm_dev_ref(struct drm_device *dev) -{ - if (dev) - kref_get(&dev->ref); -} -EXPORT_SYMBOL(drm_dev_ref); - -/** - * drm_dev_unref - Drop reference of a DRM device - * @dev: device to drop reference of or NULL - * - * This decreases the ref-count of @dev by one. The device is destroyed if the - * ref-count drops to zero. - */ -void drm_dev_unref(struct drm_device *dev) -{ - if (dev) - kref_put(&dev->ref, drm_dev_release); -} -EXPORT_SYMBOL(drm_dev_unref); - -/** - * drm_dev_register - Register DRM device - * @dev: Device to register - * @flags: Flags passed to the driver's .load() function - * - * Register the DRM device @dev with the system, advertise device to user-space - * and start normal device operation. @dev must be allocated via drm_dev_alloc() - * previously. - * - * Never call this twice on any device! - * - * NOTE: To ensure backward compatibility with existing drivers method this - * function calls the ->load() method after registering the device nodes, - * creating race conditions. Usage of the ->load() methods is therefore - * deprecated, drivers must perform all initialization before calling - * drm_dev_register(). - * - * RETURNS: - * 0 on success, negative error code on failure. - */ -int drm_dev_register(struct drm_device *dev, unsigned long flags) -{ - int ret; - - mutex_lock(&drm_global_mutex); - - ret = drm_minor_register(dev, DRM_MINOR_CONTROL); - if (ret) - goto err_minors; - - ret = drm_minor_register(dev, DRM_MINOR_RENDER); - if (ret) - goto err_minors; - - ret = drm_minor_register(dev, DRM_MINOR_LEGACY); - if (ret) - goto err_minors; - - if (dev->driver->load) { - ret = dev->driver->load(dev, flags); - if (ret) - goto err_minors; - } - - ret = 0; - goto out_unlock; - -err_minors: - drm_minor_unregister(dev, DRM_MINOR_LEGACY); - drm_minor_unregister(dev, DRM_MINOR_RENDER); - drm_minor_unregister(dev, DRM_MINOR_CONTROL); -out_unlock: - mutex_unlock(&drm_global_mutex); - return ret; -} -EXPORT_SYMBOL(drm_dev_register); - -/** - * drm_dev_unregister - Unregister DRM device - * @dev: Device to unregister - * - * Unregister the DRM device from the system. This does the reverse of - * drm_dev_register() but does not deallocate the device. The caller must call - * drm_dev_unref() to drop their final reference. - * - * This should be called first in the device teardown code to make sure - * userspace can't access the device instance any more. - */ -void drm_dev_unregister(struct drm_device *dev) -{ - struct drm_map_list *r_list, *list_temp; - - drm_lastclose(dev); - - if (dev->driver->unload) - dev->driver->unload(dev); - - if (dev->agp) - drm_pci_agp_destroy(dev); - - drm_vblank_cleanup(dev); - - list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) - drm_legacy_rmmap(dev, r_list->map); - - drm_minor_unregister(dev, DRM_MINOR_LEGACY); - drm_minor_unregister(dev, DRM_MINOR_RENDER); - drm_minor_unregister(dev, DRM_MINOR_CONTROL); -} -EXPORT_SYMBOL(drm_dev_unregister); - -/** - * drm_dev_set_unique - Set the unique name of a DRM device - * @dev: device of which to set the unique name - * @fmt: format string for unique name - * - * Sets the unique name of a DRM device using the specified format string and - * a variable list of arguments. Drivers can use this at driver probe time if - * the unique name of the devices they drive is static. - * - * Return: 0 on success or a negative error code on failure. - */ -int drm_dev_set_unique(struct drm_device *dev, const char *fmt, ...) -{ - va_list ap; - - kfree(dev->unique); - - va_start(ap, fmt); - dev->unique = kvasprintf(GFP_KERNEL, fmt, ap); - va_end(ap); - - return dev->unique ? 0 : -ENOMEM; -} -EXPORT_SYMBOL(drm_dev_set_unique); - -/* - * DRM Core - * The DRM core module initializes all global DRM objects and makes them - * available to drivers. Once setup, drivers can probe their respective - * devices. - * Currently, core management includes: - * - The "DRM-Global" key/value database - * - Global ID management for connectors - * - DRM major number allocation - * - DRM minor management - * - DRM sysfs class - * - DRM debugfs root - * - * Furthermore, the DRM core provides dynamic char-dev lookups. For each - * interface registered on a DRM device, you can request minor numbers from DRM - * core. DRM core takes care of major-number management and char-dev - * registration. A stub ->open() callback forwards any open() requests to the - * registered minor. - */ - -static int drm_stub_open(struct inode *inode, struct file *filp) -{ - const struct file_operations *new_fops; - struct drm_minor *minor; - int err; - - DRM_DEBUG("\n"); - - mutex_lock(&drm_global_mutex); - minor = drm_minor_acquire(iminor(inode)); - if (IS_ERR(minor)) { - err = PTR_ERR(minor); - goto out_unlock; - } - - new_fops = fops_get(minor->dev->driver->fops); - if (!new_fops) { - err = -ENODEV; - goto out_release; - } - - replace_fops(filp, new_fops); - if (filp->f_op->open) - err = filp->f_op->open(inode, filp); - else - err = 0; - -out_release: - drm_minor_release(minor); -out_unlock: - mutex_unlock(&drm_global_mutex); - return err; -} - -static const struct file_operations drm_stub_fops = { - .owner = THIS_MODULE, - .open = drm_stub_open, - .llseek = noop_llseek, -}; - -static int __init drm_core_init(void) -{ - int ret = -ENOMEM; - - drm_global_init(); - drm_connector_ida_init(); - idr_init(&drm_minors_idr); - - if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops)) - goto err_p1; - - ret = drm_sysfs_init(); - if (ret < 0) { - printk(KERN_ERR "DRM: Error creating drm class.\n"); - goto err_p2; - } - - drm_debugfs_root = debugfs_create_dir("dri", NULL); - if (!drm_debugfs_root) { - DRM_ERROR("Cannot create /sys/kernel/debug/dri\n"); - ret = -1; - goto err_p3; - } - - DRM_INFO("Initialized %s %d.%d.%d %s\n", - CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); - return 0; -err_p3: - drm_sysfs_destroy(); -err_p2: - unregister_chrdev(DRM_MAJOR, "drm"); - - idr_destroy(&drm_minors_idr); -err_p1: - return ret; -} - -static void __exit drm_core_exit(void) -{ - debugfs_remove(drm_debugfs_root); - drm_sysfs_destroy(); - - unregister_chrdev(DRM_MAJOR, "drm"); - - drm_connector_ida_destroy(); - idr_destroy(&drm_minors_idr); -} - -module_init(drm_core_init); -module_exit(drm_core_exit); diff --git a/src/4.x/drivers/gpu/drm/drm_edid.c b/src/4.x/drivers/gpu/drm/drm_edid.c deleted file mode 100644 index 724f7cf52..000000000 --- a/src/4.x/drivers/gpu/drm/drm_edid.c +++ /dev/null @@ -1,4166 +0,0 @@ -/* - * Copyright (c) 2006 Luc Verhaegen (quirks list) - * Copyright (c) 2007-2008 Intel Corporation - * Jesse Barnes - * Copyright 2010 Red Hat, Inc. - * - * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from - * FB layer. - * Copyright (C) 2006 Dennis Munsie - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#define version_greater(edid, maj, min) \ - (((edid)->version > (maj)) || \ - ((edid)->version == (maj) && (edid)->revision > (min))) - -#define EDID_EST_TIMINGS 16 -#define EDID_STD_TIMINGS 8 -#define EDID_DETAILED_TIMINGS 4 - -/* - * EDID blocks out in the wild have a variety of bugs, try to collect - * them here (note that userspace may work around broken monitors first, - * but fixes should make their way here so that the kernel "just works" - * on as many displays as possible). - */ - -/* First detailed mode wrong, use largest 60Hz mode */ -#define EDID_QUIRK_PREFER_LARGE_60 (1 << 0) -/* Reported 135MHz pixel clock is too high, needs adjustment */ -#define EDID_QUIRK_135_CLOCK_TOO_HIGH (1 << 1) -/* Prefer the largest mode at 75 Hz */ -#define EDID_QUIRK_PREFER_LARGE_75 (1 << 2) -/* Detail timing is in cm not mm */ -#define EDID_QUIRK_DETAILED_IN_CM (1 << 3) -/* Detailed timing descriptors have bogus size values, so just take the - * maximum size and use that. - */ -#define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE (1 << 4) -/* Monitor forgot to set the first detailed is preferred bit. */ -#define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5) -/* use +hsync +vsync for detailed mode */ -#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) -/* Force reduced-blanking timings for detailed modes */ -#define EDID_QUIRK_FORCE_REDUCED_BLANKING (1 << 7) -/* Force 8bpc */ -#define EDID_QUIRK_FORCE_8BPC (1 << 8) -/* Force 12bpc */ -#define EDID_QUIRK_FORCE_12BPC (1 << 9) -/* Force 6bpc */ -#define EDID_QUIRK_FORCE_6BPC (1 << 10) -/* Force 10bpc */ -#define EDID_QUIRK_FORCE_10BPC (1 << 11) - -struct detailed_mode_closure { - struct drm_connector *connector; - struct edid *edid; - bool preferred; - u32 quirks; - int modes; -}; - -#define LEVEL_DMT 0 -#define LEVEL_GTF 1 -#define LEVEL_GTF2 2 -#define LEVEL_CVT 3 - -static struct edid_quirk { - char vendor[4]; - int product_id; - u32 quirks; -} edid_quirk_list[] = { - /* Acer AL1706 */ - { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, - /* Acer F51 */ - { "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 }, - /* Unknown Acer */ - { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, - - /* AEO model 0 reports 8 bpc, but is a 6 bpc panel */ - { "AEO", 0, EDID_QUIRK_FORCE_6BPC }, - - /* CPT panel of Asus UX303LA reports 8 bpc, but is a 6 bpc panel */ - { "CPT", 0x17df, EDID_QUIRK_FORCE_6BPC }, - - /* Belinea 10 15 55 */ - { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 }, - { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 }, - - /* Envision Peripherals, Inc. EN-7100e */ - { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH }, - /* Envision EN2028 */ - { "EPI", 8232, EDID_QUIRK_PREFER_LARGE_60 }, - - /* Funai Electronics PM36B */ - { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | - EDID_QUIRK_DETAILED_IN_CM }, - - /* LGD panel of HP zBook 17 G2, eDP 10 bpc, but reports unknown bpc */ - { "LGD", 764, EDID_QUIRK_FORCE_10BPC }, - - /* LG Philips LCD LP154W01-A5 */ - { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, - { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, - - /* Philips 107p5 CRT */ - { "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, - - /* Proview AY765C */ - { "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, - - /* Samsung SyncMaster 205BW. Note: irony */ - { "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP }, - /* Samsung SyncMaster 22[5-6]BW */ - { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 }, - { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, - - /* Sony PVM-2541A does up to 12 bpc, but only reports max 8 bpc */ - { "SNY", 0x2541, EDID_QUIRK_FORCE_12BPC }, - - /* ViewSonic VA2026w */ - { "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING }, - - /* Medion MD 30217 PG */ - { "MED", 0x7b8, EDID_QUIRK_PREFER_LARGE_75 }, - - /* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */ - { "SEC", 0xd033, EDID_QUIRK_FORCE_8BPC }, - - /* Rotel RSX-1058 forwards sink's EDID but only does HDMI 1.1*/ - { "ETR", 13896, EDID_QUIRK_FORCE_8BPC }, -}; - -/* - * Autogenerated from the DMT spec. - * This table is copied from xfree86/modes/xf86EdidModes.c. - */ -static const struct drm_display_mode drm_dmt_modes[] = { - /* 0x01 - 640x350@85Hz */ - { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, - 736, 832, 0, 350, 382, 385, 445, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x02 - 640x400@85Hz */ - { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, - 736, 832, 0, 400, 401, 404, 445, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x03 - 720x400@85Hz */ - { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756, - 828, 936, 0, 400, 401, 404, 446, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x04 - 640x480@60Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, - 752, 800, 0, 480, 490, 492, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x05 - 640x480@72Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, - 704, 832, 0, 480, 489, 492, 520, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x06 - 640x480@75Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, - 720, 840, 0, 480, 481, 484, 500, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x07 - 640x480@85Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696, - 752, 832, 0, 480, 481, 484, 509, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x08 - 800x600@56Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, - 896, 1024, 0, 600, 601, 603, 625, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x09 - 800x600@60Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, - 968, 1056, 0, 600, 601, 605, 628, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x0a - 800x600@72Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, - 976, 1040, 0, 600, 637, 643, 666, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x0b - 800x600@75Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, - 896, 1056, 0, 600, 601, 604, 625, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x0c - 800x600@85Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832, - 896, 1048, 0, 600, 601, 604, 631, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x0d - 800x600@120Hz RB */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 73250, 800, 848, - 880, 960, 0, 600, 603, 607, 636, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x0e - 848x480@60Hz */ - { DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864, - 976, 1088, 0, 480, 486, 494, 517, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x0f - 1024x768@43Hz, interlace */ - { DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032, - 1208, 1264, 0, 768, 768, 772, 817, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | - DRM_MODE_FLAG_INTERLACE) }, - /* 0x10 - 1024x768@60Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, - 1184, 1344, 0, 768, 771, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x11 - 1024x768@70Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, - 1184, 1328, 0, 768, 771, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x12 - 1024x768@75Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040, - 1136, 1312, 0, 768, 769, 772, 800, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x13 - 1024x768@85Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072, - 1168, 1376, 0, 768, 769, 772, 808, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x14 - 1024x768@120Hz RB */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 115500, 1024, 1072, - 1104, 1184, 0, 768, 771, 775, 813, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x15 - 1152x864@75Hz */ - { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, - 1344, 1600, 0, 864, 865, 868, 900, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x55 - 1280x720@60Hz */ - { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, - 1430, 1650, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x16 - 1280x768@60Hz RB */ - { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 68250, 1280, 1328, - 1360, 1440, 0, 768, 771, 778, 790, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x17 - 1280x768@60Hz */ - { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, - 1472, 1664, 0, 768, 771, 778, 798, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x18 - 1280x768@75Hz */ - { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360, - 1488, 1696, 0, 768, 771, 778, 805, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x19 - 1280x768@85Hz */ - { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360, - 1496, 1712, 0, 768, 771, 778, 809, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x1a - 1280x768@120Hz RB */ - { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 140250, 1280, 1328, - 1360, 1440, 0, 768, 771, 778, 813, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x1b - 1280x800@60Hz RB */ - { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 71000, 1280, 1328, - 1360, 1440, 0, 800, 803, 809, 823, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x1c - 1280x800@60Hz */ - { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, - 1480, 1680, 0, 800, 803, 809, 831, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x1d - 1280x800@75Hz */ - { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360, - 1488, 1696, 0, 800, 803, 809, 838, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x1e - 1280x800@85Hz */ - { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360, - 1496, 1712, 0, 800, 803, 809, 843, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x1f - 1280x800@120Hz RB */ - { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 146250, 1280, 1328, - 1360, 1440, 0, 800, 803, 809, 847, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x20 - 1280x960@60Hz */ - { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, - 1488, 1800, 0, 960, 961, 964, 1000, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x21 - 1280x960@85Hz */ - { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344, - 1504, 1728, 0, 960, 961, 964, 1011, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x22 - 1280x960@120Hz RB */ - { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 175500, 1280, 1328, - 1360, 1440, 0, 960, 963, 967, 1017, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x23 - 1280x1024@60Hz */ - { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, - 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x24 - 1280x1024@75Hz */ - { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, - 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x25 - 1280x1024@85Hz */ - { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344, - 1504, 1728, 0, 1024, 1025, 1028, 1072, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x26 - 1280x1024@120Hz RB */ - { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 187250, 1280, 1328, - 1360, 1440, 0, 1024, 1027, 1034, 1084, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x27 - 1360x768@60Hz */ - { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, - 1536, 1792, 0, 768, 771, 777, 795, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x28 - 1360x768@120Hz RB */ - { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 148250, 1360, 1408, - 1440, 1520, 0, 768, 771, 776, 813, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x51 - 1366x768@60Hz */ - { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 85500, 1366, 1436, - 1579, 1792, 0, 768, 771, 774, 798, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x56 - 1366x768@60Hz */ - { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 72000, 1366, 1380, - 1436, 1500, 0, 768, 769, 772, 800, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x29 - 1400x1050@60Hz RB */ - { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 101000, 1400, 1448, - 1480, 1560, 0, 1050, 1053, 1057, 1080, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x2a - 1400x1050@60Hz */ - { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, - 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x2b - 1400x1050@75Hz */ - { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504, - 1648, 1896, 0, 1050, 1053, 1057, 1099, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x2c - 1400x1050@85Hz */ - { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504, - 1656, 1912, 0, 1050, 1053, 1057, 1105, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x2d - 1400x1050@120Hz RB */ - { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 208000, 1400, 1448, - 1480, 1560, 0, 1050, 1053, 1057, 1112, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x2e - 1440x900@60Hz RB */ - { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 88750, 1440, 1488, - 1520, 1600, 0, 900, 903, 909, 926, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x2f - 1440x900@60Hz */ - { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, - 1672, 1904, 0, 900, 903, 909, 934, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x30 - 1440x900@75Hz */ - { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536, - 1688, 1936, 0, 900, 903, 909, 942, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x31 - 1440x900@85Hz */ - { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544, - 1696, 1952, 0, 900, 903, 909, 948, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x32 - 1440x900@120Hz RB */ - { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 182750, 1440, 1488, - 1520, 1600, 0, 900, 903, 909, 953, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x53 - 1600x900@60Hz */ - { DRM_MODE("1600x900", DRM_MODE_TYPE_DRIVER, 108000, 1600, 1624, - 1704, 1800, 0, 900, 901, 904, 1000, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x33 - 1600x1200@60Hz */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, - 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x34 - 1600x1200@65Hz */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664, - 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x35 - 1600x1200@70Hz */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664, - 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x36 - 1600x1200@75Hz */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664, - 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x37 - 1600x1200@85Hz */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664, - 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x38 - 1600x1200@120Hz RB */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 268250, 1600, 1648, - 1680, 1760, 0, 1200, 1203, 1207, 1271, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x39 - 1680x1050@60Hz RB */ - { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 119000, 1680, 1728, - 1760, 1840, 0, 1050, 1053, 1059, 1080, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x3a - 1680x1050@60Hz */ - { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, - 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x3b - 1680x1050@75Hz */ - { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800, - 1976, 2272, 0, 1050, 1053, 1059, 1099, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x3c - 1680x1050@85Hz */ - { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808, - 1984, 2288, 0, 1050, 1053, 1059, 1105, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x3d - 1680x1050@120Hz RB */ - { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 245500, 1680, 1728, - 1760, 1840, 0, 1050, 1053, 1059, 1112, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x3e - 1792x1344@60Hz */ - { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, - 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x3f - 1792x1344@75Hz */ - { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888, - 2104, 2456, 0, 1344, 1345, 1348, 1417, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x40 - 1792x1344@120Hz RB */ - { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 333250, 1792, 1840, - 1872, 1952, 0, 1344, 1347, 1351, 1423, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x41 - 1856x1392@60Hz */ - { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, - 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x42 - 1856x1392@75Hz */ - { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984, - 2208, 2560, 0, 1392, 1393, 1396, 1500, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x43 - 1856x1392@120Hz RB */ - { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 356500, 1856, 1904, - 1936, 2016, 0, 1392, 1395, 1399, 1474, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x52 - 1920x1080@60Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, - 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x44 - 1920x1200@60Hz RB */ - { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 154000, 1920, 1968, - 2000, 2080, 0, 1200, 1203, 1209, 1235, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x45 - 1920x1200@60Hz */ - { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, - 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x46 - 1920x1200@75Hz */ - { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056, - 2264, 2608, 0, 1200, 1203, 1209, 1255, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x47 - 1920x1200@85Hz */ - { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064, - 2272, 2624, 0, 1200, 1203, 1209, 1262, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x48 - 1920x1200@120Hz RB */ - { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 317000, 1920, 1968, - 2000, 2080, 0, 1200, 1203, 1209, 1271, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x49 - 1920x1440@60Hz */ - { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, - 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x4a - 1920x1440@75Hz */ - { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064, - 2288, 2640, 0, 1440, 1441, 1444, 1500, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x4b - 1920x1440@120Hz RB */ - { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 380500, 1920, 1968, - 2000, 2080, 0, 1440, 1443, 1447, 1525, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x54 - 2048x1152@60Hz */ - { DRM_MODE("2048x1152", DRM_MODE_TYPE_DRIVER, 162000, 2048, 2074, - 2154, 2250, 0, 1152, 1153, 1156, 1200, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x4c - 2560x1600@60Hz RB */ - { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 268500, 2560, 2608, - 2640, 2720, 0, 1600, 1603, 1609, 1646, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x4d - 2560x1600@60Hz */ - { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, - 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x4e - 2560x1600@75Hz */ - { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768, - 3048, 3536, 0, 1600, 1603, 1609, 1672, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x4f - 2560x1600@85Hz */ - { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768, - 3048, 3536, 0, 1600, 1603, 1609, 1682, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 0x50 - 2560x1600@120Hz RB */ - { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 552750, 2560, 2608, - 2640, 2720, 0, 1600, 1603, 1609, 1694, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x57 - 4096x2160@60Hz RB */ - { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 556744, 4096, 4104, - 4136, 4176, 0, 2160, 2208, 2216, 2222, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 0x58 - 4096x2160@59.94Hz RB */ - { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 556188, 4096, 4104, - 4136, 4176, 0, 2160, 2208, 2216, 2222, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, -}; - -/* - * These more or less come from the DMT spec. The 720x400 modes are - * inferred from historical 80x25 practice. The 640x480@67 and 832x624@75 - * modes are old-school Mac modes. The EDID spec says the 1152x864@75 mode - * should be 1152x870, again for the Mac, but instead we use the x864 DMT - * mode. - * - * The DMT modes have been fact-checked; the rest are mild guesses. - */ -static const struct drm_display_mode edid_est_modes[] = { - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, - 968, 1056, 0, 600, 601, 605, 628, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, - 896, 1024, 0, 600, 601, 603, 625, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, - 720, 840, 0, 480, 481, 484, 500, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, - 704, 832, 0, 480, 489, 491, 520, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, - 768, 864, 0, 480, 483, 486, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, - 752, 800, 0, 480, 490, 492, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ - { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, - 846, 900, 0, 400, 421, 423, 449, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */ - { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, - 846, 900, 0, 400, 412, 414, 449, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */ - { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, - 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, - 1136, 1312, 0, 768, 769, 772, 800, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, - 1184, 1328, 0, 768, 771, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, - 1184, 1344, 0, 768, 771, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ - { DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, - 1208, 1264, 0, 768, 768, 776, 817, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ - { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, - 928, 1152, 0, 624, 625, 628, 667, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, - 896, 1056, 0, 600, 601, 604, 625, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, - 976, 1040, 0, 600, 637, 643, 666, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */ - { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, - 1344, 1600, 0, 864, 865, 868, 900, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */ -}; - -struct minimode { - short w; - short h; - short r; - short rb; -}; - -static const struct minimode est3_modes[] = { - /* byte 6 */ - { 640, 350, 85, 0 }, - { 640, 400, 85, 0 }, - { 720, 400, 85, 0 }, - { 640, 480, 85, 0 }, - { 848, 480, 60, 0 }, - { 800, 600, 85, 0 }, - { 1024, 768, 85, 0 }, - { 1152, 864, 75, 0 }, - /* byte 7 */ - { 1280, 768, 60, 1 }, - { 1280, 768, 60, 0 }, - { 1280, 768, 75, 0 }, - { 1280, 768, 85, 0 }, - { 1280, 960, 60, 0 }, - { 1280, 960, 85, 0 }, - { 1280, 1024, 60, 0 }, - { 1280, 1024, 85, 0 }, - /* byte 8 */ - { 1360, 768, 60, 0 }, - { 1440, 900, 60, 1 }, - { 1440, 900, 60, 0 }, - { 1440, 900, 75, 0 }, - { 1440, 900, 85, 0 }, - { 1400, 1050, 60, 1 }, - { 1400, 1050, 60, 0 }, - { 1400, 1050, 75, 0 }, - /* byte 9 */ - { 1400, 1050, 85, 0 }, - { 1680, 1050, 60, 1 }, - { 1680, 1050, 60, 0 }, - { 1680, 1050, 75, 0 }, - { 1680, 1050, 85, 0 }, - { 1600, 1200, 60, 0 }, - { 1600, 1200, 65, 0 }, - { 1600, 1200, 70, 0 }, - /* byte 10 */ - { 1600, 1200, 75, 0 }, - { 1600, 1200, 85, 0 }, - { 1792, 1344, 60, 0 }, - { 1792, 1344, 75, 0 }, - { 1856, 1392, 60, 0 }, - { 1856, 1392, 75, 0 }, - { 1920, 1200, 60, 1 }, - { 1920, 1200, 60, 0 }, - /* byte 11 */ - { 1920, 1200, 75, 0 }, - { 1920, 1200, 85, 0 }, - { 1920, 1440, 60, 0 }, - { 1920, 1440, 75, 0 }, -}; - -static const struct minimode extra_modes[] = { - { 1024, 576, 60, 0 }, - { 1366, 768, 60, 0 }, - { 1600, 900, 60, 0 }, - { 1680, 945, 60, 0 }, - { 1920, 1080, 60, 0 }, - { 2048, 1152, 60, 0 }, - { 2048, 1536, 60, 0 }, -}; - -/* - * Probably taken from CEA-861 spec. - * This table is converted from xorg's hw/xfree86/modes/xf86EdidModes.c. - */ -static const struct drm_display_mode edid_cea_modes[] = { - /* 1 - 640x480@60Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, - 752, 800, 0, 480, 490, 492, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 2 - 720x480@60Hz */ - { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, - 798, 858, 0, 480, 489, 495, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 3 - 720x480@60Hz */ - { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, - 798, 858, 0, 480, 489, 495, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 4 - 1280x720@60Hz */ - { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, - 1430, 1650, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 5 - 1920x1080i@60Hz */ - { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008, - 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | - DRM_MODE_FLAG_INTERLACE), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 6 - 720(1440)x480i@60Hz */ - { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, - 801, 858, 0, 480, 488, 494, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 7 - 720(1440)x480i@60Hz */ - { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, - 801, 858, 0, 480, 488, 494, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 8 - 720(1440)x240@60Hz */ - { DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, - 801, 858, 0, 240, 244, 247, 262, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_DBLCLK), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 9 - 720(1440)x240@60Hz */ - { DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, - 801, 858, 0, 240, 244, 247, 262, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_DBLCLK), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 10 - 2880x480i@60Hz */ - { DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, - 3204, 3432, 0, 480, 488, 494, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 11 - 2880x480i@60Hz */ - { DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, - 3204, 3432, 0, 480, 488, 494, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 12 - 2880x240@60Hz */ - { DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, - 3204, 3432, 0, 240, 244, 247, 262, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 13 - 2880x240@60Hz */ - { DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, - 3204, 3432, 0, 240, 244, 247, 262, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 14 - 1440x480@60Hz */ - { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472, - 1596, 1716, 0, 480, 489, 495, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 15 - 1440x480@60Hz */ - { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472, - 1596, 1716, 0, 480, 489, 495, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 16 - 1920x1080@60Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, - 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 17 - 720x576@50Hz */ - { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, - 796, 864, 0, 576, 581, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 18 - 720x576@50Hz */ - { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, - 796, 864, 0, 576, 581, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 19 - 1280x720@50Hz */ - { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720, - 1760, 1980, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 20 - 1920x1080i@50Hz */ - { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448, - 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | - DRM_MODE_FLAG_INTERLACE), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 21 - 720(1440)x576i@50Hz */ - { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, - 795, 864, 0, 576, 580, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 22 - 720(1440)x576i@50Hz */ - { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, - 795, 864, 0, 576, 580, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 23 - 720(1440)x288@50Hz */ - { DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, - 795, 864, 0, 288, 290, 293, 312, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_DBLCLK), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 24 - 720(1440)x288@50Hz */ - { DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, - 795, 864, 0, 288, 290, 293, 312, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_DBLCLK), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 25 - 2880x576i@50Hz */ - { DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, - 3180, 3456, 0, 576, 580, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 26 - 2880x576i@50Hz */ - { DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, - 3180, 3456, 0, 576, 580, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 27 - 2880x288@50Hz */ - { DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, - 3180, 3456, 0, 288, 290, 293, 312, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 28 - 2880x288@50Hz */ - { DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, - 3180, 3456, 0, 288, 290, 293, 312, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 29 - 1440x576@50Hz */ - { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, - 1592, 1728, 0, 576, 581, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 30 - 1440x576@50Hz */ - { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, - 1592, 1728, 0, 576, 581, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 31 - 1920x1080@50Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, - 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 32 - 1920x1080@24Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558, - 2602, 2750, 0, 1080, 1084, 1089, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 33 - 1920x1080@25Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448, - 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 34 - 1920x1080@30Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008, - 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 35 - 2880x480@60Hz */ - { DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944, - 3192, 3432, 0, 480, 489, 495, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 36 - 2880x480@60Hz */ - { DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944, - 3192, 3432, 0, 480, 489, 495, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 37 - 2880x576@50Hz */ - { DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928, - 3184, 3456, 0, 576, 581, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 38 - 2880x576@50Hz */ - { DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928, - 3184, 3456, 0, 576, 581, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 39 - 1920x1080i@50Hz */ - { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952, - 2120, 2304, 0, 1080, 1126, 1136, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 40 - 1920x1080i@100Hz */ - { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, - 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | - DRM_MODE_FLAG_INTERLACE), - .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 41 - 1280x720@100Hz */ - { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720, - 1760, 1980, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 42 - 720x576@100Hz */ - { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, - 796, 864, 0, 576, 581, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 43 - 720x576@100Hz */ - { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, - 796, 864, 0, 576, 581, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 44 - 720(1440)x576i@100Hz */ - { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, - 795, 864, 0, 576, 580, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), - .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 45 - 720(1440)x576i@100Hz */ - { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, - 795, 864, 0, 576, 580, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), - .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 46 - 1920x1080i@120Hz */ - { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, - 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | - DRM_MODE_FLAG_INTERLACE), - .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 47 - 1280x720@120Hz */ - { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390, - 1430, 1650, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 48 - 720x480@120Hz */ - { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736, - 798, 858, 0, 480, 489, 495, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 49 - 720x480@120Hz */ - { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736, - 798, 858, 0, 480, 489, 495, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 50 - 720(1440)x480i@120Hz */ - { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739, - 801, 858, 0, 480, 488, 494, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), - .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 51 - 720(1440)x480i@120Hz */ - { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739, - 801, 858, 0, 480, 488, 494, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), - .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 52 - 720x576@200Hz */ - { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732, - 796, 864, 0, 576, 581, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 53 - 720x576@200Hz */ - { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732, - 796, 864, 0, 576, 581, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 54 - 720(1440)x576i@200Hz */ - { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, - 795, 864, 0, 576, 580, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), - .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 55 - 720(1440)x576i@200Hz */ - { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, - 795, 864, 0, 576, 580, 586, 625, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), - .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 56 - 720x480@240Hz */ - { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736, - 798, 858, 0, 480, 489, 495, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 57 - 720x480@240Hz */ - { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736, - 798, 858, 0, 480, 489, 495, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 58 - 720(1440)x480i@240 */ - { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739, - 801, 858, 0, 480, 488, 494, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), - .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 59 - 720(1440)x480i@240 */ - { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739, - 801, 858, 0, 480, 488, 494, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), - .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 60 - 1280x720@24Hz */ - { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040, - 3080, 3300, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 61 - 1280x720@25Hz */ - { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700, - 3740, 3960, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 62 - 1280x720@30Hz */ - { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040, - 3080, 3300, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 63 - 1920x1080@120Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008, - 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 64 - 1920x1080@100Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448, - 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, -}; - -/* - * HDMI 1.4 4k modes. - */ -static const struct drm_display_mode edid_4k_modes[] = { - /* 1 - 3840x2160@30Hz */ - { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, - 3840, 4016, 4104, 4400, 0, - 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 30, }, - /* 2 - 3840x2160@25Hz */ - { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, - 3840, 4896, 4984, 5280, 0, - 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 25, }, - /* 3 - 3840x2160@24Hz */ - { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, - 3840, 5116, 5204, 5500, 0, - 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 24, }, - /* 4 - 4096x2160@24Hz (SMPTE) */ - { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, - 4096, 5116, 5204, 5500, 0, - 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 24, }, -}; - -/*** DDC fetch and block validation ***/ - -static const u8 edid_header[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 -}; - -/** - * drm_edid_header_is_valid - sanity check the header of the base EDID block - * @raw_edid: pointer to raw base EDID block - * - * Sanity check the header of the base EDID block. - * - * Return: 8 if the header is perfect, down to 0 if it's totally wrong. - */ -int drm_edid_header_is_valid(const u8 *raw_edid) -{ - int i, score = 0; - - for (i = 0; i < sizeof(edid_header); i++) - if (raw_edid[i] == edid_header[i]) - score++; - - return score; -} -EXPORT_SYMBOL(drm_edid_header_is_valid); - -static int edid_fixup __read_mostly = 6; -module_param_named(edid_fixup, edid_fixup, int, 0400); -MODULE_PARM_DESC(edid_fixup, - "Minimum number of valid EDID header bytes (0-8, default 6)"); - -static void drm_get_displayid(struct drm_connector *connector, - struct edid *edid); - -static int drm_edid_block_checksum(const u8 *raw_edid) -{ - int i; - u8 csum = 0; - for (i = 0; i < EDID_LENGTH; i++) - csum += raw_edid[i]; - - return csum; -} - -static bool drm_edid_is_zero(const u8 *in_edid, int length) -{ - if (memchr_inv(in_edid, 0, length)) - return false; - - return true; -} - -/** - * drm_edid_block_valid - Sanity check the EDID block (base or extension) - * @raw_edid: pointer to raw EDID block - * @block: type of block to validate (0 for base, extension otherwise) - * @print_bad_edid: if true, dump bad EDID blocks to the console - * @edid_corrupt: if true, the header or checksum is invalid - * - * Validate a base or extension EDID block and optionally dump bad blocks to - * the console. - * - * Return: True if the block is valid, false otherwise. - */ -bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, - bool *edid_corrupt) -{ - u8 csum; - struct edid *edid = (struct edid *)raw_edid; - - if (WARN_ON(!raw_edid)) - return false; - - if (edid_fixup > 8 || edid_fixup < 0) - edid_fixup = 6; - - if (block == 0) { - int score = drm_edid_header_is_valid(raw_edid); - if (score == 8) { - if (edid_corrupt) - *edid_corrupt = false; - } else if (score >= edid_fixup) { - /* Displayport Link CTS Core 1.2 rev1.1 test 4.2.2.6 - * The corrupt flag needs to be set here otherwise, the - * fix-up code here will correct the problem, the - * checksum is correct and the test fails - */ - if (edid_corrupt) - *edid_corrupt = true; - DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); - memcpy(raw_edid, edid_header, sizeof(edid_header)); - } else { - if (edid_corrupt) - *edid_corrupt = true; - goto bad; - } - } - - csum = drm_edid_block_checksum(raw_edid); - if (csum) { - if (print_bad_edid) { - DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); - } - - if (edid_corrupt) - *edid_corrupt = true; - - /* allow CEA to slide through, switches mangle this */ - if (raw_edid[0] != 0x02) - goto bad; - } - - /* per-block-type checks */ - switch (raw_edid[0]) { - case 0: /* base */ - if (edid->version != 1) { - DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); - goto bad; - } - - if (edid->revision > 4) - DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); - break; - - default: - break; - } - - return true; - -bad: - if (print_bad_edid) { - if (drm_edid_is_zero(raw_edid, EDID_LENGTH)) { - printk(KERN_ERR "EDID block is all zeroes\n"); - } else { - printk(KERN_ERR "Raw EDID:\n"); - print_hex_dump(KERN_ERR, " \t", DUMP_PREFIX_NONE, 16, 1, - raw_edid, EDID_LENGTH, false); - } - } - return false; -} -EXPORT_SYMBOL(drm_edid_block_valid); - -/** - * drm_edid_is_valid - sanity check EDID data - * @edid: EDID data - * - * Sanity-check an entire EDID record (including extensions) - * - * Return: True if the EDID data is valid, false otherwise. - */ -bool drm_edid_is_valid(struct edid *edid) -{ - int i; - u8 *raw = (u8 *)edid; - - if (!edid) - return false; - - for (i = 0; i <= edid->extensions; i++) - if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true, NULL)) - return false; - - return true; -} -EXPORT_SYMBOL(drm_edid_is_valid); - -#define DDC_SEGMENT_ADDR 0x30 -/** - * drm_do_probe_ddc_edid() - get EDID information via I2C - * @data: I2C device adapter - * @buf: EDID data buffer to be filled - * @block: 128 byte EDID block to start fetching from - * @len: EDID data buffer length to fetch - * - * Try to fetch EDID information by calling I2C driver functions. - * - * Return: 0 on success or -1 on failure. - */ -static int -drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len) -{ - struct i2c_adapter *adapter = data; - unsigned char start = block * EDID_LENGTH; - unsigned char segment = block >> 1; - unsigned char xfers = segment ? 3 : 2; - int ret, retries = 5; - - /* - * The core I2C driver will automatically retry the transfer if the - * adapter reports EAGAIN. However, we find that bit-banging transfers - * are susceptible to errors under a heavily loaded machine and - * generate spurious NAKs and timeouts. Retrying the transfer - * of the individual block a few times seems to overcome this. - */ - do { - struct i2c_msg msgs[] = { - { - .addr = DDC_SEGMENT_ADDR, - .flags = 0, - .len = 1, - .buf = &segment, - }, { - .addr = DDC_ADDR, - .flags = 0, - .len = 1, - .buf = &start, - }, { - .addr = DDC_ADDR, - .flags = I2C_M_RD, - .len = len, - .buf = buf, - } - }; - - /* - * Avoid sending the segment addr to not upset non-compliant - * DDC monitors. - */ - ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers); - - if (ret == -ENXIO) { - DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n", - adapter->name); - break; - } - } while (ret != xfers && --retries); - - return ret == xfers ? 0 : -1; -} - -/** - * drm_do_get_edid - get EDID data using a custom EDID block read function - * @connector: connector we're probing - * @get_edid_block: EDID block read function - * @data: private data passed to the block read function - * - * When the I2C adapter connected to the DDC bus is hidden behind a device that - * exposes a different interface to read EDID blocks this function can be used - * to get EDID data using a custom block read function. - * - * As in the general case the DDC bus is accessible by the kernel at the I2C - * level, drivers must make all reasonable efforts to expose it as an I2C - * adapter and use drm_get_edid() instead of abusing this function. - * - * Return: Pointer to valid EDID or NULL if we couldn't find any. - */ -struct edid *drm_do_get_edid(struct drm_connector *connector, - int (*get_edid_block)(void *data, u8 *buf, unsigned int block, - size_t len), - void *data) -{ - int i, j = 0, valid_extensions = 0; - u8 *block, *new; - bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); - - if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL) - return NULL; - - /* base block fetch */ - for (i = 0; i < 4; i++) { - if (get_edid_block(data, block, 0, EDID_LENGTH)) - goto out; - if (drm_edid_block_valid(block, 0, print_bad_edid, - &connector->edid_corrupt)) - break; - if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) { - connector->null_edid_counter++; - goto carp; - } - } - if (i == 4) - goto carp; - - /* if there's no extensions, we're done */ - if (block[0x7e] == 0) - return (struct edid *)block; - - new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL); - if (!new) - goto out; - block = new; - - for (j = 1; j <= block[0x7e]; j++) { - for (i = 0; i < 4; i++) { - if (get_edid_block(data, - block + (valid_extensions + 1) * EDID_LENGTH, - j, EDID_LENGTH)) - goto out; - if (drm_edid_block_valid(block + (valid_extensions + 1) - * EDID_LENGTH, j, - print_bad_edid, - NULL)) { - valid_extensions++; - break; - } - } - - if (i == 4 && print_bad_edid) { - dev_warn(connector->dev->dev, - "%s: Ignoring invalid EDID block %d.\n", - connector->name, j); - - connector->bad_edid_counter++; - } - } - - if (valid_extensions != block[0x7e]) { - block[EDID_LENGTH-1] += block[0x7e] - valid_extensions; - block[0x7e] = valid_extensions; - new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL); - if (!new) - goto out; - block = new; - } - - return (struct edid *)block; - -carp: - if (print_bad_edid) { - dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n", - connector->name, j); - } - connector->bad_edid_counter++; - -out: - kfree(block); - return NULL; -} -EXPORT_SYMBOL_GPL(drm_do_get_edid); - -/** - * drm_probe_ddc() - probe DDC presence - * @adapter: I2C adapter to probe - * - * Return: True on success, false on failure. - */ -bool -drm_probe_ddc(struct i2c_adapter *adapter) -{ - unsigned char out; - - return (drm_do_probe_ddc_edid(adapter, &out, 0, 1) == 0); -} -EXPORT_SYMBOL(drm_probe_ddc); - -/** - * drm_get_edid - get EDID data, if available - * @connector: connector we're probing - * @adapter: I2C adapter to use for DDC - * - * Poke the given I2C channel to grab EDID data if possible. If found, - * attach it to the connector. - * - * Return: Pointer to valid EDID or NULL if we couldn't find any. - */ -struct edid *drm_get_edid(struct drm_connector *connector, - struct i2c_adapter *adapter) -{ - struct edid *edid; - - if (!drm_probe_ddc(adapter)) - return NULL; - - edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter); - if (edid) - drm_get_displayid(connector, edid); - return edid; -} -EXPORT_SYMBOL(drm_get_edid); - -/** - * drm_edid_duplicate - duplicate an EDID and the extensions - * @edid: EDID to duplicate - * - * Return: Pointer to duplicated EDID or NULL on allocation failure. - */ -struct edid *drm_edid_duplicate(const struct edid *edid) -{ - return kmemdup(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL); -} -EXPORT_SYMBOL(drm_edid_duplicate); - -/*** EDID parsing ***/ - -/** - * edid_vendor - match a string against EDID's obfuscated vendor field - * @edid: EDID to match - * @vendor: vendor string - * - * Returns true if @vendor is in @edid, false otherwise - */ -static bool edid_vendor(struct edid *edid, char *vendor) -{ - char edid_vendor[3]; - - edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@'; - edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) | - ((edid->mfg_id[1] & 0xe0) >> 5)) + '@'; - edid_vendor[2] = (edid->mfg_id[1] & 0x1f) + '@'; - - return !strncmp(edid_vendor, vendor, 3); -} - -/** - * edid_get_quirks - return quirk flags for a given EDID - * @edid: EDID to process - * - * This tells subsequent routines what fixes they need to apply. - */ -static u32 edid_get_quirks(struct edid *edid) -{ - struct edid_quirk *quirk; - int i; - - for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { - quirk = &edid_quirk_list[i]; - - if (edid_vendor(edid, quirk->vendor) && - (EDID_PRODUCT_ID(edid) == quirk->product_id)) - return quirk->quirks; - } - - return 0; -} - -#define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) -#define MODE_REFRESH_DIFF(c,t) (abs((c) - (t))) - -/** - * edid_fixup_preferred - set preferred modes based on quirk list - * @connector: has mode list to fix up - * @quirks: quirks list - * - * Walk the mode list for @connector, clearing the preferred status - * on existing modes and setting it anew for the right mode ala @quirks. - */ -static void edid_fixup_preferred(struct drm_connector *connector, - u32 quirks) -{ - struct drm_display_mode *t, *cur_mode, *preferred_mode; - int target_refresh = 0; - int cur_vrefresh, preferred_vrefresh; - - if (list_empty(&connector->probed_modes)) - return; - - if (quirks & EDID_QUIRK_PREFER_LARGE_60) - target_refresh = 60; - if (quirks & EDID_QUIRK_PREFER_LARGE_75) - target_refresh = 75; - - preferred_mode = list_first_entry(&connector->probed_modes, - struct drm_display_mode, head); - - list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) { - cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED; - - if (cur_mode == preferred_mode) - continue; - - /* Largest mode is preferred */ - if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode)) - preferred_mode = cur_mode; - - cur_vrefresh = cur_mode->vrefresh ? - cur_mode->vrefresh : drm_mode_vrefresh(cur_mode); - preferred_vrefresh = preferred_mode->vrefresh ? - preferred_mode->vrefresh : drm_mode_vrefresh(preferred_mode); - /* At a given size, try to get closest to target refresh */ - if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) && - MODE_REFRESH_DIFF(cur_vrefresh, target_refresh) < - MODE_REFRESH_DIFF(preferred_vrefresh, target_refresh)) { - preferred_mode = cur_mode; - } - } - - preferred_mode->type |= DRM_MODE_TYPE_PREFERRED; -} - -static bool -mode_is_rb(const struct drm_display_mode *mode) -{ - return (mode->htotal - mode->hdisplay == 160) && - (mode->hsync_end - mode->hdisplay == 80) && - (mode->hsync_end - mode->hsync_start == 32) && - (mode->vsync_start - mode->vdisplay == 3); -} - -/* - * drm_mode_find_dmt - Create a copy of a mode if present in DMT - * @dev: Device to duplicate against - * @hsize: Mode width - * @vsize: Mode height - * @fresh: Mode refresh rate - * @rb: Mode reduced-blanking-ness - * - * Walk the DMT mode list looking for a match for the given parameters. - * - * Return: A newly allocated copy of the mode, or NULL if not found. - */ -struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, - int hsize, int vsize, int fresh, - bool rb) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) { - const struct drm_display_mode *ptr = &drm_dmt_modes[i]; - if (hsize != ptr->hdisplay) - continue; - if (vsize != ptr->vdisplay) - continue; - if (fresh != drm_mode_vrefresh(ptr)) - continue; - if (rb != mode_is_rb(ptr)) - continue; - - return drm_mode_duplicate(dev, ptr); - } - - return NULL; -} -EXPORT_SYMBOL(drm_mode_find_dmt); - -typedef void detailed_cb(struct detailed_timing *timing, void *closure); - -static void -cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) -{ - int i, n = 0; - u8 d = ext[0x02]; - u8 *det_base = ext + d; - - n = (127 - d) / 18; - for (i = 0; i < n; i++) - cb((struct detailed_timing *)(det_base + 18 * i), closure); -} - -static void -vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) -{ - unsigned int i, n = min((int)ext[0x02], 6); - u8 *det_base = ext + 5; - - if (ext[0x01] != 1) - return; /* unknown version */ - - for (i = 0; i < n; i++) - cb((struct detailed_timing *)(det_base + 18 * i), closure); -} - -static void -drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) -{ - int i; - struct edid *edid = (struct edid *)raw_edid; - - if (edid == NULL) - return; - - for (i = 0; i < EDID_DETAILED_TIMINGS; i++) - cb(&(edid->detailed_timings[i]), closure); - - for (i = 1; i <= raw_edid[0x7e]; i++) { - u8 *ext = raw_edid + (i * EDID_LENGTH); - switch (*ext) { - case CEA_EXT: - cea_for_each_detailed_block(ext, cb, closure); - break; - case VTB_EXT: - vtb_for_each_detailed_block(ext, cb, closure); - break; - default: - break; - } - } -} - -static void -is_rb(struct detailed_timing *t, void *data) -{ - u8 *r = (u8 *)t; - if (r[3] == EDID_DETAIL_MONITOR_RANGE) - if (r[15] & 0x10) - *(bool *)data = true; -} - -/* EDID 1.4 defines this explicitly. For EDID 1.3, we guess, badly. */ -static bool -drm_monitor_supports_rb(struct edid *edid) -{ - if (edid->revision >= 4) { - bool ret = false; - drm_for_each_detailed_block((u8 *)edid, is_rb, &ret); - return ret; - } - - return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0); -} - -static void -find_gtf2(struct detailed_timing *t, void *data) -{ - u8 *r = (u8 *)t; - if (r[3] == EDID_DETAIL_MONITOR_RANGE && r[10] == 0x02) - *(u8 **)data = r; -} - -/* Secondary GTF curve kicks in above some break frequency */ -static int -drm_gtf2_hbreak(struct edid *edid) -{ - u8 *r = NULL; - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? (r[12] * 2) : 0; -} - -static int -drm_gtf2_2c(struct edid *edid) -{ - u8 *r = NULL; - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? r[13] : 0; -} - -static int -drm_gtf2_m(struct edid *edid) -{ - u8 *r = NULL; - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? (r[15] << 8) + r[14] : 0; -} - -static int -drm_gtf2_k(struct edid *edid) -{ - u8 *r = NULL; - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? r[16] : 0; -} - -static int -drm_gtf2_2j(struct edid *edid) -{ - u8 *r = NULL; - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? r[17] : 0; -} - -/** - * standard_timing_level - get std. timing level(CVT/GTF/DMT) - * @edid: EDID block to scan - */ -static int standard_timing_level(struct edid *edid) -{ - if (edid->revision >= 2) { - if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)) - return LEVEL_CVT; - if (drm_gtf2_hbreak(edid)) - return LEVEL_GTF2; - return LEVEL_GTF; - } - return LEVEL_DMT; -} - -/* - * 0 is reserved. The spec says 0x01 fill for unused timings. Some old - * monitors fill with ascii space (0x20) instead. - */ -static int -bad_std_timing(u8 a, u8 b) -{ - return (a == 0x00 && b == 0x00) || - (a == 0x01 && b == 0x01) || - (a == 0x20 && b == 0x20); -} - -/** - * drm_mode_std - convert standard mode info (width, height, refresh) into mode - * @connector: connector of for the EDID block - * @edid: EDID block to scan - * @t: standard timing params - * - * Take the standard timing params (in this case width, aspect, and refresh) - * and convert them into a real mode using CVT/GTF/DMT. - */ -static struct drm_display_mode * -drm_mode_std(struct drm_connector *connector, struct edid *edid, - struct std_timing *t) -{ - struct drm_device *dev = connector->dev; - struct drm_display_mode *m, *mode = NULL; - int hsize, vsize; - int vrefresh_rate; - unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK) - >> EDID_TIMING_ASPECT_SHIFT; - unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK) - >> EDID_TIMING_VFREQ_SHIFT; - int timing_level = standard_timing_level(edid); - - if (bad_std_timing(t->hsize, t->vfreq_aspect)) - return NULL; - - /* According to the EDID spec, the hdisplay = hsize * 8 + 248 */ - hsize = t->hsize * 8 + 248; - /* vrefresh_rate = vfreq + 60 */ - vrefresh_rate = vfreq + 60; - /* the vdisplay is calculated based on the aspect ratio */ - if (aspect_ratio == 0) { - if (edid->revision < 3) - vsize = hsize; - else - vsize = (hsize * 10) / 16; - } else if (aspect_ratio == 1) - vsize = (hsize * 3) / 4; - else if (aspect_ratio == 2) - vsize = (hsize * 4) / 5; - else - vsize = (hsize * 9) / 16; - - /* HDTV hack, part 1 */ - if (vrefresh_rate == 60 && - ((hsize == 1360 && vsize == 765) || - (hsize == 1368 && vsize == 769))) { - hsize = 1366; - vsize = 768; - } - - /* - * If this connector already has a mode for this size and refresh - * rate (because it came from detailed or CVT info), use that - * instead. This way we don't have to guess at interlace or - * reduced blanking. - */ - list_for_each_entry(m, &connector->probed_modes, head) - if (m->hdisplay == hsize && m->vdisplay == vsize && - drm_mode_vrefresh(m) == vrefresh_rate) - return NULL; - - /* HDTV hack, part 2 */ - if (hsize == 1366 && vsize == 768 && vrefresh_rate == 60) { - mode = drm_cvt_mode(dev, 1366, 768, vrefresh_rate, 0, 0, - false); - mode->hdisplay = 1366; - mode->hsync_start = mode->hsync_start - 1; - mode->hsync_end = mode->hsync_end - 1; - return mode; - } - - /* check whether it can be found in default mode table */ - if (drm_monitor_supports_rb(edid)) { - mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, - true); - if (mode) - return mode; - } - mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, false); - if (mode) - return mode; - - /* okay, generate it */ - switch (timing_level) { - case LEVEL_DMT: - break; - case LEVEL_GTF: - mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); - break; - case LEVEL_GTF2: - /* - * This is potentially wrong if there's ever a monitor with - * more than one ranges section, each claiming a different - * secondary GTF curve. Please don't do that. - */ - mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); - if (!mode) - return NULL; - if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) { - drm_mode_destroy(dev, mode); - mode = drm_gtf_mode_complex(dev, hsize, vsize, - vrefresh_rate, 0, 0, - drm_gtf2_m(edid), - drm_gtf2_2c(edid), - drm_gtf2_k(edid), - drm_gtf2_2j(edid)); - } - break; - case LEVEL_CVT: - mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0, - false); - break; - } - return mode; -} - -/* - * EDID is delightfully ambiguous about how interlaced modes are to be - * encoded. Our internal representation is of frame height, but some - * HDTV detailed timings are encoded as field height. - * - * The format list here is from CEA, in frame size. Technically we - * should be checking refresh rate too. Whatever. - */ -static void -drm_mode_do_interlace_quirk(struct drm_display_mode *mode, - struct detailed_pixel_timing *pt) -{ - int i; - static const struct { - int w, h; - } cea_interlaced[] = { - { 1920, 1080 }, - { 720, 480 }, - { 1440, 480 }, - { 2880, 480 }, - { 720, 576 }, - { 1440, 576 }, - { 2880, 576 }, - }; - - if (!(pt->misc & DRM_EDID_PT_INTERLACED)) - return; - - for (i = 0; i < ARRAY_SIZE(cea_interlaced); i++) { - if ((mode->hdisplay == cea_interlaced[i].w) && - (mode->vdisplay == cea_interlaced[i].h / 2)) { - mode->vdisplay *= 2; - mode->vsync_start *= 2; - mode->vsync_end *= 2; - mode->vtotal *= 2; - mode->vtotal |= 1; - } - } - - mode->flags |= DRM_MODE_FLAG_INTERLACE; -} - -/** - * drm_mode_detailed - create a new mode from an EDID detailed timing section - * @dev: DRM device (needed to create new mode) - * @edid: EDID block - * @timing: EDID detailed timing info - * @quirks: quirks to apply - * - * An EDID detailed timing block contains enough info for us to create and - * return a new struct drm_display_mode. - */ -static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, - struct edid *edid, - struct detailed_timing *timing, - u32 quirks) -{ - struct drm_display_mode *mode; - struct detailed_pixel_timing *pt = &timing->data.pixel_data; - unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo; - unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo; - unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo; - unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo; - unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo; - unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo; - unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) << 2 | pt->vsync_offset_pulse_width_lo >> 4; - unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf); - - /* ignore tiny modes */ - if (hactive < 64 || vactive < 64) - return NULL; - - if (pt->misc & DRM_EDID_PT_STEREO) { - DRM_DEBUG_KMS("stereo mode not supported\n"); - return NULL; - } - if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) { - DRM_DEBUG_KMS("composite sync not supported\n"); - } - - /* it is incorrect if hsync/vsync width is zero */ - if (!hsync_pulse_width || !vsync_pulse_width) { - DRM_DEBUG_KMS("Incorrect Detailed timing. " - "Wrong Hsync/Vsync pulse width\n"); - return NULL; - } - - if (quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) { - mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false); - if (!mode) - return NULL; - - goto set_size; - } - - mode = drm_mode_create(dev); - if (!mode) - return NULL; - - if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) - timing->pixel_clock = cpu_to_le16(1088); - - mode->clock = le16_to_cpu(timing->pixel_clock) * 10; - - mode->hdisplay = hactive; - mode->hsync_start = mode->hdisplay + hsync_offset; - mode->hsync_end = mode->hsync_start + hsync_pulse_width; - mode->htotal = mode->hdisplay + hblank; - - mode->vdisplay = vactive; - mode->vsync_start = mode->vdisplay + vsync_offset; - mode->vsync_end = mode->vsync_start + vsync_pulse_width; - mode->vtotal = mode->vdisplay + vblank; - - /* Some EDIDs have bogus h/vtotal values */ - if (mode->hsync_end > mode->htotal) - mode->htotal = mode->hsync_end + 1; - if (mode->vsync_end > mode->vtotal) - mode->vtotal = mode->vsync_end + 1; - - drm_mode_do_interlace_quirk(mode, pt); - - if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { - pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE; - } - - mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? - DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; - mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ? - DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; - -set_size: - mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4; - mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8; - - if (quirks & EDID_QUIRK_DETAILED_IN_CM) { - mode->width_mm *= 10; - mode->height_mm *= 10; - } - - if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { - mode->width_mm = edid->width_cm * 10; - mode->height_mm = edid->height_cm * 10; - } - - mode->type = DRM_MODE_TYPE_DRIVER; - mode->vrefresh = drm_mode_vrefresh(mode); - drm_mode_set_name(mode); - - return mode; -} - -static bool -mode_in_hsync_range(const struct drm_display_mode *mode, - struct edid *edid, u8 *t) -{ - int hsync, hmin, hmax; - - hmin = t[7]; - if (edid->revision >= 4) - hmin += ((t[4] & 0x04) ? 255 : 0); - hmax = t[8]; - if (edid->revision >= 4) - hmax += ((t[4] & 0x08) ? 255 : 0); - hsync = drm_mode_hsync(mode); - - return (hsync <= hmax && hsync >= hmin); -} - -static bool -mode_in_vsync_range(const struct drm_display_mode *mode, - struct edid *edid, u8 *t) -{ - int vsync, vmin, vmax; - - vmin = t[5]; - if (edid->revision >= 4) - vmin += ((t[4] & 0x01) ? 255 : 0); - vmax = t[6]; - if (edid->revision >= 4) - vmax += ((t[4] & 0x02) ? 255 : 0); - vsync = drm_mode_vrefresh(mode); - - return (vsync <= vmax && vsync >= vmin); -} - -static u32 -range_pixel_clock(struct edid *edid, u8 *t) -{ - /* unspecified */ - if (t[9] == 0 || t[9] == 255) - return 0; - - /* 1.4 with CVT support gives us real precision, yay */ - if (edid->revision >= 4 && t[10] == 0x04) - return (t[9] * 10000) - ((t[12] >> 2) * 250); - - /* 1.3 is pathetic, so fuzz up a bit */ - return t[9] * 10000 + 5001; -} - -static bool -mode_in_range(const struct drm_display_mode *mode, struct edid *edid, - struct detailed_timing *timing) -{ - u32 max_clock; - u8 *t = (u8 *)timing; - - if (!mode_in_hsync_range(mode, edid, t)) - return false; - - if (!mode_in_vsync_range(mode, edid, t)) - return false; - - if ((max_clock = range_pixel_clock(edid, t))) - if (mode->clock > max_clock) - return false; - - /* 1.4 max horizontal check */ - if (edid->revision >= 4 && t[10] == 0x04) - if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3)))) - return false; - - if (mode_is_rb(mode) && !drm_monitor_supports_rb(edid)) - return false; - - return true; -} - -static bool valid_inferred_mode(const struct drm_connector *connector, - const struct drm_display_mode *mode) -{ - const struct drm_display_mode *m; - bool ok = false; - - list_for_each_entry(m, &connector->probed_modes, head) { - if (mode->hdisplay == m->hdisplay && - mode->vdisplay == m->vdisplay && - drm_mode_vrefresh(mode) == drm_mode_vrefresh(m)) - return false; /* duplicated */ - if (mode->hdisplay <= m->hdisplay && - mode->vdisplay <= m->vdisplay) - ok = true; - } - return ok; -} - -static int -drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid, - struct detailed_timing *timing) -{ - int i, modes = 0; - struct drm_display_mode *newmode; - struct drm_device *dev = connector->dev; - - for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) { - if (mode_in_range(drm_dmt_modes + i, edid, timing) && - valid_inferred_mode(connector, drm_dmt_modes + i)) { - newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]); - if (newmode) { - drm_mode_probed_add(connector, newmode); - modes++; - } - } - } - - return modes; -} - -/* fix up 1366x768 mode from 1368x768; - * GFT/CVT can't express 1366 width which isn't dividable by 8 - */ -static void fixup_mode_1366x768(struct drm_display_mode *mode) -{ - if (mode->hdisplay == 1368 && mode->vdisplay == 768) { - mode->hdisplay = 1366; - mode->hsync_start--; - mode->hsync_end--; - drm_mode_set_name(mode); - } -} - -static int -drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, - struct detailed_timing *timing) -{ - int i, modes = 0; - struct drm_display_mode *newmode; - struct drm_device *dev = connector->dev; - - for (i = 0; i < ARRAY_SIZE(extra_modes); i++) { - const struct minimode *m = &extra_modes[i]; - newmode = drm_gtf_mode(dev, m->w, m->h, m->r, 0, 0); - if (!newmode) - return modes; - - fixup_mode_1366x768(newmode); - if (!mode_in_range(newmode, edid, timing) || - !valid_inferred_mode(connector, newmode)) { - drm_mode_destroy(dev, newmode); - continue; - } - - drm_mode_probed_add(connector, newmode); - modes++; - } - - return modes; -} - -static int -drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, - struct detailed_timing *timing) -{ - int i, modes = 0; - struct drm_display_mode *newmode; - struct drm_device *dev = connector->dev; - bool rb = drm_monitor_supports_rb(edid); - - for (i = 0; i < ARRAY_SIZE(extra_modes); i++) { - const struct minimode *m = &extra_modes[i]; - newmode = drm_cvt_mode(dev, m->w, m->h, m->r, rb, 0, 0); - if (!newmode) - return modes; - - fixup_mode_1366x768(newmode); - if (!mode_in_range(newmode, edid, timing) || - !valid_inferred_mode(connector, newmode)) { - drm_mode_destroy(dev, newmode); - continue; - } - - drm_mode_probed_add(connector, newmode); - modes++; - } - - return modes; -} - -static void -do_inferred_modes(struct detailed_timing *timing, void *c) -{ - struct detailed_mode_closure *closure = c; - struct detailed_non_pixel *data = &timing->data.other_data; - struct detailed_data_monitor_range *range = &data->data.range; - - if (data->type != EDID_DETAIL_MONITOR_RANGE) - return; - - closure->modes += drm_dmt_modes_for_range(closure->connector, - closure->edid, - timing); - - if (!version_greater(closure->edid, 1, 1)) - return; /* GTF not defined yet */ - - switch (range->flags) { - case 0x02: /* secondary gtf, XXX could do more */ - case 0x00: /* default gtf */ - closure->modes += drm_gtf_modes_for_range(closure->connector, - closure->edid, - timing); - break; - case 0x04: /* cvt, only in 1.4+ */ - if (!version_greater(closure->edid, 1, 3)) - break; - - closure->modes += drm_cvt_modes_for_range(closure->connector, - closure->edid, - timing); - break; - case 0x01: /* just the ranges, no formula */ - default: - break; - } -} - -static int -add_inferred_modes(struct drm_connector *connector, struct edid *edid) -{ - struct detailed_mode_closure closure = { - .connector = connector, - .edid = edid, - }; - - if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block((u8 *)edid, do_inferred_modes, - &closure); - - return closure.modes; -} - -static int -drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing) -{ - int i, j, m, modes = 0; - struct drm_display_mode *mode; - u8 *est = ((u8 *)timing) + 5; - - for (i = 0; i < 6; i++) { - for (j = 7; j >= 0; j--) { - m = (i * 8) + (7 - j); - if (m >= ARRAY_SIZE(est3_modes)) - break; - if (est[i] & (1 << j)) { - mode = drm_mode_find_dmt(connector->dev, - est3_modes[m].w, - est3_modes[m].h, - est3_modes[m].r, - est3_modes[m].rb); - if (mode) { - drm_mode_probed_add(connector, mode); - modes++; - } - } - } - } - - return modes; -} - -static void -do_established_modes(struct detailed_timing *timing, void *c) -{ - struct detailed_mode_closure *closure = c; - struct detailed_non_pixel *data = &timing->data.other_data; - - if (data->type == EDID_DETAIL_EST_TIMINGS) - closure->modes += drm_est3_modes(closure->connector, timing); -} - -/** - * add_established_modes - get est. modes from EDID and add them - * @connector: connector to add mode(s) to - * @edid: EDID block to scan - * - * Each EDID block contains a bitmap of the supported "established modes" list - * (defined above). Tease them out and add them to the global modes list. - */ -static int -add_established_modes(struct drm_connector *connector, struct edid *edid) -{ - struct drm_device *dev = connector->dev; - unsigned long est_bits = edid->established_timings.t1 | - (edid->established_timings.t2 << 8) | - ((edid->established_timings.mfg_rsvd & 0x80) << 9); - int i, modes = 0; - struct detailed_mode_closure closure = { - .connector = connector, - .edid = edid, - }; - - for (i = 0; i <= EDID_EST_TIMINGS; i++) { - if (est_bits & (1<data.other_data; - struct drm_connector *connector = closure->connector; - struct edid *edid = closure->edid; - - if (data->type == EDID_DETAIL_STD_MODES) { - int i; - for (i = 0; i < 6; i++) { - struct std_timing *std; - struct drm_display_mode *newmode; - - std = &data->data.timings[i]; - newmode = drm_mode_std(connector, edid, std); - if (newmode) { - drm_mode_probed_add(connector, newmode); - closure->modes++; - } - } - } -} - -/** - * add_standard_modes - get std. modes from EDID and add them - * @connector: connector to add mode(s) to - * @edid: EDID block to scan - * - * Standard modes can be calculated using the appropriate standard (DMT, - * GTF or CVT. Grab them from @edid and add them to the list. - */ -static int -add_standard_modes(struct drm_connector *connector, struct edid *edid) -{ - int i, modes = 0; - struct detailed_mode_closure closure = { - .connector = connector, - .edid = edid, - }; - - for (i = 0; i < EDID_STD_TIMINGS; i++) { - struct drm_display_mode *newmode; - - newmode = drm_mode_std(connector, edid, - &edid->standard_timings[i]); - if (newmode) { - drm_mode_probed_add(connector, newmode); - modes++; - } - } - - if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block((u8 *)edid, do_standard_modes, - &closure); - - /* XXX should also look for standard codes in VTB blocks */ - - return modes + closure.modes; -} - -static int drm_cvt_modes(struct drm_connector *connector, - struct detailed_timing *timing) -{ - int i, j, modes = 0; - struct drm_display_mode *newmode; - struct drm_device *dev = connector->dev; - struct cvt_timing *cvt; - const int rates[] = { 60, 85, 75, 60, 50 }; - const u8 empty[3] = { 0, 0, 0 }; - - for (i = 0; i < 4; i++) { - int uninitialized_var(width), height; - cvt = &(timing->data.other_data.data.cvt[i]); - - if (!memcmp(cvt->code, empty, 3)) - continue; - - height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2; - switch (cvt->code[1] & 0x0c) { - case 0x00: - width = height * 4 / 3; - break; - case 0x04: - width = height * 16 / 9; - break; - case 0x08: - width = height * 16 / 10; - break; - case 0x0c: - width = height * 15 / 9; - break; - } - - for (j = 1; j < 5; j++) { - if (cvt->code[2] & (1 << j)) { - newmode = drm_cvt_mode(dev, width, height, - rates[j], j == 0, - false, false); - if (newmode) { - drm_mode_probed_add(connector, newmode); - modes++; - } - } - } - } - - return modes; -} - -static void -do_cvt_mode(struct detailed_timing *timing, void *c) -{ - struct detailed_mode_closure *closure = c; - struct detailed_non_pixel *data = &timing->data.other_data; - - if (data->type == EDID_DETAIL_CVT_3BYTE) - closure->modes += drm_cvt_modes(closure->connector, timing); -} - -static int -add_cvt_modes(struct drm_connector *connector, struct edid *edid) -{ - struct detailed_mode_closure closure = { - .connector = connector, - .edid = edid, - }; - - if (version_greater(edid, 1, 2)) - drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure); - - /* XXX should also look for CVT codes in VTB blocks */ - - return closure.modes; -} - -static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode); - -static void -do_detailed_mode(struct detailed_timing *timing, void *c) -{ - struct detailed_mode_closure *closure = c; - struct drm_display_mode *newmode; - - if (timing->pixel_clock) { - newmode = drm_mode_detailed(closure->connector->dev, - closure->edid, timing, - closure->quirks); - if (!newmode) - return; - - if (closure->preferred) - newmode->type |= DRM_MODE_TYPE_PREFERRED; - - /* - * Detailed modes are limited to 10kHz pixel clock resolution, - * so fix up anything that looks like CEA/HDMI mode, but the clock - * is just slightly off. - */ - fixup_detailed_cea_mode_clock(newmode); - - drm_mode_probed_add(closure->connector, newmode); - closure->modes++; - closure->preferred = 0; - } -} - -/* - * add_detailed_modes - Add modes from detailed timings - * @connector: attached connector - * @edid: EDID block to scan - * @quirks: quirks to apply - */ -static int -add_detailed_modes(struct drm_connector *connector, struct edid *edid, - u32 quirks) -{ - struct detailed_mode_closure closure = { - .connector = connector, - .edid = edid, - .preferred = 1, - .quirks = quirks, - }; - - if (closure.preferred && !version_greater(edid, 1, 3)) - closure.preferred = - (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); - - drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure); - - return closure.modes; -} - -#define AUDIO_BLOCK 0x01 -#define VIDEO_BLOCK 0x02 -#define VENDOR_BLOCK 0x03 -#define SPEAKER_BLOCK 0x04 -#define VIDEO_CAPABILITY_BLOCK 0x07 -#define EDID_BASIC_AUDIO (1 << 6) -#define EDID_CEA_YCRCB444 (1 << 5) -#define EDID_CEA_YCRCB422 (1 << 4) -#define EDID_CEA_VCDB_QS (1 << 6) - -/* - * Search EDID for CEA extension block. - */ -static u8 *drm_find_edid_extension(struct edid *edid, int ext_id) -{ - u8 *edid_ext = NULL; - int i; - - /* No EDID or EDID extensions */ - if (edid == NULL || edid->extensions == 0) - return NULL; - - /* Find CEA extension */ - for (i = 0; i < edid->extensions; i++) { - edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); - if (edid_ext[0] == ext_id) - break; - } - - if (i == edid->extensions) - return NULL; - - return edid_ext; -} - -static u8 *drm_find_cea_extension(struct edid *edid) -{ - return drm_find_edid_extension(edid, CEA_EXT); -} - -static u8 *drm_find_displayid_extension(struct edid *edid) -{ - return drm_find_edid_extension(edid, DISPLAYID_EXT); -} - -/* - * Calculate the alternate clock for the CEA mode - * (60Hz vs. 59.94Hz etc.) - */ -static unsigned int -cea_mode_alternate_clock(const struct drm_display_mode *cea_mode) -{ - unsigned int clock = cea_mode->clock; - - if (cea_mode->vrefresh % 6 != 0) - return clock; - - /* - * edid_cea_modes contains the 59.94Hz - * variant for 240 and 480 line modes, - * and the 60Hz variant otherwise. - */ - if (cea_mode->vdisplay == 240 || cea_mode->vdisplay == 480) - clock = DIV_ROUND_CLOSEST(clock * 1001, 1000); - else - clock = DIV_ROUND_CLOSEST(clock * 1000, 1001); - - return clock; -} - -/** - * drm_match_cea_mode - look for a CEA mode matching given mode - * @to_match: display mode - * - * Return: The CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861 - * mode. - */ -u8 drm_match_cea_mode(const struct drm_display_mode *to_match) -{ - u8 mode; - - if (!to_match->clock) - return 0; - - for (mode = 0; mode < ARRAY_SIZE(edid_cea_modes); mode++) { - const struct drm_display_mode *cea_mode = &edid_cea_modes[mode]; - unsigned int clock1, clock2; - - /* Check both 60Hz and 59.94Hz */ - clock1 = cea_mode->clock; - clock2 = cea_mode_alternate_clock(cea_mode); - - if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) || - KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) && - drm_mode_equal_no_clocks_no_stereo(to_match, cea_mode)) - return mode + 1; - } - return 0; -} -EXPORT_SYMBOL(drm_match_cea_mode); - -/** - * drm_get_cea_aspect_ratio - get the picture aspect ratio corresponding to - * the input VIC from the CEA mode list - * @video_code: ID given to each of the CEA modes - * - * Returns picture aspect ratio - */ -enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code) -{ - /* return picture aspect ratio for video_code - 1 to access the - * right array element - */ - return edid_cea_modes[video_code-1].picture_aspect_ratio; -} -EXPORT_SYMBOL(drm_get_cea_aspect_ratio); - -/* - * Calculate the alternate clock for HDMI modes (those from the HDMI vendor - * specific block). - * - * It's almost like cea_mode_alternate_clock(), we just need to add an - * exception for the VIC 4 mode (4096x2160@24Hz): no alternate clock for this - * one. - */ -static unsigned int -hdmi_mode_alternate_clock(const struct drm_display_mode *hdmi_mode) -{ - if (hdmi_mode->vdisplay == 4096 && hdmi_mode->hdisplay == 2160) - return hdmi_mode->clock; - - return cea_mode_alternate_clock(hdmi_mode); -} - -/* - * drm_match_hdmi_mode - look for a HDMI mode matching given mode - * @to_match: display mode - * - * An HDMI mode is one defined in the HDMI vendor specific block. - * - * Returns the HDMI Video ID (VIC) of the mode or 0 if it isn't one. - */ -static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match) -{ - u8 mode; - - if (!to_match->clock) - return 0; - - for (mode = 0; mode < ARRAY_SIZE(edid_4k_modes); mode++) { - const struct drm_display_mode *hdmi_mode = &edid_4k_modes[mode]; - unsigned int clock1, clock2; - - /* Make sure to also match alternate clocks */ - clock1 = hdmi_mode->clock; - clock2 = hdmi_mode_alternate_clock(hdmi_mode); - - if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) || - KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) && - drm_mode_equal_no_clocks_no_stereo(to_match, hdmi_mode)) - return mode + 1; - } - return 0; -} - -static int -add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid) -{ - struct drm_device *dev = connector->dev; - struct drm_display_mode *mode, *tmp; - LIST_HEAD(list); - int modes = 0; - - /* Don't add CEA modes if the CEA extension block is missing */ - if (!drm_find_cea_extension(edid)) - return 0; - - /* - * Go through all probed modes and create a new mode - * with the alternate clock for certain CEA modes. - */ - list_for_each_entry(mode, &connector->probed_modes, head) { - const struct drm_display_mode *cea_mode = NULL; - struct drm_display_mode *newmode; - u8 mode_idx = drm_match_cea_mode(mode) - 1; - unsigned int clock1, clock2; - - if (mode_idx < ARRAY_SIZE(edid_cea_modes)) { - cea_mode = &edid_cea_modes[mode_idx]; - clock2 = cea_mode_alternate_clock(cea_mode); - } else { - mode_idx = drm_match_hdmi_mode(mode) - 1; - if (mode_idx < ARRAY_SIZE(edid_4k_modes)) { - cea_mode = &edid_4k_modes[mode_idx]; - clock2 = hdmi_mode_alternate_clock(cea_mode); - } - } - - if (!cea_mode) - continue; - - clock1 = cea_mode->clock; - - if (clock1 == clock2) - continue; - - if (mode->clock != clock1 && mode->clock != clock2) - continue; - - newmode = drm_mode_duplicate(dev, cea_mode); - if (!newmode) - continue; - - /* Carry over the stereo flags */ - newmode->flags |= mode->flags & DRM_MODE_FLAG_3D_MASK; - - /* - * The current mode could be either variant. Make - * sure to pick the "other" clock for the new mode. - */ - if (mode->clock != clock1) - newmode->clock = clock1; - else - newmode->clock = clock2; - - list_add_tail(&newmode->head, &list); - } - - list_for_each_entry_safe(mode, tmp, &list, head) { - list_del(&mode->head); - drm_mode_probed_add(connector, mode); - modes++; - } - - return modes; -} - -static struct drm_display_mode * -drm_display_mode_from_vic_index(struct drm_connector *connector, - const u8 *video_db, u8 video_len, - u8 video_index) -{ - struct drm_device *dev = connector->dev; - struct drm_display_mode *newmode; - u8 cea_mode; - - if (video_db == NULL || video_index >= video_len) - return NULL; - - /* CEA modes are numbered 1..127 */ - cea_mode = (video_db[video_index] & 127) - 1; - if (cea_mode >= ARRAY_SIZE(edid_cea_modes)) - return NULL; - - newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]); - if (!newmode) - return NULL; - - newmode->vrefresh = 0; - - return newmode; -} - -static int -do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) -{ - int i, modes = 0; - - for (i = 0; i < len; i++) { - struct drm_display_mode *mode; - mode = drm_display_mode_from_vic_index(connector, db, len, i); - if (mode) { - drm_mode_probed_add(connector, mode); - modes++; - } - } - - return modes; -} - -struct stereo_mandatory_mode { - int width, height, vrefresh; - unsigned int flags; -}; - -static const struct stereo_mandatory_mode stereo_mandatory_modes[] = { - { 1920, 1080, 24, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, - { 1920, 1080, 24, DRM_MODE_FLAG_3D_FRAME_PACKING }, - { 1920, 1080, 50, - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, - { 1920, 1080, 60, - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, - { 1280, 720, 50, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, - { 1280, 720, 50, DRM_MODE_FLAG_3D_FRAME_PACKING }, - { 1280, 720, 60, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, - { 1280, 720, 60, DRM_MODE_FLAG_3D_FRAME_PACKING } -}; - -static bool -stereo_match_mandatory(const struct drm_display_mode *mode, - const struct stereo_mandatory_mode *stereo_mode) -{ - unsigned int interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; - - return mode->hdisplay == stereo_mode->width && - mode->vdisplay == stereo_mode->height && - interlaced == (stereo_mode->flags & DRM_MODE_FLAG_INTERLACE) && - drm_mode_vrefresh(mode) == stereo_mode->vrefresh; -} - -static int add_hdmi_mandatory_stereo_modes(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - const struct drm_display_mode *mode; - struct list_head stereo_modes; - int modes = 0, i; - - INIT_LIST_HEAD(&stereo_modes); - - list_for_each_entry(mode, &connector->probed_modes, head) { - for (i = 0; i < ARRAY_SIZE(stereo_mandatory_modes); i++) { - const struct stereo_mandatory_mode *mandatory; - struct drm_display_mode *new_mode; - - if (!stereo_match_mandatory(mode, - &stereo_mandatory_modes[i])) - continue; - - mandatory = &stereo_mandatory_modes[i]; - new_mode = drm_mode_duplicate(dev, mode); - if (!new_mode) - continue; - - new_mode->flags |= mandatory->flags; - list_add_tail(&new_mode->head, &stereo_modes); - modes++; - } - } - - list_splice_tail(&stereo_modes, &connector->probed_modes); - - return modes; -} - -static int add_hdmi_mode(struct drm_connector *connector, u8 vic) -{ - struct drm_device *dev = connector->dev; - struct drm_display_mode *newmode; - - vic--; /* VICs start at 1 */ - if (vic >= ARRAY_SIZE(edid_4k_modes)) { - DRM_ERROR("Unknown HDMI VIC: %d\n", vic); - return 0; - } - - newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]); - if (!newmode) - return 0; - - drm_mode_probed_add(connector, newmode); - - return 1; -} - -static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, - const u8 *video_db, u8 video_len, u8 video_index) -{ - struct drm_display_mode *newmode; - int modes = 0; - - if (structure & (1 << 0)) { - newmode = drm_display_mode_from_vic_index(connector, video_db, - video_len, - video_index); - if (newmode) { - newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING; - drm_mode_probed_add(connector, newmode); - modes++; - } - } - if (structure & (1 << 6)) { - newmode = drm_display_mode_from_vic_index(connector, video_db, - video_len, - video_index); - if (newmode) { - newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; - drm_mode_probed_add(connector, newmode); - modes++; - } - } - if (structure & (1 << 8)) { - newmode = drm_display_mode_from_vic_index(connector, video_db, - video_len, - video_index); - if (newmode) { - newmode->flags |= DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; - drm_mode_probed_add(connector, newmode); - modes++; - } - } - - return modes; -} - -/* - * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block - * @connector: connector corresponding to the HDMI sink - * @db: start of the CEA vendor specific block - * @len: length of the CEA block payload, ie. one can access up to db[len] - * - * Parses the HDMI VSDB looking for modes to add to @connector. This function - * also adds the stereo 3d modes when applicable. - */ -static int -do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, - const u8 *video_db, u8 video_len) -{ - int modes = 0, offset = 0, i, multi_present = 0, multi_len; - u8 vic_len, hdmi_3d_len = 0; - u16 mask; - u16 structure_all; - - if (len < 8) - goto out; - - /* no HDMI_Video_Present */ - if (!(db[8] & (1 << 5))) - goto out; - - /* Latency_Fields_Present */ - if (db[8] & (1 << 7)) - offset += 2; - - /* I_Latency_Fields_Present */ - if (db[8] & (1 << 6)) - offset += 2; - - /* the declared length is not long enough for the 2 first bytes - * of additional video format capabilities */ - if (len < (8 + offset + 2)) - goto out; - - /* 3D_Present */ - offset++; - if (db[8 + offset] & (1 << 7)) { - modes += add_hdmi_mandatory_stereo_modes(connector); - - /* 3D_Multi_present */ - multi_present = (db[8 + offset] & 0x60) >> 5; - } - - offset++; - vic_len = db[8 + offset] >> 5; - hdmi_3d_len = db[8 + offset] & 0x1f; - - for (i = 0; i < vic_len && len >= (9 + offset + i); i++) { - u8 vic; - - vic = db[9 + offset + i]; - modes += add_hdmi_mode(connector, vic); - } - offset += 1 + vic_len; - - if (multi_present == 1) - multi_len = 2; - else if (multi_present == 2) - multi_len = 4; - else - multi_len = 0; - - if (len < (8 + offset + hdmi_3d_len - 1)) - goto out; - - if (hdmi_3d_len < multi_len) - goto out; - - if (multi_present == 1 || multi_present == 2) { - /* 3D_Structure_ALL */ - structure_all = (db[8 + offset] << 8) | db[9 + offset]; - - /* check if 3D_MASK is present */ - if (multi_present == 2) - mask = (db[10 + offset] << 8) | db[11 + offset]; - else - mask = 0xffff; - - for (i = 0; i < 16; i++) { - if (mask & (1 << i)) - modes += add_3d_struct_modes(connector, - structure_all, - video_db, - video_len, i); - } - } - - offset += multi_len; - - for (i = 0; i < (hdmi_3d_len - multi_len); i++) { - int vic_index; - struct drm_display_mode *newmode = NULL; - unsigned int newflag = 0; - bool detail_present; - - detail_present = ((db[8 + offset + i] & 0x0f) > 7); - - if (detail_present && (i + 1 == hdmi_3d_len - multi_len)) - break; - - /* 2D_VIC_order_X */ - vic_index = db[8 + offset + i] >> 4; - - /* 3D_Structure_X */ - switch (db[8 + offset + i] & 0x0f) { - case 0: - newflag = DRM_MODE_FLAG_3D_FRAME_PACKING; - break; - case 6: - newflag = DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; - break; - case 8: - /* 3D_Detail_X */ - if ((db[9 + offset + i] >> 4) == 1) - newflag = DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; - break; - } - - if (newflag != 0) { - newmode = drm_display_mode_from_vic_index(connector, - video_db, - video_len, - vic_index); - - if (newmode) { - newmode->flags |= newflag; - drm_mode_probed_add(connector, newmode); - modes++; - } - } - - if (detail_present) - i++; - } - -out: - return modes; -} - -static int -cea_db_payload_len(const u8 *db) -{ - return db[0] & 0x1f; -} - -static int -cea_db_tag(const u8 *db) -{ - return db[0] >> 5; -} - -static int -cea_revision(const u8 *cea) -{ - return cea[1]; -} - -static int -cea_db_offsets(const u8 *cea, int *start, int *end) -{ - /* Data block offset in CEA extension block */ - *start = 4; - *end = cea[2]; - if (*end == 0) - *end = 127; - if (*end < 4 || *end > 127) - return -ERANGE; - return 0; -} - -static bool cea_db_is_hdmi_vsdb(const u8 *db) -{ - int hdmi_id; - - if (cea_db_tag(db) != VENDOR_BLOCK) - return false; - - if (cea_db_payload_len(db) < 5) - return false; - - hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); - - return hdmi_id == HDMI_IEEE_OUI; -} - -#define for_each_cea_db(cea, i, start, end) \ - for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) - -static int -add_cea_modes(struct drm_connector *connector, struct edid *edid) -{ - const u8 *cea = drm_find_cea_extension(edid); - const u8 *db, *hdmi = NULL, *video = NULL; - u8 dbl, hdmi_len, video_len = 0; - int modes = 0; - - if (cea && cea_revision(cea) >= 3) { - int i, start, end; - - if (cea_db_offsets(cea, &start, &end)) - return 0; - - for_each_cea_db(cea, i, start, end) { - db = &cea[i]; - dbl = cea_db_payload_len(db); - - if (cea_db_tag(db) == VIDEO_BLOCK) { - video = db + 1; - video_len = dbl; - modes += do_cea_modes(connector, video, dbl); - } - else if (cea_db_is_hdmi_vsdb(db)) { - hdmi = db; - hdmi_len = dbl; - } - } - } - - /* - * We parse the HDMI VSDB after having added the cea modes as we will - * be patching their flags when the sink supports stereo 3D. - */ - if (hdmi) - modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video, - video_len); - - return modes; -} - -static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode) -{ - const struct drm_display_mode *cea_mode; - int clock1, clock2, clock; - u8 mode_idx; - const char *type; - - mode_idx = drm_match_cea_mode(mode) - 1; - if (mode_idx < ARRAY_SIZE(edid_cea_modes)) { - type = "CEA"; - cea_mode = &edid_cea_modes[mode_idx]; - clock1 = cea_mode->clock; - clock2 = cea_mode_alternate_clock(cea_mode); - } else { - mode_idx = drm_match_hdmi_mode(mode) - 1; - if (mode_idx < ARRAY_SIZE(edid_4k_modes)) { - type = "HDMI"; - cea_mode = &edid_4k_modes[mode_idx]; - clock1 = cea_mode->clock; - clock2 = hdmi_mode_alternate_clock(cea_mode); - } else { - return; - } - } - - /* pick whichever is closest */ - if (abs(mode->clock - clock1) < abs(mode->clock - clock2)) - clock = clock1; - else - clock = clock2; - - if (mode->clock == clock) - return; - - DRM_DEBUG("detailed mode matches %s VIC %d, adjusting clock %d -> %d\n", - type, mode_idx + 1, mode->clock, clock); - mode->clock = clock; -} - -static void -parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db) -{ - u8 len = cea_db_payload_len(db); - - if (len >= 6) { - connector->eld[5] |= (db[6] >> 7) << 1; /* Supports_AI */ - connector->dvi_dual = db[6] & 1; - } - if (len >= 7) - connector->max_tmds_clock = db[7] * 5; - if (len >= 8) { - connector->latency_present[0] = db[8] >> 7; - connector->latency_present[1] = (db[8] >> 6) & 1; - } - if (len >= 9) - connector->video_latency[0] = db[9]; - if (len >= 10) - connector->audio_latency[0] = db[10]; - if (len >= 11) - connector->video_latency[1] = db[11]; - if (len >= 12) - connector->audio_latency[1] = db[12]; - - DRM_DEBUG_KMS("HDMI: DVI dual %d, " - "max TMDS clock %d, " - "latency present %d %d, " - "video latency %d %d, " - "audio latency %d %d\n", - connector->dvi_dual, - connector->max_tmds_clock, - (int) connector->latency_present[0], - (int) connector->latency_present[1], - connector->video_latency[0], - connector->video_latency[1], - connector->audio_latency[0], - connector->audio_latency[1]); -} - -static void -monitor_name(struct detailed_timing *t, void *data) -{ - if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME) - *(u8 **)data = t->data.other_data.data.str.str; -} - -/** - * drm_edid_to_eld - build ELD from EDID - * @connector: connector corresponding to the HDMI/DP sink - * @edid: EDID to parse - * - * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The - * HDCP and Port_ID ELD fields are left for the graphics driver to fill in. - */ -void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) -{ - uint8_t *eld = connector->eld; - u8 *cea; - u8 *name; - u8 *db; - int sad_count = 0; - int mnl; - int dbl; - - memset(eld, 0, sizeof(connector->eld)); - - cea = drm_find_cea_extension(edid); - if (!cea) { - DRM_DEBUG_KMS("ELD: no CEA Extension found\n"); - return; - } - - name = NULL; - drm_for_each_detailed_block((u8 *)edid, monitor_name, &name); - for (mnl = 0; name && mnl < 13; mnl++) { - if (name[mnl] == 0x0a) - break; - eld[20 + mnl] = name[mnl]; - } - eld[4] = (cea[1] << 5) | mnl; - DRM_DEBUG_KMS("ELD monitor %s\n", eld + 20); - - eld[0] = 2 << 3; /* ELD version: 2 */ - - eld[16] = edid->mfg_id[0]; - eld[17] = edid->mfg_id[1]; - eld[18] = edid->prod_code[0]; - eld[19] = edid->prod_code[1]; - - if (cea_revision(cea) >= 3) { - int i, start, end; - - if (cea_db_offsets(cea, &start, &end)) { - start = 0; - end = 0; - } - - for_each_cea_db(cea, i, start, end) { - db = &cea[i]; - dbl = cea_db_payload_len(db); - - switch (cea_db_tag(db)) { - case AUDIO_BLOCK: - /* Audio Data Block, contains SADs */ - sad_count = dbl / 3; - if (dbl >= 1) - memcpy(eld + 20 + mnl, &db[1], dbl); - break; - case SPEAKER_BLOCK: - /* Speaker Allocation Data Block */ - if (dbl >= 1) - eld[7] = db[1]; - break; - case VENDOR_BLOCK: - /* HDMI Vendor-Specific Data Block */ - if (cea_db_is_hdmi_vsdb(db)) - parse_hdmi_vsdb(connector, db); - break; - default: - break; - } - } - } - eld[5] |= sad_count << 4; - - if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort || - connector->connector_type == DRM_MODE_CONNECTOR_eDP) - eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_DP; - else - eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_HDMI; - - eld[DRM_ELD_BASELINE_ELD_LEN] = - DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4); - - DRM_DEBUG_KMS("ELD size %d, SAD count %d\n", - drm_eld_size(eld), sad_count); -} -EXPORT_SYMBOL(drm_edid_to_eld); - -/** - * drm_edid_to_sad - extracts SADs from EDID - * @edid: EDID to parse - * @sads: pointer that will be set to the extracted SADs - * - * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it. - * - * Note: The returned pointer needs to be freed using kfree(). - * - * Return: The number of found SADs or negative number on error. - */ -int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads) -{ - int count = 0; - int i, start, end, dbl; - u8 *cea; - - cea = drm_find_cea_extension(edid); - if (!cea) { - DRM_DEBUG_KMS("SAD: no CEA Extension found\n"); - return -ENOENT; - } - - if (cea_revision(cea) < 3) { - DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); - return -ENOTSUPP; - } - - if (cea_db_offsets(cea, &start, &end)) { - DRM_DEBUG_KMS("SAD: invalid data block offsets\n"); - return -EPROTO; - } - - for_each_cea_db(cea, i, start, end) { - u8 *db = &cea[i]; - - if (cea_db_tag(db) == AUDIO_BLOCK) { - int j; - dbl = cea_db_payload_len(db); - - count = dbl / 3; /* SAD is 3B */ - *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL); - if (!*sads) - return -ENOMEM; - for (j = 0; j < count; j++) { - u8 *sad = &db[1 + j * 3]; - - (*sads)[j].format = (sad[0] & 0x78) >> 3; - (*sads)[j].channels = sad[0] & 0x7; - (*sads)[j].freq = sad[1] & 0x7F; - (*sads)[j].byte2 = sad[2]; - } - break; - } - } - - return count; -} -EXPORT_SYMBOL(drm_edid_to_sad); - -/** - * drm_edid_to_speaker_allocation - extracts Speaker Allocation Data Blocks from EDID - * @edid: EDID to parse - * @sadb: pointer to the speaker block - * - * Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it. - * - * Note: The returned pointer needs to be freed using kfree(). - * - * Return: The number of found Speaker Allocation Blocks or negative number on - * error. - */ -int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb) -{ - int count = 0; - int i, start, end, dbl; - const u8 *cea; - - cea = drm_find_cea_extension(edid); - if (!cea) { - DRM_DEBUG_KMS("SAD: no CEA Extension found\n"); - return -ENOENT; - } - - if (cea_revision(cea) < 3) { - DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); - return -ENOTSUPP; - } - - if (cea_db_offsets(cea, &start, &end)) { - DRM_DEBUG_KMS("SAD: invalid data block offsets\n"); - return -EPROTO; - } - - for_each_cea_db(cea, i, start, end) { - const u8 *db = &cea[i]; - - if (cea_db_tag(db) == SPEAKER_BLOCK) { - dbl = cea_db_payload_len(db); - - /* Speaker Allocation Data Block */ - if (dbl == 3) { - *sadb = kmemdup(&db[1], dbl, GFP_KERNEL); - if (!*sadb) - return -ENOMEM; - count = dbl; - break; - } - } - } - - return count; -} -EXPORT_SYMBOL(drm_edid_to_speaker_allocation); - -/** - * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay - * @connector: connector associated with the HDMI/DP sink - * @mode: the display mode - * - * Return: The HDMI/DP sink's audio-video sync delay in milliseconds or 0 if - * the sink doesn't support audio or video. - */ -int drm_av_sync_delay(struct drm_connector *connector, - const struct drm_display_mode *mode) -{ - int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); - int a, v; - - if (!connector->latency_present[0]) - return 0; - if (!connector->latency_present[1]) - i = 0; - - a = connector->audio_latency[i]; - v = connector->video_latency[i]; - - /* - * HDMI/DP sink doesn't support audio or video? - */ - if (a == 255 || v == 255) - return 0; - - /* - * Convert raw EDID values to millisecond. - * Treat unknown latency as 0ms. - */ - if (a) - a = min(2 * (a - 1), 500); - if (v) - v = min(2 * (v - 1), 500); - - return max(v - a, 0); -} -EXPORT_SYMBOL(drm_av_sync_delay); - -/** - * drm_select_eld - select one ELD from multiple HDMI/DP sinks - * @encoder: the encoder just changed display mode - * - * It's possible for one encoder to be associated with multiple HDMI/DP sinks. - * The policy is now hard coded to simply use the first HDMI/DP sink's ELD. - * - * Return: The connector associated with the first HDMI/DP sink that has ELD - * attached to it. - */ -struct drm_connector *drm_select_eld(struct drm_encoder *encoder) -{ - struct drm_connector *connector; - struct drm_device *dev = encoder->dev; - - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); - WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); - - drm_for_each_connector(connector, dev) - if (connector->encoder == encoder && connector->eld[0]) - return connector; - - return NULL; -} -EXPORT_SYMBOL(drm_select_eld); - -/** - * drm_detect_hdmi_monitor - detect whether monitor is HDMI - * @edid: monitor EDID information - * - * Parse the CEA extension according to CEA-861-B. - * - * Return: True if the monitor is HDMI, false if not or unknown. - */ -bool drm_detect_hdmi_monitor(struct edid *edid) -{ - u8 *edid_ext; - int i; - int start_offset, end_offset; - - edid_ext = drm_find_cea_extension(edid); - if (!edid_ext) - return false; - - if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) - return false; - - /* - * Because HDMI identifier is in Vendor Specific Block, - * search it from all data blocks of CEA extension. - */ - for_each_cea_db(edid_ext, i, start_offset, end_offset) { - if (cea_db_is_hdmi_vsdb(&edid_ext[i])) - return true; - } - - return false; -} -EXPORT_SYMBOL(drm_detect_hdmi_monitor); - -/** - * drm_detect_monitor_audio - check monitor audio capability - * @edid: EDID block to scan - * - * Monitor should have CEA extension block. - * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic - * audio' only. If there is any audio extension block and supported - * audio format, assume at least 'basic audio' support, even if 'basic - * audio' is not defined in EDID. - * - * Return: True if the monitor supports audio, false otherwise. - */ -bool drm_detect_monitor_audio(struct edid *edid) -{ - u8 *edid_ext; - int i, j; - bool has_audio = false; - int start_offset, end_offset; - - edid_ext = drm_find_cea_extension(edid); - if (!edid_ext) - goto end; - - has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0); - - if (has_audio) { - DRM_DEBUG_KMS("Monitor has basic audio support\n"); - goto end; - } - - if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) - goto end; - - for_each_cea_db(edid_ext, i, start_offset, end_offset) { - if (cea_db_tag(&edid_ext[i]) == AUDIO_BLOCK) { - has_audio = true; - for (j = 1; j < cea_db_payload_len(&edid_ext[i]) + 1; j += 3) - DRM_DEBUG_KMS("CEA audio format %d\n", - (edid_ext[i + j] >> 3) & 0xf); - goto end; - } - } -end: - return has_audio; -} -EXPORT_SYMBOL(drm_detect_monitor_audio); - -/** - * drm_rgb_quant_range_selectable - is RGB quantization range selectable? - * @edid: EDID block to scan - * - * Check whether the monitor reports the RGB quantization range selection - * as supported. The AVI infoframe can then be used to inform the monitor - * which quantization range (full or limited) is used. - * - * Return: True if the RGB quantization range is selectable, false otherwise. - */ -bool drm_rgb_quant_range_selectable(struct edid *edid) -{ - u8 *edid_ext; - int i, start, end; - - edid_ext = drm_find_cea_extension(edid); - if (!edid_ext) - return false; - - if (cea_db_offsets(edid_ext, &start, &end)) - return false; - - for_each_cea_db(edid_ext, i, start, end) { - if (cea_db_tag(&edid_ext[i]) == VIDEO_CAPABILITY_BLOCK && - cea_db_payload_len(&edid_ext[i]) == 2) { - DRM_DEBUG_KMS("CEA VCDB 0x%02x\n", edid_ext[i + 2]); - return edid_ext[i + 2] & EDID_CEA_VCDB_QS; - } - } - - return false; -} -EXPORT_SYMBOL(drm_rgb_quant_range_selectable); - -/** - * drm_assign_hdmi_deep_color_info - detect whether monitor supports - * hdmi deep color modes and update drm_display_info if so. - * @edid: monitor EDID information - * @info: Updated with maximum supported deep color bpc and color format - * if deep color supported. - * @connector: DRM connector, used only for debug output - * - * Parse the CEA extension according to CEA-861-B. - * Return true if HDMI deep color supported, false if not or unknown. - */ -static bool drm_assign_hdmi_deep_color_info(struct edid *edid, - struct drm_display_info *info, - struct drm_connector *connector) -{ - u8 *edid_ext, *hdmi; - int i; - int start_offset, end_offset; - unsigned int dc_bpc = 0; - - edid_ext = drm_find_cea_extension(edid); - if (!edid_ext) - return false; - - if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) - return false; - - /* - * Because HDMI identifier is in Vendor Specific Block, - * search it from all data blocks of CEA extension. - */ - for_each_cea_db(edid_ext, i, start_offset, end_offset) { - if (cea_db_is_hdmi_vsdb(&edid_ext[i])) { - /* HDMI supports at least 8 bpc */ - info->bpc = 8; - - hdmi = &edid_ext[i]; - if (cea_db_payload_len(hdmi) < 6) - return false; - - if (hdmi[6] & DRM_EDID_HDMI_DC_30) { - dc_bpc = 10; - info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_30; - DRM_DEBUG("%s: HDMI sink does deep color 30.\n", - connector->name); - } - - if (hdmi[6] & DRM_EDID_HDMI_DC_36) { - dc_bpc = 12; - info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_36; - DRM_DEBUG("%s: HDMI sink does deep color 36.\n", - connector->name); - } - - if (hdmi[6] & DRM_EDID_HDMI_DC_48) { - dc_bpc = 16; - info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_48; - DRM_DEBUG("%s: HDMI sink does deep color 48.\n", - connector->name); - } - - if (dc_bpc > 0) { - DRM_DEBUG("%s: Assigning HDMI sink color depth as %d bpc.\n", - connector->name, dc_bpc); - info->bpc = dc_bpc; - - /* - * Deep color support mandates RGB444 support for all video - * modes and forbids YCRCB422 support for all video modes per - * HDMI 1.3 spec. - */ - info->color_formats = DRM_COLOR_FORMAT_RGB444; - - /* YCRCB444 is optional according to spec. */ - if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) { - info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; - DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n", - connector->name); - } - - /* - * Spec says that if any deep color mode is supported at all, - * then deep color 36 bit must be supported. - */ - if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) { - DRM_DEBUG("%s: HDMI sink should do DC_36, but does not!\n", - connector->name); - } - - return true; - } - else { - DRM_DEBUG("%s: No deep color support on this HDMI sink.\n", - connector->name); - } - } - } - - return false; -} - -/** - * drm_add_display_info - pull display info out if present - * @edid: EDID data - * @info: display info (attached to connector) - * @connector: connector whose edid is used to build display info - * - * Grab any available display info and stuff it into the drm_display_info - * structure that's part of the connector. Useful for tracking bpp and - * color spaces. - */ -static void drm_add_display_info(struct edid *edid, - struct drm_display_info *info, - struct drm_connector *connector) -{ - u8 *edid_ext; - - info->width_mm = edid->width_cm * 10; - info->height_mm = edid->height_cm * 10; - - /* driver figures it out in this case */ - info->bpc = 0; - info->color_formats = 0; - - if (edid->revision < 3) - return; - - if (!(edid->input & DRM_EDID_INPUT_DIGITAL)) - return; - - /* Get data from CEA blocks if present */ - edid_ext = drm_find_cea_extension(edid); - if (edid_ext) { - info->cea_rev = edid_ext[1]; - - /* The existence of a CEA block should imply RGB support */ - info->color_formats = DRM_COLOR_FORMAT_RGB444; - if (edid_ext[3] & EDID_CEA_YCRCB444) - info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; - if (edid_ext[3] & EDID_CEA_YCRCB422) - info->color_formats |= DRM_COLOR_FORMAT_YCRCB422; - } - - /* HDMI deep color modes supported? Assign to info, if so */ - drm_assign_hdmi_deep_color_info(edid, info, connector); - - /* Only defined for 1.4 with digital displays */ - if (edid->revision < 4) - return; - - switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) { - case DRM_EDID_DIGITAL_DEPTH_6: - info->bpc = 6; - break; - case DRM_EDID_DIGITAL_DEPTH_8: - info->bpc = 8; - break; - case DRM_EDID_DIGITAL_DEPTH_10: - info->bpc = 10; - break; - case DRM_EDID_DIGITAL_DEPTH_12: - info->bpc = 12; - break; - case DRM_EDID_DIGITAL_DEPTH_14: - info->bpc = 14; - break; - case DRM_EDID_DIGITAL_DEPTH_16: - info->bpc = 16; - break; - case DRM_EDID_DIGITAL_DEPTH_UNDEF: - default: - info->bpc = 0; - break; - } - - DRM_DEBUG("%s: Assigning EDID-1.4 digital sink color depth as %d bpc.\n", - connector->name, info->bpc); - - info->color_formats |= DRM_COLOR_FORMAT_RGB444; - if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444) - info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; - if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422) - info->color_formats |= DRM_COLOR_FORMAT_YCRCB422; -} - -/** - * drm_add_edid_modes - add modes from EDID data, if available - * @connector: connector we're probing - * @edid: EDID data - * - * Add the specified modes to the connector's mode list. - * - * Return: The number of modes added or 0 if we couldn't find any. - */ -int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) -{ - int num_modes = 0; - u32 quirks; - - if (edid == NULL) { - return 0; - } - if (!drm_edid_is_valid(edid)) { - dev_warn(connector->dev->dev, "%s: EDID invalid.\n", - connector->name); - return 0; - } - - quirks = edid_get_quirks(edid); - - /* - * EDID spec says modes should be preferred in this order: - * - preferred detailed mode - * - other detailed modes from base block - * - detailed modes from extension blocks - * - CVT 3-byte code modes - * - standard timing codes - * - established timing codes - * - modes inferred from GTF or CVT range information - * - * We get this pretty much right. - * - * XXX order for additional mode types in extension blocks? - */ - num_modes += add_detailed_modes(connector, edid, quirks); - num_modes += add_cvt_modes(connector, edid); - num_modes += add_standard_modes(connector, edid); - num_modes += add_established_modes(connector, edid); - num_modes += add_cea_modes(connector, edid); - num_modes += add_alternate_cea_modes(connector, edid); - if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) - num_modes += add_inferred_modes(connector, edid); - - if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) - edid_fixup_preferred(connector, quirks); - - drm_add_display_info(edid, &connector->display_info, connector); - - if (quirks & EDID_QUIRK_FORCE_6BPC) - connector->display_info.bpc = 6; - - if (quirks & EDID_QUIRK_FORCE_8BPC) - connector->display_info.bpc = 8; - - if (quirks & EDID_QUIRK_FORCE_10BPC) - connector->display_info.bpc = 10; - - if (quirks & EDID_QUIRK_FORCE_12BPC) - connector->display_info.bpc = 12; - - return num_modes; -} -EXPORT_SYMBOL(drm_add_edid_modes); - -/** - * drm_add_modes_noedid - add modes for the connectors without EDID - * @connector: connector we're probing - * @hdisplay: the horizontal display limit - * @vdisplay: the vertical display limit - * - * Add the specified modes to the connector's mode list. Only when the - * hdisplay/vdisplay is not beyond the given limit, it will be added. - * - * Return: The number of modes added or 0 if we couldn't find any. - */ -int drm_add_modes_noedid(struct drm_connector *connector, - int hdisplay, int vdisplay) -{ - int i, count, num_modes = 0; - struct drm_display_mode *mode; - struct drm_device *dev = connector->dev; - - count = ARRAY_SIZE(drm_dmt_modes); - if (hdisplay < 0) - hdisplay = 0; - if (vdisplay < 0) - vdisplay = 0; - - for (i = 0; i < count; i++) { - const struct drm_display_mode *ptr = &drm_dmt_modes[i]; - if (hdisplay && vdisplay) { - /* - * Only when two are valid, they will be used to check - * whether the mode should be added to the mode list of - * the connector. - */ - if (ptr->hdisplay > hdisplay || - ptr->vdisplay > vdisplay) - continue; - } - if (drm_mode_vrefresh(ptr) > 61) - continue; - mode = drm_mode_duplicate(dev, ptr); - if (mode) { - drm_mode_probed_add(connector, mode); - num_modes++; - } - } - return num_modes; -} -EXPORT_SYMBOL(drm_add_modes_noedid); - -/** - * drm_set_preferred_mode - Sets the preferred mode of a connector - * @connector: connector whose mode list should be processed - * @hpref: horizontal resolution of preferred mode - * @vpref: vertical resolution of preferred mode - * - * Marks a mode as preferred if it matches the resolution specified by @hpref - * and @vpref. - */ -void drm_set_preferred_mode(struct drm_connector *connector, - int hpref, int vpref) -{ - struct drm_display_mode *mode; - - list_for_each_entry(mode, &connector->probed_modes, head) { - if (mode->hdisplay == hpref && - mode->vdisplay == vpref) - mode->type |= DRM_MODE_TYPE_PREFERRED; - } -} -EXPORT_SYMBOL(drm_set_preferred_mode); - -/** - * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with - * data from a DRM display mode - * @frame: HDMI AVI infoframe - * @mode: DRM display mode - * - * Return: 0 on success or a negative error code on failure. - */ -int -drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, - const struct drm_display_mode *mode) -{ - int err; - - if (!frame || !mode) - return -EINVAL; - - err = hdmi_avi_infoframe_init(frame); - if (err < 0) - return err; - - if (mode->flags & DRM_MODE_FLAG_DBLCLK) - frame->pixel_repeat = 1; - - frame->video_code = drm_match_cea_mode(mode); - - frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; - - /* - * Populate picture aspect ratio from either - * user input (if specified) or from the CEA mode list. - */ - if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 || - mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9) - frame->picture_aspect = mode->picture_aspect_ratio; - else if (frame->video_code > 0) - frame->picture_aspect = drm_get_cea_aspect_ratio( - frame->video_code); - - frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; - frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN; - - return 0; -} -EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode); - -static enum hdmi_3d_structure -s3d_structure_from_display_mode(const struct drm_display_mode *mode) -{ - u32 layout = mode->flags & DRM_MODE_FLAG_3D_MASK; - - switch (layout) { - case DRM_MODE_FLAG_3D_FRAME_PACKING: - return HDMI_3D_STRUCTURE_FRAME_PACKING; - case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE: - return HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE; - case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE: - return HDMI_3D_STRUCTURE_LINE_ALTERNATIVE; - case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL: - return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL; - case DRM_MODE_FLAG_3D_L_DEPTH: - return HDMI_3D_STRUCTURE_L_DEPTH; - case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH: - return HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH; - case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM: - return HDMI_3D_STRUCTURE_TOP_AND_BOTTOM; - case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF: - return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF; - default: - return HDMI_3D_STRUCTURE_INVALID; - } -} - -/** - * drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with - * data from a DRM display mode - * @frame: HDMI vendor infoframe - * @mode: DRM display mode - * - * Note that there's is a need to send HDMI vendor infoframes only when using a - * 4k or stereoscopic 3D mode. So when giving any other mode as input this - * function will return -EINVAL, error that can be safely ignored. - * - * Return: 0 on success or a negative error code on failure. - */ -int -drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, - const struct drm_display_mode *mode) -{ - int err; - u32 s3d_flags; - u8 vic; - - if (!frame || !mode) - return -EINVAL; - - vic = drm_match_hdmi_mode(mode); - s3d_flags = mode->flags & DRM_MODE_FLAG_3D_MASK; - - if (!vic && !s3d_flags) - return -EINVAL; - - if (vic && s3d_flags) - return -EINVAL; - - err = hdmi_vendor_infoframe_init(frame); - if (err < 0) - return err; - - if (vic) - frame->vic = vic; - else - frame->s3d_struct = s3d_structure_from_display_mode(mode); - - return 0; -} -EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode); - -static int drm_parse_display_id(struct drm_connector *connector, - u8 *displayid, int length, - bool is_edid_extension) -{ - /* if this is an EDID extension the first byte will be 0x70 */ - int idx = 0; - struct displayid_hdr *base; - struct displayid_block *block; - u8 csum = 0; - int i; - - if (is_edid_extension) - idx = 1; - - base = (struct displayid_hdr *)&displayid[idx]; - - DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n", - base->rev, base->bytes, base->prod_id, base->ext_count); - - if (base->bytes + 5 > length - idx) - return -EINVAL; - - for (i = idx; i <= base->bytes + 5; i++) { - csum += displayid[i]; - } - if (csum) { - DRM_ERROR("DisplayID checksum invalid, remainder is %d\n", csum); - return -EINVAL; - } - - block = (struct displayid_block *)&displayid[idx + 4]; - DRM_DEBUG_KMS("block id %d, rev %d, len %d\n", - block->tag, block->rev, block->num_bytes); - - switch (block->tag) { - case DATA_BLOCK_TILED_DISPLAY: { - struct displayid_tiled_block *tile = (struct displayid_tiled_block *)block; - - u16 w, h; - u8 tile_v_loc, tile_h_loc; - u8 num_v_tile, num_h_tile; - struct drm_tile_group *tg; - - w = tile->tile_size[0] | tile->tile_size[1] << 8; - h = tile->tile_size[2] | tile->tile_size[3] << 8; - - num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30); - num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 0x30); - tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 4); - tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 0x3) << 4); - - connector->has_tile = true; - if (tile->tile_cap & 0x80) - connector->tile_is_single_monitor = true; - - connector->num_h_tile = num_h_tile + 1; - connector->num_v_tile = num_v_tile + 1; - connector->tile_h_loc = tile_h_loc; - connector->tile_v_loc = tile_v_loc; - connector->tile_h_size = w + 1; - connector->tile_v_size = h + 1; - - DRM_DEBUG_KMS("tile cap 0x%x\n", tile->tile_cap); - DRM_DEBUG_KMS("tile_size %d x %d\n", w + 1, h + 1); - DRM_DEBUG_KMS("topo num tiles %dx%d, location %dx%d\n", - num_h_tile + 1, num_v_tile + 1, tile_h_loc, tile_v_loc); - DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0], tile->topology_id[1], tile->topology_id[2]); - - tg = drm_mode_get_tile_group(connector->dev, tile->topology_id); - if (!tg) { - tg = drm_mode_create_tile_group(connector->dev, tile->topology_id); - } - if (!tg) - return -ENOMEM; - - if (connector->tile_group != tg) { - /* if we haven't got a pointer, - take the reference, drop ref to old tile group */ - if (connector->tile_group) { - drm_mode_put_tile_group(connector->dev, connector->tile_group); - } - connector->tile_group = tg; - } else - /* if same tile group, then release the ref we just took. */ - drm_mode_put_tile_group(connector->dev, tg); - } - break; - default: - printk("unknown displayid tag %d\n", block->tag); - break; - } - return 0; -} - -static void drm_get_displayid(struct drm_connector *connector, - struct edid *edid) -{ - void *displayid = NULL; - int ret; - connector->has_tile = false; - displayid = drm_find_displayid_extension(edid); - if (!displayid) { - /* drop reference to any tile group we had */ - goto out_drop_ref; - } - - ret = drm_parse_display_id(connector, displayid, EDID_LENGTH, true); - if (ret < 0) - goto out_drop_ref; - if (!connector->has_tile) - goto out_drop_ref; - return; -out_drop_ref: - if (connector->tile_group) { - drm_mode_put_tile_group(connector->dev, connector->tile_group); - connector->tile_group = NULL; - } - return; -} diff --git a/src/4.x/drivers/gpu/drm/drm_edid_load.c b/src/4.x/drivers/gpu/drm/drm_edid_load.c deleted file mode 100644 index 698b8c3b0..000000000 --- a/src/4.x/drivers/gpu/drm/drm_edid_load.c +++ /dev/null @@ -1,322 +0,0 @@ -/* - drm_edid_load.c: use a built-in EDID data set or load it via the firmware - interface - - Copyright (C) 2012 Carsten Emde - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include -#include -#include -#include -#include -#include - -static char edid_firmware[PATH_MAX]; -module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644); -MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob " - "from built-in data or /lib/firmware instead. "); - -#define GENERIC_EDIDS 6 -static const char * const generic_edid_name[GENERIC_EDIDS] = { - "edid/800x600.bin", - "edid/1024x768.bin", - "edid/1280x1024.bin", - "edid/1600x1200.bin", - "edid/1680x1050.bin", - "edid/1920x1080.bin", -}; - -static const u8 generic_edid[GENERIC_EDIDS][128] = { - { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x16, 0x01, 0x03, 0x6d, 0x1b, 0x14, 0x78, - 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, - 0x20, 0x50, 0x54, 0x01, 0x00, 0x00, 0x45, 0x40, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xa0, 0x0f, - 0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80, - 0x14, 0x00, 0x15, 0xd0, 0x10, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, - 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, - 0x3d, 0x24, 0x26, 0x05, 0x00, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, - 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53, - 0x56, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xc2, - }, - { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78, - 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, - 0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19, - 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90, - 0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18, - 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, - 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, - 0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, - 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58, - 0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55, - }, - { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2c, 0x23, 0x78, - 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, - 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a, - 0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70, - 0x13, 0x00, 0xbc, 0x63, 0x11, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, - 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, - 0x3d, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, - 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53, - 0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0, - }, - { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x16, 0x01, 0x03, 0x6d, 0x37, 0x29, 0x78, - 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, - 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xa9, 0x40, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x48, 0x3f, - 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0, - 0x13, 0x00, 0x2b, 0xa0, 0x21, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, - 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, - 0x3d, 0x4a, 0x4c, 0x11, 0x00, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, - 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x55, - 0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0x9d, - }, - { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78, - 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, - 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39, - 0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0, - 0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, - 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, - 0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, - 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57, - 0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26, - }, - { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78, - 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, - 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, - 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, - 0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, - 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, - 0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, - 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46, - 0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05, - }, -}; - -static int edid_size(const u8 *edid, int data_size) -{ - if (data_size < EDID_LENGTH) - return 0; - - return (edid[0x7e] + 1) * EDID_LENGTH; -} - -static void *edid_load(struct drm_connector *connector, const char *name, - const char *connector_name) -{ - const struct firmware *fw = NULL; - const u8 *fwdata; - u8 *edid; - int fwsize, builtin; - int i, valid_extensions = 0; - bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); - - builtin = 0; - for (i = 0; i < GENERIC_EDIDS; i++) { - if (strcmp(name, generic_edid_name[i]) == 0) { - fwdata = generic_edid[i]; - fwsize = sizeof(generic_edid[i]); - builtin = 1; - break; - } - } - if (!builtin) { - struct platform_device *pdev; - int err; - - pdev = platform_device_register_simple(connector_name, -1, NULL, 0); - if (IS_ERR(pdev)) { - DRM_ERROR("Failed to register EDID firmware platform device " - "for connector \"%s\"\n", connector_name); - return ERR_CAST(pdev); - } - - err = request_firmware(&fw, name, &pdev->dev); - platform_device_unregister(pdev); - if (err) { - DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n", - name, err); - return ERR_PTR(err); - } - - fwdata = fw->data; - fwsize = fw->size; - } - - if (edid_size(fwdata, fwsize) != fwsize) { - DRM_ERROR("Size of EDID firmware \"%s\" is invalid " - "(expected %d, got %d\n", name, - edid_size(fwdata, fwsize), (int)fwsize); - edid = ERR_PTR(-EINVAL); - goto out; - } - - edid = kmemdup(fwdata, fwsize, GFP_KERNEL); - if (edid == NULL) { - edid = ERR_PTR(-ENOMEM); - goto out; - } - - if (!drm_edid_block_valid(edid, 0, print_bad_edid, - &connector->edid_corrupt)) { - connector->bad_edid_counter++; - DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ", - name); - kfree(edid); - edid = ERR_PTR(-EINVAL); - goto out; - } - - for (i = 1; i <= edid[0x7e]; i++) { - if (i != valid_extensions + 1) - memcpy(edid + (valid_extensions + 1) * EDID_LENGTH, - edid + i * EDID_LENGTH, EDID_LENGTH); - if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, - print_bad_edid, - NULL)) - valid_extensions++; - } - - if (valid_extensions != edid[0x7e]) { - u8 *new_edid; - - edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions; - DRM_INFO("Found %d valid extensions instead of %d in EDID data " - "\"%s\" for connector \"%s\"\n", valid_extensions, - edid[0x7e], name, connector_name); - edid[0x7e] = valid_extensions; - - new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH, - GFP_KERNEL); - if (new_edid) - edid = new_edid; - } - - DRM_INFO("Got %s EDID base block and %d extension%s from " - "\"%s\" for connector \"%s\"\n", builtin ? "built-in" : - "external", valid_extensions, valid_extensions == 1 ? "" : "s", - name, connector_name); - -out: - release_firmware(fw); - return edid; -} - -int drm_load_edid_firmware(struct drm_connector *connector) -{ - const char *connector_name = connector->name; - char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL; - int ret; - struct edid *edid; - - if (edid_firmware[0] == '\0') - return 0; - - /* - * If there are multiple edid files specified and separated - * by commas, search through the list looking for one that - * matches the connector. - * - * If there's one or more that don't't specify a connector, keep - * the last one found one as a fallback. - */ - fwstr = kstrdup(edid_firmware, GFP_KERNEL); - edidstr = fwstr; - - while ((edidname = strsep(&edidstr, ","))) { - colon = strchr(edidname, ':'); - if (colon != NULL) { - if (strncmp(connector_name, edidname, colon - edidname)) - continue; - edidname = colon + 1; - break; - } - - if (*edidname != '\0') /* corner case: multiple ',' */ - fallback = edidname; - } - - if (!edidname) { - if (!fallback) { - kfree(fwstr); - return 0; - } - edidname = fallback; - } - - last = edidname + strlen(edidname) - 1; - if (*last == '\n') - *last = '\0'; - - edid = edid_load(connector, edidname, connector_name); - kfree(fwstr); - - if (IS_ERR_OR_NULL(edid)) - return 0; - - drm_mode_connector_update_edid_property(connector, edid); - ret = drm_add_edid_modes(connector, edid); - drm_edid_to_eld(connector, edid); - kfree(edid); - - return ret; -} diff --git a/src/4.x/drivers/gpu/drm/drm_encoder_slave.c b/src/4.x/drivers/gpu/drm/drm_encoder_slave.c deleted file mode 100644 index d18b88b75..000000000 --- a/src/4.x/drivers/gpu/drm/drm_encoder_slave.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2009 Francisco Jerez. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include - -#include - -/** - * drm_i2c_encoder_init - Initialize an I2C slave encoder - * @dev: DRM device. - * @encoder: Encoder to be attached to the I2C device. You aren't - * required to have called drm_encoder_init() before. - * @adap: I2C adapter that will be used to communicate with - * the device. - * @info: Information that will be used to create the I2C device. - * Required fields are @addr and @type. - * - * Create an I2C device on the specified bus (the module containing its - * driver is transparently loaded) and attach it to the specified - * &drm_encoder_slave. The @slave_funcs field will be initialized with - * the hooks provided by the slave driver. - * - * If @info->platform_data is non-NULL it will be used as the initial - * slave config. - * - * Returns 0 on success or a negative errno on failure, in particular, - * -ENODEV is returned when no matching driver is found. - */ -int drm_i2c_encoder_init(struct drm_device *dev, - struct drm_encoder_slave *encoder, - struct i2c_adapter *adap, - const struct i2c_board_info *info) -{ - struct module *module = NULL; - struct i2c_client *client; - struct drm_i2c_encoder_driver *encoder_drv; - int err = 0; - - request_module("%s%s", I2C_MODULE_PREFIX, info->type); - - client = i2c_new_device(adap, info); - if (!client) { - err = -ENOMEM; - goto fail; - } - - if (!client->dev.driver) { - err = -ENODEV; - goto fail_unregister; - } - - module = client->dev.driver->owner; - if (!try_module_get(module)) { - err = -ENODEV; - goto fail_unregister; - } - - encoder->bus_priv = client; - - encoder_drv = to_drm_i2c_encoder_driver(to_i2c_driver(client->dev.driver)); - - err = encoder_drv->encoder_init(client, dev, encoder); - if (err) - goto fail_unregister; - - if (info->platform_data) - encoder->slave_funcs->set_config(&encoder->base, - info->platform_data); - - return 0; - -fail_unregister: - i2c_unregister_device(client); - module_put(module); -fail: - return err; -} -EXPORT_SYMBOL(drm_i2c_encoder_init); - -/** - * drm_i2c_encoder_destroy - Unregister the I2C device backing an encoder - * @drm_encoder: Encoder to be unregistered. - * - * This should be called from the @destroy method of an I2C slave - * encoder driver once I2C access is no longer needed. - */ -void drm_i2c_encoder_destroy(struct drm_encoder *drm_encoder) -{ - struct drm_encoder_slave *encoder = to_encoder_slave(drm_encoder); - struct i2c_client *client = drm_i2c_encoder_get_client(drm_encoder); - struct module *module = client->dev.driver->owner; - - i2c_unregister_device(client); - encoder->bus_priv = NULL; - - module_put(module); -} -EXPORT_SYMBOL(drm_i2c_encoder_destroy); - -/* - * Wrapper fxns which can be plugged in to drm_encoder_helper_funcs: - */ - -static inline struct drm_encoder_slave_funcs * -get_slave_funcs(struct drm_encoder *enc) -{ - return to_encoder_slave(enc)->slave_funcs; -} - -void drm_i2c_encoder_dpms(struct drm_encoder *encoder, int mode) -{ - get_slave_funcs(encoder)->dpms(encoder, mode); -} -EXPORT_SYMBOL(drm_i2c_encoder_dpms); - -bool drm_i2c_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return get_slave_funcs(encoder)->mode_fixup(encoder, mode, adjusted_mode); -} -EXPORT_SYMBOL(drm_i2c_encoder_mode_fixup); - -void drm_i2c_encoder_prepare(struct drm_encoder *encoder) -{ - drm_i2c_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); -} -EXPORT_SYMBOL(drm_i2c_encoder_prepare); - -void drm_i2c_encoder_commit(struct drm_encoder *encoder) -{ - drm_i2c_encoder_dpms(encoder, DRM_MODE_DPMS_ON); -} -EXPORT_SYMBOL(drm_i2c_encoder_commit); - -void drm_i2c_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode); -} -EXPORT_SYMBOL(drm_i2c_encoder_mode_set); - -enum drm_connector_status drm_i2c_encoder_detect(struct drm_encoder *encoder, - struct drm_connector *connector) -{ - return get_slave_funcs(encoder)->detect(encoder, connector); -} -EXPORT_SYMBOL(drm_i2c_encoder_detect); - -void drm_i2c_encoder_save(struct drm_encoder *encoder) -{ - get_slave_funcs(encoder)->save(encoder); -} -EXPORT_SYMBOL(drm_i2c_encoder_save); - -void drm_i2c_encoder_restore(struct drm_encoder *encoder) -{ - get_slave_funcs(encoder)->restore(encoder); -} -EXPORT_SYMBOL(drm_i2c_encoder_restore); diff --git a/src/4.x/drivers/gpu/drm/drm_fb_cma_helper.c b/src/4.x/drivers/gpu/drm/drm_fb_cma_helper.c deleted file mode 100644 index c19a62561..000000000 --- a/src/4.x/drivers/gpu/drm/drm_fb_cma_helper.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * drm kms/fb cma (contiguous memory allocator) helper functions - * - * Copyright (C) 2012 Analog Device Inc. - * Author: Lars-Peter Clausen - * - * Based on udl_fbdev.c - * Copyright (C) 2012 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include - -struct drm_fb_cma { - struct drm_framebuffer fb; - struct drm_gem_cma_object *obj[4]; -}; - -struct drm_fbdev_cma { - struct drm_fb_helper fb_helper; - struct drm_fb_cma *fb; -}; - -static inline struct drm_fbdev_cma *to_fbdev_cma(struct drm_fb_helper *helper) -{ - return container_of(helper, struct drm_fbdev_cma, fb_helper); -} - -static inline struct drm_fb_cma *to_fb_cma(struct drm_framebuffer *fb) -{ - return container_of(fb, struct drm_fb_cma, fb); -} - -static void drm_fb_cma_destroy(struct drm_framebuffer *fb) -{ - struct drm_fb_cma *fb_cma = to_fb_cma(fb); - int i; - - for (i = 0; i < 4; i++) { - if (fb_cma->obj[i]) - drm_gem_object_unreference_unlocked(&fb_cma->obj[i]->base); - } - - drm_framebuffer_cleanup(fb); - kfree(fb_cma); -} - -static int drm_fb_cma_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, unsigned int *handle) -{ - struct drm_fb_cma *fb_cma = to_fb_cma(fb); - - return drm_gem_handle_create(file_priv, - &fb_cma->obj[0]->base, handle); -} - -static struct drm_framebuffer_funcs drm_fb_cma_funcs = { - .destroy = drm_fb_cma_destroy, - .create_handle = drm_fb_cma_create_handle, -}; - -static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev, - struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_cma_object **obj, - unsigned int num_planes) -{ - struct drm_fb_cma *fb_cma; - int ret; - int i; - - fb_cma = kzalloc(sizeof(*fb_cma), GFP_KERNEL); - if (!fb_cma) - return ERR_PTR(-ENOMEM); - - drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd); - - for (i = 0; i < num_planes; i++) - fb_cma->obj[i] = obj[i]; - - ret = drm_framebuffer_init(dev, &fb_cma->fb, &drm_fb_cma_funcs); - if (ret) { - dev_err(dev->dev, "Failed to initialize framebuffer: %d\n", ret); - kfree(fb_cma); - return ERR_PTR(ret); - } - - return fb_cma; -} - -/** - * drm_fb_cma_create() - (struct drm_mode_config_funcs *)->fb_create callback function - * - * If your hardware has special alignment or pitch requirements these should be - * checked before calling this function. - */ -struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev, - struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) -{ - struct drm_fb_cma *fb_cma; - struct drm_gem_cma_object *objs[4]; - struct drm_gem_object *obj; - unsigned int hsub; - unsigned int vsub; - int ret; - int i; - - hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); - vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); - - for (i = 0; i < drm_format_num_planes(mode_cmd->pixel_format); i++) { - unsigned int width = mode_cmd->width / (i ? hsub : 1); - unsigned int height = mode_cmd->height / (i ? vsub : 1); - unsigned int min_size; - - obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[i]); - if (!obj) { - dev_err(dev->dev, "Failed to lookup GEM object\n"); - ret = -ENXIO; - goto err_gem_object_unreference; - } - - min_size = (height - 1) * mode_cmd->pitches[i] - + width * drm_format_plane_cpp(mode_cmd->pixel_format, i) - + mode_cmd->offsets[i]; - - if (obj->size < min_size) { - drm_gem_object_unreference_unlocked(obj); - ret = -EINVAL; - goto err_gem_object_unreference; - } - objs[i] = to_drm_gem_cma_obj(obj); - } - - fb_cma = drm_fb_cma_alloc(dev, mode_cmd, objs, i); - if (IS_ERR(fb_cma)) { - ret = PTR_ERR(fb_cma); - goto err_gem_object_unreference; - } - - return &fb_cma->fb; - -err_gem_object_unreference: - for (i--; i >= 0; i--) - drm_gem_object_unreference_unlocked(&objs[i]->base); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(drm_fb_cma_create); - -/** - * drm_fb_cma_get_gem_obj() - Get CMA GEM object for framebuffer - * @fb: The framebuffer - * @plane: Which plane - * - * Return the CMA GEM object for given framebuffer. - * - * This function will usually be called from the CRTC callback functions. - */ -struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, - unsigned int plane) -{ - struct drm_fb_cma *fb_cma = to_fb_cma(fb); - - if (plane >= 4) - return NULL; - - return fb_cma->obj[plane]; -} -EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj); - -#ifdef CONFIG_DEBUG_FS -/* - * drm_fb_cma_describe() - Helper to dump information about a single - * CMA framebuffer object - */ -static void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m) -{ - struct drm_fb_cma *fb_cma = to_fb_cma(fb); - int i, n = drm_format_num_planes(fb->pixel_format); - - seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height, - (char *)&fb->pixel_format); - - for (i = 0; i < n; i++) { - seq_printf(m, " %d: offset=%d pitch=%d, obj: ", - i, fb->offsets[i], fb->pitches[i]); - drm_gem_cma_describe(fb_cma->obj[i], m); - } -} - -/** - * drm_fb_cma_debugfs_show() - Helper to list CMA framebuffer objects - * in debugfs. - */ -int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_framebuffer *fb; - - mutex_lock(&dev->mode_config.fb_lock); - drm_for_each_fb(fb, dev) - drm_fb_cma_describe(fb, m); - mutex_unlock(&dev->mode_config.fb_lock); - - return 0; -} -EXPORT_SYMBOL_GPL(drm_fb_cma_debugfs_show); -#endif - -static struct fb_ops drm_fbdev_cma_ops = { - .owner = THIS_MODULE, - .fb_fillrect = drm_fb_helper_sys_fillrect, - .fb_copyarea = drm_fb_helper_sys_copyarea, - .fb_imageblit = drm_fb_helper_sys_imageblit, - .fb_check_var = drm_fb_helper_check_var, - .fb_set_par = drm_fb_helper_set_par, - .fb_blank = drm_fb_helper_blank, - .fb_pan_display = drm_fb_helper_pan_display, - .fb_setcmap = drm_fb_helper_setcmap, -}; - -static int drm_fbdev_cma_create(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct drm_fbdev_cma *fbdev_cma = to_fbdev_cma(helper); - struct drm_mode_fb_cmd2 mode_cmd = { 0 }; - struct drm_device *dev = helper->dev; - struct drm_gem_cma_object *obj; - struct drm_framebuffer *fb; - unsigned int bytes_per_pixel; - unsigned long offset; - struct fb_info *fbi; - size_t size; - int ret; - - DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", - sizes->surface_width, sizes->surface_height, - sizes->surface_bpp); - - bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); - - mode_cmd.width = sizes->surface_width; - mode_cmd.height = sizes->surface_height; - mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel; - mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, - sizes->surface_depth); - - size = mode_cmd.pitches[0] * mode_cmd.height; - obj = drm_gem_cma_create(dev, size); - if (IS_ERR(obj)) - return -ENOMEM; - - fbi = drm_fb_helper_alloc_fbi(helper); - if (IS_ERR(fbi)) { - ret = PTR_ERR(fbi); - goto err_drm_gem_cma_free_object; - } - - fbdev_cma->fb = drm_fb_cma_alloc(dev, &mode_cmd, &obj, 1); - if (IS_ERR(fbdev_cma->fb)) { - dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n"); - ret = PTR_ERR(fbdev_cma->fb); - goto err_fb_info_destroy; - } - - fb = &fbdev_cma->fb->fb; - helper->fb = fb; - - fbi->par = helper; - fbi->flags = FBINFO_FLAG_DEFAULT; - fbi->fbops = &drm_fbdev_cma_ops; - - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); - drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); - - offset = fbi->var.xoffset * bytes_per_pixel; - offset += fbi->var.yoffset * fb->pitches[0]; - - dev->mode_config.fb_base = (resource_size_t)obj->paddr; - fbi->screen_base = obj->vaddr + offset; - fbi->fix.smem_start = (unsigned long)(obj->paddr + offset); - fbi->screen_size = size; - fbi->fix.smem_len = size; - - return 0; - -err_fb_info_destroy: - drm_fb_helper_release_fbi(helper); -err_drm_gem_cma_free_object: - drm_gem_cma_free_object(&obj->base); - return ret; -} - -static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { - .fb_probe = drm_fbdev_cma_create, -}; - -/** - * drm_fbdev_cma_init() - Allocate and initializes a drm_fbdev_cma struct - * @dev: DRM device - * @preferred_bpp: Preferred bits per pixel for the device - * @num_crtc: Number of CRTCs - * @max_conn_count: Maximum number of connectors - * - * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR. - */ -struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int num_crtc, - unsigned int max_conn_count) -{ - struct drm_fbdev_cma *fbdev_cma; - struct drm_fb_helper *helper; - int ret; - - fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL); - if (!fbdev_cma) { - dev_err(dev->dev, "Failed to allocate drm fbdev.\n"); - return ERR_PTR(-ENOMEM); - } - - helper = &fbdev_cma->fb_helper; - - drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs); - - ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count); - if (ret < 0) { - dev_err(dev->dev, "Failed to initialize drm fb helper.\n"); - goto err_free; - } - - ret = drm_fb_helper_single_add_all_connectors(helper); - if (ret < 0) { - dev_err(dev->dev, "Failed to add connectors.\n"); - goto err_drm_fb_helper_fini; - - } - - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(dev); - - ret = drm_fb_helper_initial_config(helper, preferred_bpp); - if (ret < 0) { - dev_err(dev->dev, "Failed to set initial hw configuration.\n"); - goto err_drm_fb_helper_fini; - } - - return fbdev_cma; - -err_drm_fb_helper_fini: - drm_fb_helper_fini(helper); -err_free: - kfree(fbdev_cma); - - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(drm_fbdev_cma_init); - -/** - * drm_fbdev_cma_fini() - Free drm_fbdev_cma struct - * @fbdev_cma: The drm_fbdev_cma struct - */ -void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma) -{ - drm_fb_helper_unregister_fbi(&fbdev_cma->fb_helper); - drm_fb_helper_release_fbi(&fbdev_cma->fb_helper); - - if (fbdev_cma->fb) { - drm_framebuffer_unregister_private(&fbdev_cma->fb->fb); - drm_fb_cma_destroy(&fbdev_cma->fb->fb); - } - - drm_fb_helper_fini(&fbdev_cma->fb_helper); - kfree(fbdev_cma); -} -EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); - -/** - * drm_fbdev_cma_restore_mode() - Restores initial framebuffer mode - * @fbdev_cma: The drm_fbdev_cma struct, may be NULL - * - * This function is usually called from the DRM drivers lastclose callback. - */ -void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) -{ - if (fbdev_cma) - drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev_cma->fb_helper); -} -EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode); - -/** - * drm_fbdev_cma_hotplug_event() - Poll for hotpulug events - * @fbdev_cma: The drm_fbdev_cma struct, may be NULL - * - * This function is usually called from the DRM drivers output_poll_changed - * callback. - */ -void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma) -{ - if (fbdev_cma) - drm_fb_helper_hotplug_event(&fbdev_cma->fb_helper); -} -EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event); diff --git a/src/4.x/drivers/gpu/drm/drm_fb_helper.c b/src/4.x/drivers/gpu/drm/drm_fb_helper.c deleted file mode 100644 index e449f22c8..000000000 --- a/src/4.x/drivers/gpu/drm/drm_fb_helper.c +++ /dev/null @@ -1,2198 +0,0 @@ -/* - * Copyright (c) 2006-2009 Red Hat Inc. - * Copyright (c) 2006-2008 Intel Corporation - * Copyright (c) 2007 Dave Airlie - * - * DRM framebuffer helper functions - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that copyright - * notice and this permission notice appear in supporting documentation, and - * that the name of the copyright holders not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. The copyright holders make no representations - * about the suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - * - * Authors: - * Dave Airlie - * Jesse Barnes - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static bool drm_fbdev_emulation = true; -module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); -MODULE_PARM_DESC(fbdev_emulation, - "Enable legacy fbdev emulation [default=true]"); - -static LIST_HEAD(kernel_fb_helper_list); - -/** - * DOC: fbdev helpers - * - * The fb helper functions are useful to provide an fbdev on top of a drm kernel - * mode setting driver. They can be used mostly independently from the crtc - * helper functions used by many drivers to implement the kernel mode setting - * interfaces. - * - * Initialization is done as a four-step process with drm_fb_helper_prepare(), - * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and - * drm_fb_helper_initial_config(). Drivers with fancier requirements than the - * default behaviour can override the third step with their own code. - * Teardown is done with drm_fb_helper_fini(). - * - * At runtime drivers should restore the fbdev console by calling - * drm_fb_helper_restore_fbdev_mode_unlocked() from their ->lastclose callback. - * They should also notify the fb helper code from updates to the output - * configuration by calling drm_fb_helper_hotplug_event(). For easier - * integration with the output polling code in drm_crtc_helper.c the modeset - * code provides a ->output_poll_changed callback. - * - * All other functions exported by the fb helper library can be used to - * implement the fbdev driver interface by the driver. - * - * It is possible, though perhaps somewhat tricky, to implement race-free - * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare() - * helper must be called first to initialize the minimum required to make - * hotplug detection work. Drivers also need to make sure to properly set up - * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init() - * it is safe to enable interrupts and start processing hotplug events. At the - * same time, drivers should initialize all modeset objects such as CRTCs, - * encoders and connectors. To finish up the fbdev helper initialization, the - * drm_fb_helper_init() function is called. To probe for all attached displays - * and set up an initial configuration using the detected hardware, drivers - * should call drm_fb_helper_single_add_all_connectors() followed by - * drm_fb_helper_initial_config(). - */ - -/** - * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev - * emulation helper - * @fb_helper: fbdev initialized with drm_fb_helper_init - * - * This functions adds all the available connectors for use with the given - * fb_helper. This is a separate step to allow drivers to freely assign - * connectors to the fbdev, e.g. if some are reserved for special purposes or - * not adequate to be used for the fbcon. - * - * This function is protected against concurrent connector hotadds/removals - * using drm_fb_helper_add_one_connector() and - * drm_fb_helper_remove_one_connector(). - */ -int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev = fb_helper->dev; - struct drm_connector *connector; - int i; - - if (!drm_fbdev_emulation) - return 0; - - mutex_lock(&dev->mode_config.mutex); - drm_for_each_connector(connector, dev) { - struct drm_fb_helper_connector *fb_helper_connector; - - fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL); - if (!fb_helper_connector) - goto fail; - - fb_helper_connector->connector = connector; - fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; - } - mutex_unlock(&dev->mode_config.mutex); - return 0; -fail: - for (i = 0; i < fb_helper->connector_count; i++) { - kfree(fb_helper->connector_info[i]); - fb_helper->connector_info[i] = NULL; - } - fb_helper->connector_count = 0; - mutex_unlock(&dev->mode_config.mutex); - - return -ENOMEM; -} -EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); - -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector) -{ - struct drm_fb_helper_connector **temp; - struct drm_fb_helper_connector *fb_helper_connector; - - if (!drm_fbdev_emulation) - return 0; - - WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); - if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) { - temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), GFP_KERNEL); - if (!temp) - return -ENOMEM; - - fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1; - fb_helper->connector_info = temp; - } - - - fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL); - if (!fb_helper_connector) - return -ENOMEM; - - fb_helper_connector->connector = connector; - fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; - return 0; -} -EXPORT_SYMBOL(drm_fb_helper_add_one_connector); - -static void remove_from_modeset(struct drm_mode_set *set, - struct drm_connector *connector) -{ - int i, j; - - for (i = 0; i < set->num_connectors; i++) { - if (set->connectors[i] == connector) - break; - } - - if (i == set->num_connectors) - return; - - for (j = i + 1; j < set->num_connectors; j++) { - set->connectors[j - 1] = set->connectors[j]; - } - set->num_connectors--; - - /* - * TODO maybe need to makes sure we set it back to !=NULL somewhere? - */ - if (set->num_connectors == 0) { - set->fb = NULL; - drm_mode_destroy(connector->dev, set->mode); - set->mode = NULL; - } -} - -int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - struct drm_fb_helper_connector *fb_helper_connector; - int i, j; - - if (!drm_fbdev_emulation) - return 0; - - WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); - - for (i = 0; i < fb_helper->connector_count; i++) { - if (fb_helper->connector_info[i]->connector == connector) - break; - } - - if (i == fb_helper->connector_count) - return -EINVAL; - fb_helper_connector = fb_helper->connector_info[i]; - - for (j = i + 1; j < fb_helper->connector_count; j++) { - fb_helper->connector_info[j - 1] = fb_helper->connector_info[j]; - } - fb_helper->connector_count--; - kfree(fb_helper_connector); - - /* also cleanup dangling references to the connector: */ - for (i = 0; i < fb_helper->crtc_count; i++) - remove_from_modeset(&fb_helper->crtc_info[i].mode_set, connector); - - return 0; -} -EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); - -static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper) -{ - uint16_t *r_base, *g_base, *b_base; - int i; - - if (helper->funcs->gamma_get == NULL) - return; - - r_base = crtc->gamma_store; - g_base = r_base + crtc->gamma_size; - b_base = g_base + crtc->gamma_size; - - for (i = 0; i < crtc->gamma_size; i++) - helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i); -} - -static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) -{ - uint16_t *r_base, *g_base, *b_base; - - if (crtc->funcs->gamma_set == NULL) - return; - - r_base = crtc->gamma_store; - g_base = r_base + crtc->gamma_size; - b_base = g_base + crtc->gamma_size; - - crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); -} - -/** - * drm_fb_helper_debug_enter - implementation for ->fb_debug_enter - * @info: fbdev registered by the helper - */ -int drm_fb_helper_debug_enter(struct fb_info *info) -{ - struct drm_fb_helper *helper = info->par; - const struct drm_crtc_helper_funcs *funcs; - int i; - - list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { - for (i = 0; i < helper->crtc_count; i++) { - struct drm_mode_set *mode_set = - &helper->crtc_info[i].mode_set; - - if (!mode_set->crtc->enabled) - continue; - - funcs = mode_set->crtc->helper_private; - drm_fb_helper_save_lut_atomic(mode_set->crtc, helper); - funcs->mode_set_base_atomic(mode_set->crtc, - mode_set->fb, - mode_set->x, - mode_set->y, - ENTER_ATOMIC_MODE_SET); - } - } - - return 0; -} -EXPORT_SYMBOL(drm_fb_helper_debug_enter); - -/* Find the real fb for a given fb helper CRTC */ -static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_crtc *c; - - drm_for_each_crtc(c, dev) { - if (crtc->base.id == c->base.id) - return c->primary->fb; - } - - return NULL; -} - -/** - * drm_fb_helper_debug_leave - implementation for ->fb_debug_leave - * @info: fbdev registered by the helper - */ -int drm_fb_helper_debug_leave(struct fb_info *info) -{ - struct drm_fb_helper *helper = info->par; - struct drm_crtc *crtc; - const struct drm_crtc_helper_funcs *funcs; - struct drm_framebuffer *fb; - int i; - - for (i = 0; i < helper->crtc_count; i++) { - struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; - crtc = mode_set->crtc; - funcs = crtc->helper_private; - fb = drm_mode_config_fb(crtc); - - if (!crtc->enabled) - continue; - - if (!fb) { - DRM_ERROR("no fb to restore??\n"); - continue; - } - - drm_fb_helper_restore_lut_atomic(mode_set->crtc); - funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, - crtc->y, LEAVE_ATOMIC_MODE_SET); - } - - return 0; -} -EXPORT_SYMBOL(drm_fb_helper_debug_leave); - -static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev = fb_helper->dev; - struct drm_plane *plane; - struct drm_atomic_state *state; - int i, ret; - unsigned plane_mask; - - state = drm_atomic_state_alloc(dev); - if (!state) - return -ENOMEM; - - state->acquire_ctx = dev->mode_config.acquire_ctx; -retry: - plane_mask = 0; - drm_for_each_plane(plane, dev) { - struct drm_plane_state *plane_state; - - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - goto fail; - } - - plane_state->rotation = BIT(DRM_ROTATE_0); - - plane->old_fb = plane->fb; - plane_mask |= 1 << drm_plane_index(plane); - - /* disable non-primary: */ - if (plane->type == DRM_PLANE_TYPE_PRIMARY) - continue; - - ret = __drm_atomic_helper_disable_plane(plane, plane_state); - if (ret != 0) - goto fail; - } - - for(i = 0; i < fb_helper->crtc_count; i++) { - struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; - - ret = __drm_atomic_helper_set_config(mode_set, state); - if (ret != 0) - goto fail; - } - - ret = drm_atomic_commit(state); - -fail: - drm_atomic_clean_old_fb(dev, plane_mask, ret); - - if (ret == -EDEADLK) - goto backoff; - - if (ret != 0) - drm_atomic_state_free(state); - - return ret; - -backoff: - drm_atomic_state_clear(state); - drm_atomic_legacy_backoff(state); - - goto retry; -} - -static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev = fb_helper->dev; - struct drm_plane *plane; - int i; - - drm_warn_on_modeset_not_all_locked(dev); - - if (fb_helper->atomic) - return restore_fbdev_mode_atomic(fb_helper); - - drm_for_each_plane(plane, dev) { - if (plane->type != DRM_PLANE_TYPE_PRIMARY) - drm_plane_force_disable(plane); - - if (dev->mode_config.rotation_property) { - drm_mode_plane_set_obj_prop(plane, - dev->mode_config.rotation_property, - BIT(DRM_ROTATE_0)); - } - } - - for (i = 0; i < fb_helper->crtc_count; i++) { - struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; - struct drm_crtc *crtc = mode_set->crtc; - int ret; - - if (crtc->funcs->cursor_set2) { - ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0); - if (ret) - return ret; - } else if (crtc->funcs->cursor_set) { - ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0); - if (ret) - return ret; - } - - ret = drm_mode_set_config_internal(mode_set); - if (ret) - return ret; - } - - return 0; -} - -/** - * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration - * @fb_helper: fbcon to restore - * - * This should be called from driver's drm ->lastclose callback - * when implementing an fbcon on top of kms using this helper. This ensures that - * the user isn't greeted with a black screen when e.g. X dies. - * - * RETURNS: - * Zero if everything went ok, negative error code otherwise. - */ -int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev = fb_helper->dev; - bool do_delayed; - int ret; - - if (!drm_fbdev_emulation) - return -ENODEV; - - drm_modeset_lock_all(dev); - ret = restore_fbdev_mode(fb_helper); - - do_delayed = fb_helper->delayed_hotplug; - if (do_delayed) - fb_helper->delayed_hotplug = false; - drm_modeset_unlock_all(dev); - - if (do_delayed) - drm_fb_helper_hotplug_event(fb_helper); - return ret; -} -EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked); - -static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev = fb_helper->dev; - struct drm_crtc *crtc; - int bound = 0, crtcs_bound = 0; - - /* Sometimes user space wants everything disabled, so don't steal the - * display if there's a master. */ - if (dev->primary->master) - return false; - - drm_for_each_crtc(crtc, dev) { - if (crtc->primary->fb) - crtcs_bound++; - if (crtc->primary->fb == fb_helper->fb) - bound++; - } - - if (bound < crtcs_bound) - return false; - - return true; -} - -#ifdef CONFIG_MAGIC_SYSRQ -/* - * restore fbcon display for all kms driver's using this helper, used for sysrq - * and panic handling. - */ -static bool drm_fb_helper_force_kernel_mode(void) -{ - bool ret, error = false; - struct drm_fb_helper *helper; - - if (list_empty(&kernel_fb_helper_list)) - return false; - - list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { - struct drm_device *dev = helper->dev; - - if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) - continue; - - drm_modeset_lock_all(dev); - ret = restore_fbdev_mode(helper); - if (ret) - error = true; - drm_modeset_unlock_all(dev); - } - return error; -} - -static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) -{ - bool ret; - ret = drm_fb_helper_force_kernel_mode(); - if (ret == true) - DRM_ERROR("Failed to restore crtc configuration\n"); -} -static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); - -static void drm_fb_helper_sysrq(int dummy1) -{ - schedule_work(&drm_fb_helper_restore_work); -} - -static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { - .handler = drm_fb_helper_sysrq, - .help_msg = "force-fb(V)", - .action_msg = "Restore framebuffer console", -}; -#else -static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; -#endif - -static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) -{ - struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; - struct drm_crtc *crtc; - struct drm_connector *connector; - int i, j; - - /* - * For each CRTC in this fb, turn the connectors on/off. - */ - drm_modeset_lock_all(dev); - if (!drm_fb_helper_is_bound(fb_helper)) { - drm_modeset_unlock_all(dev); - return; - } - - for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; - - if (!crtc->enabled) - continue; - - /* Walk the connectors & encoders on this fb turning them on/off */ - for (j = 0; j < fb_helper->connector_count; j++) { - connector = fb_helper->connector_info[j]->connector; - connector->funcs->dpms(connector, dpms_mode); - drm_object_property_set_value(&connector->base, - dev->mode_config.dpms_property, dpms_mode); - } - } - drm_modeset_unlock_all(dev); -} - -/** - * drm_fb_helper_blank - implementation for ->fb_blank - * @blank: desired blanking state - * @info: fbdev registered by the helper - */ -int drm_fb_helper_blank(int blank, struct fb_info *info) -{ - if (oops_in_progress) - return -EBUSY; - - switch (blank) { - /* Display: On; HSync: On, VSync: On */ - case FB_BLANK_UNBLANK: - drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON); - break; - /* Display: Off; HSync: On, VSync: On */ - case FB_BLANK_NORMAL: - drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY); - break; - /* Display: Off; HSync: Off, VSync: On */ - case FB_BLANK_HSYNC_SUSPEND: - drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY); - break; - /* Display: Off; HSync: On, VSync: Off */ - case FB_BLANK_VSYNC_SUSPEND: - drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND); - break; - /* Display: Off; HSync: Off, VSync: Off */ - case FB_BLANK_POWERDOWN: - drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF); - break; - } - return 0; -} -EXPORT_SYMBOL(drm_fb_helper_blank); - -static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) -{ - int i; - - for (i = 0; i < helper->connector_count; i++) - kfree(helper->connector_info[i]); - kfree(helper->connector_info); - for (i = 0; i < helper->crtc_count; i++) { - kfree(helper->crtc_info[i].mode_set.connectors); - if (helper->crtc_info[i].mode_set.mode) - drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode); - } - kfree(helper->crtc_info); -} - -/** - * drm_fb_helper_prepare - setup a drm_fb_helper structure - * @dev: DRM device - * @helper: driver-allocated fbdev helper structure to set up - * @funcs: pointer to structure of functions associate with this helper - * - * Sets up the bare minimum to make the framebuffer helper usable. This is - * useful to implement race-free initialization of the polling helpers. - */ -void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, - const struct drm_fb_helper_funcs *funcs) -{ - INIT_LIST_HEAD(&helper->kernel_fb_list); - helper->funcs = funcs; - helper->dev = dev; -} -EXPORT_SYMBOL(drm_fb_helper_prepare); - -/** - * drm_fb_helper_init - initialize a drm_fb_helper structure - * @dev: drm device - * @fb_helper: driver-allocated fbdev helper structure to initialize - * @crtc_count: maximum number of crtcs to support in this fbdev emulation - * @max_conn_count: max connector count - * - * This allocates the structures for the fbdev helper with the given limits. - * Note that this won't yet touch the hardware (through the driver interfaces) - * nor register the fbdev. This is only done in drm_fb_helper_initial_config() - * to allow driver writes more control over the exact init sequence. - * - * Drivers must call drm_fb_helper_prepare() before calling this function. - * - * RETURNS: - * Zero if everything went ok, nonzero otherwise. - */ -int drm_fb_helper_init(struct drm_device *dev, - struct drm_fb_helper *fb_helper, - int crtc_count, int max_conn_count) -{ - struct drm_crtc *crtc; - int i; - - if (!drm_fbdev_emulation) - return 0; - - if (!max_conn_count) - return -EINVAL; - - fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); - if (!fb_helper->crtc_info) - return -ENOMEM; - - fb_helper->crtc_count = crtc_count; - fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); - if (!fb_helper->connector_info) { - kfree(fb_helper->crtc_info); - return -ENOMEM; - } - fb_helper->connector_info_alloc_count = dev->mode_config.num_connector; - fb_helper->connector_count = 0; - - for (i = 0; i < crtc_count; i++) { - fb_helper->crtc_info[i].mode_set.connectors = - kcalloc(max_conn_count, - sizeof(struct drm_connector *), - GFP_KERNEL); - - if (!fb_helper->crtc_info[i].mode_set.connectors) - goto out_free; - fb_helper->crtc_info[i].mode_set.num_connectors = 0; - } - - i = 0; - drm_for_each_crtc(crtc, dev) { - fb_helper->crtc_info[i].mode_set.crtc = crtc; - i++; - } - - fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC); - - return 0; -out_free: - drm_fb_helper_crtc_free(fb_helper); - return -ENOMEM; -} -EXPORT_SYMBOL(drm_fb_helper_init); - -/** - * drm_fb_helper_alloc_fbi - allocate fb_info and some of its members - * @fb_helper: driver-allocated fbdev helper - * - * A helper to alloc fb_info and the members cmap and apertures. Called - * by the driver within the fb_probe fb_helper callback function. - * - * RETURNS: - * fb_info pointer if things went okay, pointer containing error code - * otherwise - */ -struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper) -{ - struct device *dev = fb_helper->dev->dev; - struct fb_info *info; - int ret; - - info = framebuffer_alloc(0, dev); - if (!info) - return ERR_PTR(-ENOMEM); - - ret = fb_alloc_cmap(&info->cmap, 256, 0); - if (ret) - goto err_release; - - info->apertures = alloc_apertures(1); - if (!info->apertures) { - ret = -ENOMEM; - goto err_free_cmap; - } - - fb_helper->fbdev = info; - - return info; - -err_free_cmap: - fb_dealloc_cmap(&info->cmap); -err_release: - framebuffer_release(info); - return ERR_PTR(ret); -} -EXPORT_SYMBOL(drm_fb_helper_alloc_fbi); - -/** - * drm_fb_helper_unregister_fbi - unregister fb_info framebuffer device - * @fb_helper: driver-allocated fbdev helper - * - * A wrapper around unregister_framebuffer, to release the fb_info - * framebuffer device - */ -void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper) -{ - if (fb_helper && fb_helper->fbdev) - unregister_framebuffer(fb_helper->fbdev); -} -EXPORT_SYMBOL(drm_fb_helper_unregister_fbi); - -/** - * drm_fb_helper_release_fbi - dealloc fb_info and its members - * @fb_helper: driver-allocated fbdev helper - * - * A helper to free memory taken by fb_info and the members cmap and - * apertures - */ -void drm_fb_helper_release_fbi(struct drm_fb_helper *fb_helper) -{ - if (fb_helper) { - struct fb_info *info = fb_helper->fbdev; - - if (info) { - if (info->cmap.len) - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } - - fb_helper->fbdev = NULL; - } -} -EXPORT_SYMBOL(drm_fb_helper_release_fbi); - -void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) -{ - if (!drm_fbdev_emulation) - return; - - if (!list_empty(&fb_helper->kernel_fb_list)) { - list_del(&fb_helper->kernel_fb_list); - if (list_empty(&kernel_fb_helper_list)) { - unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); - } - } - - drm_fb_helper_crtc_free(fb_helper); - -} -EXPORT_SYMBOL(drm_fb_helper_fini); - -/** - * drm_fb_helper_unlink_fbi - wrapper around unlink_framebuffer - * @fb_helper: driver-allocated fbdev helper - * - * A wrapper around unlink_framebuffer implemented by fbdev core - */ -void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper) -{ - if (fb_helper && fb_helper->fbdev) - unlink_framebuffer(fb_helper->fbdev); -} -EXPORT_SYMBOL(drm_fb_helper_unlink_fbi); - -/** - * drm_fb_helper_sys_read - wrapper around fb_sys_read - * @info: fb_info struct pointer - * @buf: userspace buffer to read from framebuffer memory - * @count: number of bytes to read from framebuffer memory - * @ppos: read offset within framebuffer memory - * - * A wrapper around fb_sys_read implemented by fbdev core - */ -ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf, - size_t count, loff_t *ppos) -{ - return fb_sys_read(info, buf, count, ppos); -} -EXPORT_SYMBOL(drm_fb_helper_sys_read); - -/** - * drm_fb_helper_sys_write - wrapper around fb_sys_write - * @info: fb_info struct pointer - * @buf: userspace buffer to write to framebuffer memory - * @count: number of bytes to write to framebuffer memory - * @ppos: write offset within framebuffer memory - * - * A wrapper around fb_sys_write implemented by fbdev core - */ -ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf, - size_t count, loff_t *ppos) -{ - return fb_sys_write(info, buf, count, ppos); -} -EXPORT_SYMBOL(drm_fb_helper_sys_write); - -/** - * drm_fb_helper_sys_fillrect - wrapper around sys_fillrect - * @info: fbdev registered by the helper - * @rect: info about rectangle to fill - * - * A wrapper around sys_fillrect implemented by fbdev core - */ -void drm_fb_helper_sys_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) -{ - sys_fillrect(info, rect); -} -EXPORT_SYMBOL(drm_fb_helper_sys_fillrect); - -/** - * drm_fb_helper_sys_copyarea - wrapper around sys_copyarea - * @info: fbdev registered by the helper - * @area: info about area to copy - * - * A wrapper around sys_copyarea implemented by fbdev core - */ -void drm_fb_helper_sys_copyarea(struct fb_info *info, - const struct fb_copyarea *area) -{ - sys_copyarea(info, area); -} -EXPORT_SYMBOL(drm_fb_helper_sys_copyarea); - -/** - * drm_fb_helper_sys_imageblit - wrapper around sys_imageblit - * @info: fbdev registered by the helper - * @image: info about image to blit - * - * A wrapper around sys_imageblit implemented by fbdev core - */ -void drm_fb_helper_sys_imageblit(struct fb_info *info, - const struct fb_image *image) -{ - sys_imageblit(info, image); -} -EXPORT_SYMBOL(drm_fb_helper_sys_imageblit); - -/** - * drm_fb_helper_cfb_fillrect - wrapper around cfb_fillrect - * @info: fbdev registered by the helper - * @rect: info about rectangle to fill - * - * A wrapper around cfb_imageblit implemented by fbdev core - */ -void drm_fb_helper_cfb_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) -{ - cfb_fillrect(info, rect); -} -EXPORT_SYMBOL(drm_fb_helper_cfb_fillrect); - -/** - * drm_fb_helper_cfb_copyarea - wrapper around cfb_copyarea - * @info: fbdev registered by the helper - * @area: info about area to copy - * - * A wrapper around cfb_copyarea implemented by fbdev core - */ -void drm_fb_helper_cfb_copyarea(struct fb_info *info, - const struct fb_copyarea *area) -{ - cfb_copyarea(info, area); -} -EXPORT_SYMBOL(drm_fb_helper_cfb_copyarea); - -/** - * drm_fb_helper_cfb_imageblit - wrapper around cfb_imageblit - * @info: fbdev registered by the helper - * @image: info about image to blit - * - * A wrapper around cfb_imageblit implemented by fbdev core - */ -void drm_fb_helper_cfb_imageblit(struct fb_info *info, - const struct fb_image *image) -{ - cfb_imageblit(info, image); -} -EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit); - -/** - * drm_fb_helper_set_suspend - wrapper around fb_set_suspend - * @fb_helper: driver-allocated fbdev helper - * @state: desired state, zero to resume, non-zero to suspend - * - * A wrapper around fb_set_suspend implemented by fbdev core - */ -void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state) -{ - if (fb_helper && fb_helper->fbdev) - fb_set_suspend(fb_helper->fbdev, state); -} -EXPORT_SYMBOL(drm_fb_helper_set_suspend); - -static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, u16 regno, struct fb_info *info) -{ - struct drm_fb_helper *fb_helper = info->par; - struct drm_framebuffer *fb = fb_helper->fb; - int pindex; - - if (info->fix.visual == FB_VISUAL_TRUECOLOR) { - u32 *palette; - u32 value; - /* place color in psuedopalette */ - if (regno > 16) - return -EINVAL; - palette = (u32 *)info->pseudo_palette; - red >>= (16 - info->var.red.length); - green >>= (16 - info->var.green.length); - blue >>= (16 - info->var.blue.length); - value = (red << info->var.red.offset) | - (green << info->var.green.offset) | - (blue << info->var.blue.offset); - if (info->var.transp.length > 0) { - u32 mask = (1 << info->var.transp.length) - 1; - mask <<= info->var.transp.offset; - value |= mask; - } - palette[regno] = value; - return 0; - } - - /* - * The driver really shouldn't advertise pseudo/directcolor - * visuals if it can't deal with the palette. - */ - if (WARN_ON(!fb_helper->funcs->gamma_set || - !fb_helper->funcs->gamma_get)) - return -EINVAL; - - pindex = regno; - - if (fb->bits_per_pixel == 16) { - pindex = regno << 3; - - if (fb->depth == 16 && regno > 63) - return -EINVAL; - if (fb->depth == 15 && regno > 31) - return -EINVAL; - - if (fb->depth == 16) { - u16 r, g, b; - int i; - if (regno < 32) { - for (i = 0; i < 8; i++) - fb_helper->funcs->gamma_set(crtc, red, - green, blue, pindex + i); - } - - fb_helper->funcs->gamma_get(crtc, &r, - &g, &b, - pindex >> 1); - - for (i = 0; i < 4; i++) - fb_helper->funcs->gamma_set(crtc, r, - green, b, - (pindex >> 1) + i); - } - } - - if (fb->depth != 16) - fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex); - return 0; -} - -/** - * drm_fb_helper_setcmap - implementation for ->fb_setcmap - * @cmap: cmap to set - * @info: fbdev registered by the helper - */ -int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) -{ - struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; - const struct drm_crtc_helper_funcs *crtc_funcs; - u16 *red, *green, *blue, *transp; - struct drm_crtc *crtc; - int i, j, rc = 0; - int start; - - if (oops_in_progress) - return -EBUSY; - - drm_modeset_lock_all(dev); - if (!drm_fb_helper_is_bound(fb_helper)) { - drm_modeset_unlock_all(dev); - return -EBUSY; - } - - for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; - crtc_funcs = crtc->helper_private; - - red = cmap->red; - green = cmap->green; - blue = cmap->blue; - transp = cmap->transp; - start = cmap->start; - - for (j = 0; j < cmap->len; j++) { - u16 hred, hgreen, hblue, htransp = 0xffff; - - hred = *red++; - hgreen = *green++; - hblue = *blue++; - - if (transp) - htransp = *transp++; - - rc = setcolreg(crtc, hred, hgreen, hblue, start++, info); - if (rc) - goto out; - } - if (crtc_funcs->load_lut) - crtc_funcs->load_lut(crtc); - } - out: - drm_modeset_unlock_all(dev); - return rc; -} -EXPORT_SYMBOL(drm_fb_helper_setcmap); - -/** - * drm_fb_helper_check_var - implementation for ->fb_check_var - * @var: screeninfo to check - * @info: fbdev registered by the helper - */ -int drm_fb_helper_check_var(struct fb_var_screeninfo *var, - struct fb_info *info) -{ - struct drm_fb_helper *fb_helper = info->par; - struct drm_framebuffer *fb = fb_helper->fb; - int depth; - - if (in_dbg_master()) - return -EINVAL; - - if (var->pixclock != 0) { - DRM_DEBUG("fbdev emulation doesn't support changing the pixel clock, value of pixclock is ignored\n"); - var->pixclock = 0; - } - - /* Need to resize the fb object !!! */ - if (var->bits_per_pixel > fb->bits_per_pixel || - var->xres > fb->width || var->yres > fb->height || - var->xres_virtual > fb->width || var->yres_virtual > fb->height) { - DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb " - "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n", - var->xres, var->yres, var->bits_per_pixel, - var->xres_virtual, var->yres_virtual, - fb->width, fb->height, fb->bits_per_pixel); - return -EINVAL; - } - - switch (var->bits_per_pixel) { - case 16: - depth = (var->green.length == 6) ? 16 : 15; - break; - case 32: - depth = (var->transp.length > 0) ? 32 : 24; - break; - default: - depth = var->bits_per_pixel; - break; - } - - switch (depth) { - case 8: - var->red.offset = 0; - var->green.offset = 0; - var->blue.offset = 0; - var->red.length = 8; - var->green.length = 8; - var->blue.length = 8; - var->transp.length = 0; - var->transp.offset = 0; - break; - case 15: - var->red.offset = 10; - var->green.offset = 5; - var->blue.offset = 0; - var->red.length = 5; - var->green.length = 5; - var->blue.length = 5; - var->transp.length = 1; - var->transp.offset = 15; - break; - case 16: - var->red.offset = 11; - var->green.offset = 5; - var->blue.offset = 0; - var->red.length = 5; - var->green.length = 6; - var->blue.length = 5; - var->transp.length = 0; - var->transp.offset = 0; - break; - case 24: - var->red.offset = 16; - var->green.offset = 8; - var->blue.offset = 0; - var->red.length = 8; - var->green.length = 8; - var->blue.length = 8; - var->transp.length = 0; - var->transp.offset = 0; - break; - case 32: - var->red.offset = 16; - var->green.offset = 8; - var->blue.offset = 0; - var->red.length = 8; - var->green.length = 8; - var->blue.length = 8; - var->transp.length = 8; - var->transp.offset = 24; - break; - default: - return -EINVAL; - } - return 0; -} -EXPORT_SYMBOL(drm_fb_helper_check_var); - -/** - * drm_fb_helper_set_par - implementation for ->fb_set_par - * @info: fbdev registered by the helper - * - * This will let fbcon do the mode init and is called at initialization time by - * the fbdev core when registering the driver, and later on through the hotplug - * callback. - */ -int drm_fb_helper_set_par(struct fb_info *info) -{ - struct drm_fb_helper *fb_helper = info->par; - struct fb_var_screeninfo *var = &info->var; - - if (oops_in_progress) - return -EBUSY; - - if (var->pixclock != 0) { - DRM_ERROR("PIXEL CLOCK SET\n"); - return -EINVAL; - } - - drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper); - - return 0; -} -EXPORT_SYMBOL(drm_fb_helper_set_par); - -static int pan_display_atomic(struct fb_var_screeninfo *var, - struct fb_info *info) -{ - struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; - struct drm_atomic_state *state; - struct drm_plane *plane; - int i, ret; - unsigned plane_mask; - - state = drm_atomic_state_alloc(dev); - if (!state) - return -ENOMEM; - - state->acquire_ctx = dev->mode_config.acquire_ctx; -retry: - plane_mask = 0; - for(i = 0; i < fb_helper->crtc_count; i++) { - struct drm_mode_set *mode_set; - - mode_set = &fb_helper->crtc_info[i].mode_set; - - mode_set->x = var->xoffset; - mode_set->y = var->yoffset; - - ret = __drm_atomic_helper_set_config(mode_set, state); - if (ret != 0) - goto fail; - - plane = mode_set->crtc->primary; - plane_mask |= drm_plane_index(plane); - plane->old_fb = plane->fb; - } - - ret = drm_atomic_commit(state); - if (ret != 0) - goto fail; - - info->var.xoffset = var->xoffset; - info->var.yoffset = var->yoffset; - - -fail: - drm_atomic_clean_old_fb(dev, plane_mask, ret); - - if (ret == -EDEADLK) - goto backoff; - - if (ret != 0) - drm_atomic_state_free(state); - - return ret; - -backoff: - drm_atomic_state_clear(state); - drm_atomic_legacy_backoff(state); - - goto retry; -} - -/** - * drm_fb_helper_pan_display - implementation for ->fb_pan_display - * @var: updated screen information - * @info: fbdev registered by the helper - */ -int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, - struct fb_info *info) -{ - struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; - struct drm_mode_set *modeset; - int ret = 0; - int i; - - if (oops_in_progress) - return -EBUSY; - - drm_modeset_lock_all(dev); - if (!drm_fb_helper_is_bound(fb_helper)) { - drm_modeset_unlock_all(dev); - return -EBUSY; - } - - if (fb_helper->atomic) { - ret = pan_display_atomic(var, info); - goto unlock; - } - - for (i = 0; i < fb_helper->crtc_count; i++) { - modeset = &fb_helper->crtc_info[i].mode_set; - - modeset->x = var->xoffset; - modeset->y = var->yoffset; - - if (modeset->num_connectors) { - ret = drm_mode_set_config_internal(modeset); - if (!ret) { - info->var.xoffset = var->xoffset; - info->var.yoffset = var->yoffset; - } - } - } -unlock: - drm_modeset_unlock_all(dev); - return ret; -} -EXPORT_SYMBOL(drm_fb_helper_pan_display); - -/* - * Allocates the backing storage and sets up the fbdev info structure through - * the ->fb_probe callback and then registers the fbdev and sets up the panic - * notifier. - */ -static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, - int preferred_bpp) -{ - int ret = 0; - int crtc_count = 0; - int i; - struct fb_info *info; - struct drm_fb_helper_surface_size sizes; - int gamma_size = 0; - - memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); - sizes.surface_depth = 24; - sizes.surface_bpp = 32; - sizes.fb_width = (unsigned)-1; - sizes.fb_height = (unsigned)-1; - - /* if driver picks 8 or 16 by default use that - for both depth/bpp */ - if (preferred_bpp != sizes.surface_bpp) - sizes.surface_depth = sizes.surface_bpp = preferred_bpp; - - /* first up get a count of crtcs now in use and new min/maxes width/heights */ - for (i = 0; i < fb_helper->connector_count; i++) { - struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; - struct drm_cmdline_mode *cmdline_mode; - - cmdline_mode = &fb_helper_conn->connector->cmdline_mode; - - if (cmdline_mode->bpp_specified) { - switch (cmdline_mode->bpp) { - case 8: - sizes.surface_depth = sizes.surface_bpp = 8; - break; - case 15: - sizes.surface_depth = 15; - sizes.surface_bpp = 16; - break; - case 16: - sizes.surface_depth = sizes.surface_bpp = 16; - break; - case 24: - sizes.surface_depth = sizes.surface_bpp = 24; - break; - case 32: - sizes.surface_depth = 24; - sizes.surface_bpp = 32; - break; - } - break; - } - } - - crtc_count = 0; - for (i = 0; i < fb_helper->crtc_count; i++) { - struct drm_display_mode *desired_mode; - struct drm_mode_set *mode_set; - int x, y, j; - /* in case of tile group, are we the last tile vert or horiz? - * If no tile group you are always the last one both vertically - * and horizontally - */ - bool lastv = true, lasth = true; - - desired_mode = fb_helper->crtc_info[i].desired_mode; - mode_set = &fb_helper->crtc_info[i].mode_set; - - if (!desired_mode) - continue; - - crtc_count++; - - x = fb_helper->crtc_info[i].x; - y = fb_helper->crtc_info[i].y; - - if (gamma_size == 0) - gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; - - sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width); - sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height); - - for (j = 0; j < mode_set->num_connectors; j++) { - struct drm_connector *connector = mode_set->connectors[j]; - if (connector->has_tile) { - lasth = (connector->tile_h_loc == (connector->num_h_tile - 1)); - lastv = (connector->tile_v_loc == (connector->num_v_tile - 1)); - /* cloning to multiple tiles is just crazy-talk, so: */ - break; - } - } - - if (lasth) - sizes.fb_width = min_t(u32, desired_mode->hdisplay + x, sizes.fb_width); - if (lastv) - sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height); - } - - if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { - /* hmm everyone went away - assume VGA cable just fell out - and will come back later. */ - DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n"); - sizes.fb_width = sizes.surface_width = 1024; - sizes.fb_height = sizes.surface_height = 768; - } - - /* push down into drivers */ - ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); - if (ret < 0) - return ret; - - info = fb_helper->fbdev; - - /* - * Set the fb pointer - usually drm_setup_crtcs does this for hotplug - * events, but at init time drm_setup_crtcs needs to be called before - * the fb is allocated (since we need to figure out the desired size of - * the fb before we can allocate it ...). Hence we need to fix things up - * here again. - */ - for (i = 0; i < fb_helper->crtc_count; i++) - if (fb_helper->crtc_info[i].mode_set.num_connectors) - fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; - - - info->var.pixclock = 0; - if (register_framebuffer(info) < 0) - return -EINVAL; - - dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", - info->node, info->fix.id); - - if (list_empty(&kernel_fb_helper_list)) { - register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); - } - - list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); - - return 0; -} - -/** - * drm_fb_helper_fill_fix - initializes fixed fbdev information - * @info: fbdev registered by the helper - * @pitch: desired pitch - * @depth: desired depth - * - * Helper to fill in the fixed fbdev information useful for a non-accelerated - * fbdev emulations. Drivers which support acceleration methods which impose - * additional constraints need to set up their own limits. - * - * Drivers should call this (or their equivalent setup code) from their - * ->fb_probe callback. - */ -void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, - uint32_t depth) -{ - info->fix.type = FB_TYPE_PACKED_PIXELS; - info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : - FB_VISUAL_TRUECOLOR; - info->fix.mmio_start = 0; - info->fix.mmio_len = 0; - info->fix.type_aux = 0; - info->fix.xpanstep = 1; /* doing it in hw */ - info->fix.ypanstep = 1; /* doing it in hw */ - info->fix.ywrapstep = 0; - info->fix.accel = FB_ACCEL_NONE; - - info->fix.line_length = pitch; - return; -} -EXPORT_SYMBOL(drm_fb_helper_fill_fix); - -/** - * drm_fb_helper_fill_var - initalizes variable fbdev information - * @info: fbdev instance to set up - * @fb_helper: fb helper instance to use as template - * @fb_width: desired fb width - * @fb_height: desired fb height - * - * Sets up the variable fbdev metainformation from the given fb helper instance - * and the drm framebuffer allocated in fb_helper->fb. - * - * Drivers should call this (or their equivalent setup code) from their - * ->fb_probe callback after having allocated the fbdev backing - * storage framebuffer. - */ -void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, - uint32_t fb_width, uint32_t fb_height) -{ - struct drm_framebuffer *fb = fb_helper->fb; - info->pseudo_palette = fb_helper->pseudo_palette; - info->var.xres_virtual = fb->width; - info->var.yres_virtual = fb->height; - info->var.bits_per_pixel = fb->bits_per_pixel; - info->var.accel_flags = FB_ACCELF_TEXT; - info->var.xoffset = 0; - info->var.yoffset = 0; - info->var.activate = FB_ACTIVATE_NOW; - info->var.height = -1; - info->var.width = -1; - - switch (fb->depth) { - case 8: - info->var.red.offset = 0; - info->var.green.offset = 0; - info->var.blue.offset = 0; - info->var.red.length = 8; /* 8bit DAC */ - info->var.green.length = 8; - info->var.blue.length = 8; - info->var.transp.offset = 0; - info->var.transp.length = 0; - break; - case 15: - info->var.red.offset = 10; - info->var.green.offset = 5; - info->var.blue.offset = 0; - info->var.red.length = 5; - info->var.green.length = 5; - info->var.blue.length = 5; - info->var.transp.offset = 15; - info->var.transp.length = 1; - break; - case 16: - info->var.red.offset = 11; - info->var.green.offset = 5; - info->var.blue.offset = 0; - info->var.red.length = 5; - info->var.green.length = 6; - info->var.blue.length = 5; - info->var.transp.offset = 0; - break; - case 24: - info->var.red.offset = 16; - info->var.green.offset = 8; - info->var.blue.offset = 0; - info->var.red.length = 8; - info->var.green.length = 8; - info->var.blue.length = 8; - info->var.transp.offset = 0; - info->var.transp.length = 0; - break; - case 32: - info->var.red.offset = 16; - info->var.green.offset = 8; - info->var.blue.offset = 0; - info->var.red.length = 8; - info->var.green.length = 8; - info->var.blue.length = 8; - info->var.transp.offset = 24; - info->var.transp.length = 8; - break; - default: - break; - } - - info->var.xres = fb_width; - info->var.yres = fb_height; -} -EXPORT_SYMBOL(drm_fb_helper_fill_var); - -static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper, - uint32_t maxX, - uint32_t maxY) -{ - struct drm_connector *connector; - int count = 0; - int i; - - for (i = 0; i < fb_helper->connector_count; i++) { - connector = fb_helper->connector_info[i]->connector; - count += connector->funcs->fill_modes(connector, maxX, maxY); - } - - return count; -} - -struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) -{ - struct drm_display_mode *mode; - - list_for_each_entry(mode, &fb_connector->connector->modes, head) { - if (mode->hdisplay > width || - mode->vdisplay > height) - continue; - if (mode->type & DRM_MODE_TYPE_PREFERRED) - return mode; - } - return NULL; -} -EXPORT_SYMBOL(drm_has_preferred_mode); - -static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) -{ - return fb_connector->connector->cmdline_mode.specified; -} - -struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, - int width, int height) -{ - struct drm_cmdline_mode *cmdline_mode; - struct drm_display_mode *mode; - bool prefer_non_interlace; - - cmdline_mode = &fb_helper_conn->connector->cmdline_mode; - if (cmdline_mode->specified == false) - return NULL; - - /* attempt to find a matching mode in the list of modes - * we have gotten so far, if not add a CVT mode that conforms - */ - if (cmdline_mode->rb || cmdline_mode->margins) - goto create_mode; - - prefer_non_interlace = !cmdline_mode->interlace; -again: - list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { - /* check width/height */ - if (mode->hdisplay != cmdline_mode->xres || - mode->vdisplay != cmdline_mode->yres) - continue; - - if (cmdline_mode->refresh_specified) { - if (mode->vrefresh != cmdline_mode->refresh) - continue; - } - - if (cmdline_mode->interlace) { - if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) - continue; - } else if (prefer_non_interlace) { - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - continue; - } - return mode; - } - - if (prefer_non_interlace) { - prefer_non_interlace = false; - goto again; - } - -create_mode: - mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, - cmdline_mode); - list_add(&mode->head, &fb_helper_conn->connector->modes); - return mode; -} -EXPORT_SYMBOL(drm_pick_cmdline_mode); - -static bool drm_connector_enabled(struct drm_connector *connector, bool strict) -{ - bool enable; - - if (strict) - enable = connector->status == connector_status_connected; - else - enable = connector->status != connector_status_disconnected; - - return enable; -} - -static void drm_enable_connectors(struct drm_fb_helper *fb_helper, - bool *enabled) -{ - bool any_enabled = false; - struct drm_connector *connector; - int i = 0; - - for (i = 0; i < fb_helper->connector_count; i++) { - connector = fb_helper->connector_info[i]->connector; - enabled[i] = drm_connector_enabled(connector, true); - DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, - enabled[i] ? "yes" : "no"); - any_enabled |= enabled[i]; - } - - if (any_enabled) - return; - - for (i = 0; i < fb_helper->connector_count; i++) { - connector = fb_helper->connector_info[i]->connector; - enabled[i] = drm_connector_enabled(connector, false); - } -} - -static bool drm_target_cloned(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) -{ - int count, i, j; - bool can_clone = false; - struct drm_fb_helper_connector *fb_helper_conn; - struct drm_display_mode *dmt_mode, *mode; - - /* only contemplate cloning in the single crtc case */ - if (fb_helper->crtc_count > 1) - return false; - - count = 0; - for (i = 0; i < fb_helper->connector_count; i++) { - if (enabled[i]) - count++; - } - - /* only contemplate cloning if more than one connector is enabled */ - if (count <= 1) - return false; - - /* check the command line or if nothing common pick 1024x768 */ - can_clone = true; - for (i = 0; i < fb_helper->connector_count; i++) { - if (!enabled[i]) - continue; - fb_helper_conn = fb_helper->connector_info[i]; - modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); - if (!modes[i]) { - can_clone = false; - break; - } - for (j = 0; j < i; j++) { - if (!enabled[j]) - continue; - if (!drm_mode_equal(modes[j], modes[i])) - can_clone = false; - } - } - - if (can_clone) { - DRM_DEBUG_KMS("can clone using command line\n"); - return true; - } - - /* try and find a 1024x768 mode on each connector */ - can_clone = true; - dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false); - - for (i = 0; i < fb_helper->connector_count; i++) { - - if (!enabled[i]) - continue; - - fb_helper_conn = fb_helper->connector_info[i]; - list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { - if (drm_mode_equal(mode, dmt_mode)) - modes[i] = mode; - } - if (!modes[i]) - can_clone = false; - } - - if (can_clone) { - DRM_DEBUG_KMS("can clone using 1024x768\n"); - return true; - } - DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); - return false; -} - -static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - int idx, - int h_idx, int v_idx) -{ - struct drm_fb_helper_connector *fb_helper_conn; - int i; - int hoffset = 0, voffset = 0; - - for (i = 0; i < fb_helper->connector_count; i++) { - fb_helper_conn = fb_helper->connector_info[i]; - if (!fb_helper_conn->connector->has_tile) - continue; - - if (!modes[i] && (h_idx || v_idx)) { - DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i, - fb_helper_conn->connector->base.id); - continue; - } - if (fb_helper_conn->connector->tile_h_loc < h_idx) - hoffset += modes[i]->hdisplay; - - if (fb_helper_conn->connector->tile_v_loc < v_idx) - voffset += modes[i]->vdisplay; - } - offsets[idx].x = hoffset; - offsets[idx].y = voffset; - DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx); - return 0; -} - -static bool drm_target_preferred(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) -{ - struct drm_fb_helper_connector *fb_helper_conn; - int i; - uint64_t conn_configured = 0, mask; - int tile_pass = 0; - mask = (1 << fb_helper->connector_count) - 1; -retry: - for (i = 0; i < fb_helper->connector_count; i++) { - fb_helper_conn = fb_helper->connector_info[i]; - - if (conn_configured & (1 << i)) - continue; - - if (enabled[i] == false) { - conn_configured |= (1 << i); - continue; - } - - /* first pass over all the untiled connectors */ - if (tile_pass == 0 && fb_helper_conn->connector->has_tile) - continue; - - if (tile_pass == 1) { - if (fb_helper_conn->connector->tile_h_loc != 0 || - fb_helper_conn->connector->tile_v_loc != 0) - continue; - - } else { - if (fb_helper_conn->connector->tile_h_loc != tile_pass -1 && - fb_helper_conn->connector->tile_v_loc != tile_pass - 1) - /* if this tile_pass doesn't cover any of the tiles - keep going */ - continue; - - /* find the tile offsets for this pass - need - to find all tiles left and above */ - drm_get_tile_offsets(fb_helper, modes, offsets, - i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc); - } - DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", - fb_helper_conn->connector->base.id); - - /* got for command line mode first */ - modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); - if (!modes[i]) { - DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", - fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0); - modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); - } - /* No preferred modes, pick one off the list */ - if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) { - list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head) - break; - } - DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : - "none"); - conn_configured |= (1 << i); - } - - if ((conn_configured & mask) != mask) { - tile_pass++; - goto retry; - } - return true; -} - -static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, - struct drm_fb_helper_crtc **best_crtcs, - struct drm_display_mode **modes, - int n, int width, int height) -{ - int c, o; - struct drm_connector *connector; - const struct drm_connector_helper_funcs *connector_funcs; - struct drm_encoder *encoder; - int my_score, best_score, score; - struct drm_fb_helper_crtc **crtcs, *crtc; - struct drm_fb_helper_connector *fb_helper_conn; - - if (n == fb_helper->connector_count) - return 0; - - fb_helper_conn = fb_helper->connector_info[n]; - connector = fb_helper_conn->connector; - - best_crtcs[n] = NULL; - best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); - if (modes[n] == NULL) - return best_score; - - crtcs = kzalloc(fb_helper->connector_count * - sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); - if (!crtcs) - return best_score; - - my_score = 1; - if (connector->status == connector_status_connected) - my_score++; - if (drm_has_cmdline_mode(fb_helper_conn)) - my_score++; - if (drm_has_preferred_mode(fb_helper_conn, width, height)) - my_score++; - - connector_funcs = connector->helper_private; - encoder = connector_funcs->best_encoder(connector); - if (!encoder) - goto out; - - /* select a crtc for this connector and then attempt to configure - remaining connectors */ - for (c = 0; c < fb_helper->crtc_count; c++) { - crtc = &fb_helper->crtc_info[c]; - - if ((encoder->possible_crtcs & (1 << c)) == 0) - continue; - - for (o = 0; o < n; o++) - if (best_crtcs[o] == crtc) - break; - - if (o < n) { - /* ignore cloning unless only a single crtc */ - if (fb_helper->crtc_count > 1) - continue; - - if (!drm_mode_equal(modes[o], modes[n])) - continue; - } - - crtcs[n] = crtc; - memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *)); - score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, - width, height); - if (score > best_score) { - best_score = score; - memcpy(best_crtcs, crtcs, - fb_helper->connector_count * - sizeof(struct drm_fb_helper_crtc *)); - } - } -out: - kfree(crtcs); - return best_score; -} - -static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev = fb_helper->dev; - struct drm_fb_helper_crtc **crtcs; - struct drm_display_mode **modes; - struct drm_fb_offset *offsets; - struct drm_mode_set *modeset; - bool *enabled; - int width, height; - int i; - - DRM_DEBUG_KMS("\n"); - - width = dev->mode_config.max_width; - height = dev->mode_config.max_height; - - crtcs = kcalloc(dev->mode_config.num_connector, - sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); - modes = kcalloc(dev->mode_config.num_connector, - sizeof(struct drm_display_mode *), GFP_KERNEL); - offsets = kcalloc(dev->mode_config.num_connector, - sizeof(struct drm_fb_offset), GFP_KERNEL); - enabled = kcalloc(dev->mode_config.num_connector, - sizeof(bool), GFP_KERNEL); - if (!crtcs || !modes || !enabled || !offsets) { - DRM_ERROR("Memory allocation failed\n"); - goto out; - } - - - drm_enable_connectors(fb_helper, enabled); - - if (!(fb_helper->funcs->initial_config && - fb_helper->funcs->initial_config(fb_helper, crtcs, modes, - offsets, - enabled, width, height))) { - memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0])); - memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0])); - memset(offsets, 0, dev->mode_config.num_connector*sizeof(offsets[0])); - - if (!drm_target_cloned(fb_helper, modes, offsets, - enabled, width, height) && - !drm_target_preferred(fb_helper, modes, offsets, - enabled, width, height)) - DRM_ERROR("Unable to find initial modes\n"); - - DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", - width, height); - - drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); - } - - /* need to set the modesets up here for use later */ - /* fill out the connector<->crtc mappings into the modesets */ - for (i = 0; i < fb_helper->crtc_count; i++) { - modeset = &fb_helper->crtc_info[i].mode_set; - modeset->num_connectors = 0; - modeset->fb = NULL; - } - - for (i = 0; i < fb_helper->connector_count; i++) { - struct drm_display_mode *mode = modes[i]; - struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; - struct drm_fb_offset *offset = &offsets[i]; - modeset = &fb_crtc->mode_set; - - if (mode && fb_crtc) { - DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", - mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y); - fb_crtc->desired_mode = mode; - fb_crtc->x = offset->x; - fb_crtc->y = offset->y; - if (modeset->mode) - drm_mode_destroy(dev, modeset->mode); - modeset->mode = drm_mode_duplicate(dev, - fb_crtc->desired_mode); - modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; - modeset->fb = fb_helper->fb; - modeset->x = offset->x; - modeset->y = offset->y; - } - } - - /* Clear out any old modes if there are no more connected outputs. */ - for (i = 0; i < fb_helper->crtc_count; i++) { - modeset = &fb_helper->crtc_info[i].mode_set; - if (modeset->num_connectors == 0) { - BUG_ON(modeset->fb); - if (modeset->mode) - drm_mode_destroy(dev, modeset->mode); - modeset->mode = NULL; - } - } -out: - kfree(crtcs); - kfree(modes); - kfree(offsets); - kfree(enabled); -} - -/** - * drm_fb_helper_initial_config - setup a sane initial connector configuration - * @fb_helper: fb_helper device struct - * @bpp_sel: bpp value to use for the framebuffer configuration - * - * Scans the CRTCs and connectors and tries to put together an initial setup. - * At the moment, this is a cloned configuration across all heads with - * a new framebuffer object as the backing store. - * - * Note that this also registers the fbdev and so allows userspace to call into - * the driver through the fbdev interfaces. - * - * This function will call down into the ->fb_probe callback to let - * the driver allocate and initialize the fbdev info structure and the drm - * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and - * drm_fb_helper_fill_fix() are provided as helpers to setup simple default - * values for the fbdev info structure. - * - * RETURNS: - * Zero if everything went ok, nonzero otherwise. - */ -int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) -{ - struct drm_device *dev = fb_helper->dev; - int count = 0; - - if (!drm_fbdev_emulation) - return 0; - - mutex_lock(&dev->mode_config.mutex); - count = drm_fb_helper_probe_connector_modes(fb_helper, - dev->mode_config.max_width, - dev->mode_config.max_height); - mutex_unlock(&dev->mode_config.mutex); - /* - * we shouldn't end up with no modes here. - */ - if (count == 0) - dev_info(fb_helper->dev->dev, "No connectors reported connected with modes\n"); - - drm_setup_crtcs(fb_helper); - - return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); -} -EXPORT_SYMBOL(drm_fb_helper_initial_config); - -/** - * drm_fb_helper_hotplug_event - respond to a hotplug notification by - * probing all the outputs attached to the fb - * @fb_helper: the drm_fb_helper - * - * Scan the connectors attached to the fb_helper and try to put together a - * setup after *notification of a change in output configuration. - * - * Called at runtime, takes the mode config locks to be able to check/change the - * modeset configuration. Must be run from process context (which usually means - * either the output polling work or a work item launched from the driver's - * hotplug interrupt). - * - * Note that drivers may call this even before calling - * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows - * for a race-free fbcon setup and will make sure that the fbdev emulation will - * not miss any hotplug events. - * - * RETURNS: - * 0 on success and a non-zero error code otherwise. - */ -int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev = fb_helper->dev; - u32 max_width, max_height; - - if (!drm_fbdev_emulation) - return 0; - - mutex_lock(&fb_helper->dev->mode_config.mutex); - if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) { - fb_helper->delayed_hotplug = true; - mutex_unlock(&fb_helper->dev->mode_config.mutex); - return 0; - } - DRM_DEBUG_KMS("\n"); - - max_width = fb_helper->fb->width; - max_height = fb_helper->fb->height; - - drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height); - mutex_unlock(&fb_helper->dev->mode_config.mutex); - - drm_modeset_lock_all(dev); - drm_setup_crtcs(fb_helper); - drm_modeset_unlock_all(dev); - drm_fb_helper_set_par(fb_helper->fbdev); - - return 0; -} -EXPORT_SYMBOL(drm_fb_helper_hotplug_event); - -/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT) - * but the module doesn't depend on any fb console symbols. At least - * attempt to load fbcon to avoid leaving the system without a usable console. - */ -#if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT) -static int __init drm_fb_helper_modinit(void) -{ - const char *name = "fbcon"; - struct module *fbcon; - - mutex_lock(&module_mutex); - fbcon = find_module(name); - mutex_unlock(&module_mutex); - - if (!fbcon) - request_module_nowait(name); - return 0; -} - -module_init(drm_fb_helper_modinit); -#endif diff --git a/src/4.x/drivers/gpu/drm/drm_flip_work.c b/src/4.x/drivers/gpu/drm/drm_flip_work.c deleted file mode 100644 index 12dea16f2..000000000 --- a/src/4.x/drivers/gpu/drm/drm_flip_work.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2013 Red Hat - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -/** - * drm_flip_work_allocate_task - allocate a flip-work task - * @data: data associated to the task - * @flags: allocator flags - * - * Allocate a drm_flip_task object and attach private data to it. - */ -struct drm_flip_task *drm_flip_work_allocate_task(void *data, gfp_t flags) -{ - struct drm_flip_task *task; - - task = kzalloc(sizeof(*task), flags); - if (task) - task->data = data; - - return task; -} -EXPORT_SYMBOL(drm_flip_work_allocate_task); - -/** - * drm_flip_work_queue_task - queue a specific task - * @work: the flip-work - * @task: the task to handle - * - * Queues task, that will later be run (passed back to drm_flip_func_t - * func) on a work queue after drm_flip_work_commit() is called. - */ -void drm_flip_work_queue_task(struct drm_flip_work *work, - struct drm_flip_task *task) -{ - unsigned long flags; - - spin_lock_irqsave(&work->lock, flags); - list_add_tail(&task->node, &work->queued); - spin_unlock_irqrestore(&work->lock, flags); -} -EXPORT_SYMBOL(drm_flip_work_queue_task); - -/** - * drm_flip_work_queue - queue work - * @work: the flip-work - * @val: the value to queue - * - * Queues work, that will later be run (passed back to drm_flip_func_t - * func) on a work queue after drm_flip_work_commit() is called. - */ -void drm_flip_work_queue(struct drm_flip_work *work, void *val) -{ - struct drm_flip_task *task; - - task = drm_flip_work_allocate_task(val, - drm_can_sleep() ? GFP_KERNEL : GFP_ATOMIC); - if (task) { - drm_flip_work_queue_task(work, task); - } else { - DRM_ERROR("%s could not allocate task!\n", work->name); - work->func(work, val); - } -} -EXPORT_SYMBOL(drm_flip_work_queue); - -/** - * drm_flip_work_commit - commit queued work - * @work: the flip-work - * @wq: the work-queue to run the queued work on - * - * Trigger work previously queued by drm_flip_work_queue() to run - * on a workqueue. The typical usage would be to queue work (via - * drm_flip_work_queue()) at any point (from vblank irq and/or - * prior), and then from vblank irq commit the queued work. - */ -void drm_flip_work_commit(struct drm_flip_work *work, - struct workqueue_struct *wq) -{ - unsigned long flags; - - spin_lock_irqsave(&work->lock, flags); - list_splice_tail(&work->queued, &work->commited); - INIT_LIST_HEAD(&work->queued); - spin_unlock_irqrestore(&work->lock, flags); - queue_work(wq, &work->worker); -} -EXPORT_SYMBOL(drm_flip_work_commit); - -static void flip_worker(struct work_struct *w) -{ - struct drm_flip_work *work = container_of(w, struct drm_flip_work, worker); - struct list_head tasks; - unsigned long flags; - - while (1) { - struct drm_flip_task *task, *tmp; - - INIT_LIST_HEAD(&tasks); - spin_lock_irqsave(&work->lock, flags); - list_splice_tail(&work->commited, &tasks); - INIT_LIST_HEAD(&work->commited); - spin_unlock_irqrestore(&work->lock, flags); - - if (list_empty(&tasks)) - break; - - list_for_each_entry_safe(task, tmp, &tasks, node) { - work->func(work, task->data); - kfree(task); - } - } -} - -/** - * drm_flip_work_init - initialize flip-work - * @work: the flip-work to initialize - * @name: debug name - * @func: the callback work function - * - * Initializes/allocates resources for the flip-work - */ -void drm_flip_work_init(struct drm_flip_work *work, - const char *name, drm_flip_func_t func) -{ - work->name = name; - INIT_LIST_HEAD(&work->queued); - INIT_LIST_HEAD(&work->commited); - spin_lock_init(&work->lock); - work->func = func; - - INIT_WORK(&work->worker, flip_worker); -} -EXPORT_SYMBOL(drm_flip_work_init); - -/** - * drm_flip_work_cleanup - cleans up flip-work - * @work: the flip-work to cleanup - * - * Destroy resources allocated for the flip-work - */ -void drm_flip_work_cleanup(struct drm_flip_work *work) -{ - WARN_ON(!list_empty(&work->queued) || !list_empty(&work->commited)); -} -EXPORT_SYMBOL(drm_flip_work_cleanup); diff --git a/src/4.x/drivers/gpu/drm/drm_fops.c b/src/4.x/drivers/gpu/drm/drm_fops.c deleted file mode 100644 index 88ceac091..000000000 --- a/src/4.x/drivers/gpu/drm/drm_fops.c +++ /dev/null @@ -1,578 +0,0 @@ -/** - * \file drm_fops.c - * File operations for DRM - * - * \author Rickard E. (Rik) Faith - * \author Daryll Strauss - * \author Gareth Hughes - */ - -/* - * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.com - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include "drm_legacy.h" -#include "drm_internal.h" - -/* from BKL pushdown */ -DEFINE_MUTEX(drm_global_mutex); - -static int drm_open_helper(struct file *filp, struct drm_minor *minor); - -static int drm_setup(struct drm_device * dev) -{ - int ret; - - if (dev->driver->firstopen && - !drm_core_check_feature(dev, DRIVER_MODESET)) { - ret = dev->driver->firstopen(dev); - if (ret != 0) - return ret; - } - - ret = drm_legacy_dma_setup(dev); - if (ret < 0) - return ret; - - - DRM_DEBUG("\n"); - return 0; -} - -/** - * Open file. - * - * \param inode device inode - * \param filp file pointer. - * \return zero on success or a negative number on failure. - * - * Searches the DRM device with the same minor number, calls open_helper(), and - * increments the device open count. If the open count was previous at zero, - * i.e., it's the first that the device is open, then calls setup(). - */ -int drm_open(struct inode *inode, struct file *filp) -{ - struct drm_device *dev; - struct drm_minor *minor; - int retcode; - int need_setup = 0; - - minor = drm_minor_acquire(iminor(inode)); - if (IS_ERR(minor)) - return PTR_ERR(minor); - - dev = minor->dev; - if (!dev->open_count++) - need_setup = 1; - - /* share address_space across all char-devs of a single device */ - filp->f_mapping = dev->anon_inode->i_mapping; - - retcode = drm_open_helper(filp, minor); - if (retcode) - goto err_undo; - if (need_setup) { - retcode = drm_setup(dev); - if (retcode) - goto err_undo; - } - return 0; - -err_undo: - dev->open_count--; - drm_minor_release(minor); - return retcode; -} -EXPORT_SYMBOL(drm_open); - -/** - * Check whether DRI will run on this CPU. - * - * \return non-zero if the DRI will run on this CPU, or zero otherwise. - */ -static int drm_cpu_valid(void) -{ -#if defined(__sparc__) && !defined(__sparc_v9__) - return 0; /* No cmpxchg before v9 sparc. */ -#endif - return 1; -} - -/** - * drm_new_set_master - Allocate a new master object and become master for the - * associated master realm. - * - * @dev: The associated device. - * @fpriv: File private identifying the client. - * - * This function must be called with dev::struct_mutex held. - * Returns negative error code on failure. Zero on success. - */ -int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv) -{ - struct drm_master *old_master; - int ret; - - lockdep_assert_held_once(&dev->master_mutex); - - /* create a new master */ - fpriv->minor->master = drm_master_create(fpriv->minor); - if (!fpriv->minor->master) - return -ENOMEM; - - /* take another reference for the copy in the local file priv */ - old_master = fpriv->master; - fpriv->master = drm_master_get(fpriv->minor->master); - - if (dev->driver->master_create) { - ret = dev->driver->master_create(dev, fpriv->master); - if (ret) - goto out_err; - } - if (dev->driver->master_set) { - ret = dev->driver->master_set(dev, fpriv, true); - if (ret) - goto out_err; - } - - fpriv->is_master = 1; - fpriv->allowed_master = 1; - fpriv->authenticated = 1; - if (old_master) - drm_master_put(&old_master); - - return 0; - -out_err: - /* drop both references and restore old master on failure */ - drm_master_put(&fpriv->minor->master); - drm_master_put(&fpriv->master); - fpriv->master = old_master; - - return ret; -} - -/** - * Called whenever a process opens /dev/drm. - * - * \param filp file pointer. - * \param minor acquired minor-object. - * \return zero on success or a negative number on failure. - * - * Creates and initializes a drm_file structure for the file private data in \p - * filp and add it into the double linked list in \p dev. - */ -static int drm_open_helper(struct file *filp, struct drm_minor *minor) -{ - struct drm_device *dev = minor->dev; - struct drm_file *priv; - int ret; - - if (filp->f_flags & O_EXCL) - return -EBUSY; /* No exclusive opens */ - if (!drm_cpu_valid()) - return -EINVAL; - if (dev->switch_power_state != DRM_SWITCH_POWER_ON && dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF) - return -EINVAL; - - DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor->index); - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - filp->private_data = priv; - filp->f_mode |= FMODE_UNSIGNED_OFFSET; - priv->filp = filp; - priv->uid = current_euid(); - priv->pid = get_pid(task_pid(current)); - priv->minor = minor; - - /* for compatibility root is always authenticated */ - priv->authenticated = capable(CAP_SYS_ADMIN); - priv->lock_count = 0; - - INIT_LIST_HEAD(&priv->lhead); - INIT_LIST_HEAD(&priv->fbs); - mutex_init(&priv->fbs_lock); - INIT_LIST_HEAD(&priv->blobs); - INIT_LIST_HEAD(&priv->event_list); - init_waitqueue_head(&priv->event_wait); - priv->event_space = 4096; /* set aside 4k for event buffer */ - - if (drm_core_check_feature(dev, DRIVER_GEM)) - drm_gem_open(dev, priv); - - if (drm_core_check_feature(dev, DRIVER_PRIME)) - drm_prime_init_file_private(&priv->prime); - - if (dev->driver->open) { - ret = dev->driver->open(dev, priv); - if (ret < 0) - goto out_prime_destroy; - } - - /* if there is no current master make this fd it, but do not create - * any master object for render clients */ - mutex_lock(&dev->master_mutex); - if (drm_is_primary_client(priv) && !priv->minor->master) { - /* create a new master */ - ret = drm_new_set_master(dev, priv); - if (ret) - goto out_close; - } else if (drm_is_primary_client(priv)) { - /* get a reference to the master */ - priv->master = drm_master_get(priv->minor->master); - } - mutex_unlock(&dev->master_mutex); - - mutex_lock(&dev->struct_mutex); - list_add(&priv->lhead, &dev->filelist); - mutex_unlock(&dev->struct_mutex); - -#ifdef __alpha__ - /* - * Default the hose - */ - if (!dev->hose) { - struct pci_dev *pci_dev; - pci_dev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL); - if (pci_dev) { - dev->hose = pci_dev->sysdata; - pci_dev_put(pci_dev); - } - if (!dev->hose) { - struct pci_bus *b = list_entry(pci_root_buses.next, - struct pci_bus, node); - if (b) - dev->hose = b->sysdata; - } - } -#endif - - return 0; - -out_close: - mutex_unlock(&dev->master_mutex); - if (dev->driver->postclose) - dev->driver->postclose(dev, priv); -out_prime_destroy: - if (drm_core_check_feature(dev, DRIVER_PRIME)) - drm_prime_destroy_file_private(&priv->prime); - if (drm_core_check_feature(dev, DRIVER_GEM)) - drm_gem_release(dev, priv); - put_pid(priv->pid); - kfree(priv); - filp->private_data = NULL; - return ret; -} - -static void drm_master_release(struct drm_device *dev, struct file *filp) -{ - struct drm_file *file_priv = filp->private_data; - - if (drm_legacy_i_have_hw_lock(dev, file_priv)) { - DRM_DEBUG("File %p released, freeing lock for context %d\n", - filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); - drm_legacy_lock_free(&file_priv->master->lock, - _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); - } -} - -static void drm_events_release(struct drm_file *file_priv) -{ - struct drm_device *dev = file_priv->minor->dev; - struct drm_pending_event *e, *et; - struct drm_pending_vblank_event *v, *vt; - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - - /* Remove pending flips */ - list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link) - if (v->base.file_priv == file_priv) { - list_del(&v->base.link); - drm_vblank_put(dev, v->pipe); - v->base.destroy(&v->base); - } - - /* Remove unconsumed events */ - list_for_each_entry_safe(e, et, &file_priv->event_list, link) { - list_del(&e->link); - e->destroy(e); - } - - spin_unlock_irqrestore(&dev->event_lock, flags); -} - -/** - * drm_legacy_dev_reinit - * - * Reinitializes a legacy/ums drm device in it's lastclose function. - */ -static void drm_legacy_dev_reinit(struct drm_device *dev) -{ - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return; - - dev->sigdata.lock = NULL; - - dev->context_flag = 0; - dev->last_context = 0; - dev->if_version = 0; -} - -/** - * Take down the DRM device. - * - * \param dev DRM device structure. - * - * Frees every resource in \p dev. - * - * \sa drm_device - */ -int drm_lastclose(struct drm_device * dev) -{ - DRM_DEBUG("\n"); - - if (dev->driver->lastclose) - dev->driver->lastclose(dev); - DRM_DEBUG("driver lastclose completed\n"); - - if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET)) - drm_irq_uninstall(dev); - - mutex_lock(&dev->struct_mutex); - - drm_agp_clear(dev); - - drm_legacy_sg_cleanup(dev); - drm_legacy_vma_flush(dev); - drm_legacy_dma_takedown(dev); - - mutex_unlock(&dev->struct_mutex); - - drm_legacy_dev_reinit(dev); - - DRM_DEBUG("lastclose completed\n"); - return 0; -} - -/** - * Release file. - * - * \param inode device inode - * \param file_priv DRM file private. - * \return zero on success or a negative number on failure. - * - * If the hardware lock is held then free it, and take it again for the kernel - * context since it's necessary to reclaim buffers. Unlink the file private - * data from its list and free it. Decreases the open count and if it reaches - * zero calls drm_lastclose(). - */ -int drm_release(struct inode *inode, struct file *filp) -{ - struct drm_file *file_priv = filp->private_data; - struct drm_minor *minor = file_priv->minor; - struct drm_device *dev = minor->dev; - int retcode = 0; - - mutex_lock(&drm_global_mutex); - - DRM_DEBUG("open_count = %d\n", dev->open_count); - - mutex_lock(&dev->struct_mutex); - list_del(&file_priv->lhead); - if (file_priv->magic) - idr_remove(&file_priv->master->magic_map, file_priv->magic); - mutex_unlock(&dev->struct_mutex); - - if (dev->driver->preclose) - dev->driver->preclose(dev, file_priv); - - /* ======================================================== - * Begin inline drm_release - */ - - DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", - task_pid_nr(current), - (long)old_encode_dev(file_priv->minor->kdev->devt), - dev->open_count); - - /* if the master has gone away we can't do anything with the lock */ - if (file_priv->minor->master) - drm_master_release(dev, filp); - - if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) - drm_legacy_reclaim_buffers(dev, file_priv); - - drm_events_release(file_priv); - - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - drm_fb_release(file_priv); - drm_property_destroy_user_blobs(dev, file_priv); - } - - if (drm_core_check_feature(dev, DRIVER_GEM)) - drm_gem_release(dev, file_priv); - - drm_legacy_ctxbitmap_flush(dev, file_priv); - - mutex_lock(&dev->master_mutex); - - if (file_priv->is_master) { - struct drm_master *master = file_priv->master; - - /** - * Since the master is disappearing, so is the - * possibility to lock. - */ - mutex_lock(&dev->struct_mutex); - if (master->lock.hw_lock) { - if (dev->sigdata.lock == master->lock.hw_lock) - dev->sigdata.lock = NULL; - master->lock.hw_lock = NULL; - master->lock.file_priv = NULL; - wake_up_interruptible_all(&master->lock.lock_queue); - } - mutex_unlock(&dev->struct_mutex); - - if (file_priv->minor->master == file_priv->master) { - /* drop the reference held my the minor */ - if (dev->driver->master_drop) - dev->driver->master_drop(dev, file_priv, true); - drm_master_put(&file_priv->minor->master); - } - } - - /* drop the master reference held by the file priv */ - if (file_priv->master) - drm_master_put(&file_priv->master); - file_priv->is_master = 0; - mutex_unlock(&dev->master_mutex); - - if (dev->driver->postclose) - dev->driver->postclose(dev, file_priv); - - - if (drm_core_check_feature(dev, DRIVER_PRIME)) - drm_prime_destroy_file_private(&file_priv->prime); - - WARN_ON(!list_empty(&file_priv->event_list)); - - put_pid(file_priv->pid); - kfree(file_priv); - - /* ======================================================== - * End inline drm_release - */ - - if (!--dev->open_count) { - retcode = drm_lastclose(dev); - if (drm_device_is_unplugged(dev)) - drm_put_dev(dev); - } - mutex_unlock(&drm_global_mutex); - - drm_minor_release(minor); - - return retcode; -} -EXPORT_SYMBOL(drm_release); - -ssize_t drm_read(struct file *filp, char __user *buffer, - size_t count, loff_t *offset) -{ - struct drm_file *file_priv = filp->private_data; - struct drm_device *dev = file_priv->minor->dev; - ssize_t ret = 0; - - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - - spin_lock_irq(&dev->event_lock); - for (;;) { - if (list_empty(&file_priv->event_list)) { - if (ret) - break; - - if (filp->f_flags & O_NONBLOCK) { - ret = -EAGAIN; - break; - } - - spin_unlock_irq(&dev->event_lock); - ret = wait_event_interruptible(file_priv->event_wait, - !list_empty(&file_priv->event_list)); - spin_lock_irq(&dev->event_lock); - if (ret < 0) - break; - - ret = 0; - } else { - struct drm_pending_event *e; - - e = list_first_entry(&file_priv->event_list, - struct drm_pending_event, link); - if (e->event->length + ret > count) - break; - - if (__copy_to_user_inatomic(buffer + ret, - e->event, e->event->length)) { - if (ret == 0) - ret = -EFAULT; - break; - } - - file_priv->event_space += e->event->length; - ret += e->event->length; - list_del(&e->link); - e->destroy(e); - } - } - spin_unlock_irq(&dev->event_lock); - - return ret; -} -EXPORT_SYMBOL(drm_read); - -unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) -{ - struct drm_file *file_priv = filp->private_data; - unsigned int mask = 0; - - poll_wait(filp, &file_priv->event_wait, wait); - - if (!list_empty(&file_priv->event_list)) - mask |= POLLIN | POLLRDNORM; - - return mask; -} -EXPORT_SYMBOL(drm_poll); diff --git a/src/4.x/drivers/gpu/drm/drm_gem.c b/src/4.x/drivers/gpu/drm/drm_gem.c deleted file mode 100644 index 914711313..000000000 --- a/src/4.x/drivers/gpu/drm/drm_gem.c +++ /dev/null @@ -1,913 +0,0 @@ -/* - * Copyright © 2008 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - * - * Authors: - * Eric Anholt - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "drm_internal.h" - -/** @file drm_gem.c - * - * This file provides some of the base ioctls and library routines for - * the graphics memory manager implemented by each device driver. - * - * Because various devices have different requirements in terms of - * synchronization and migration strategies, implementing that is left up to - * the driver, and all that the general API provides should be generic -- - * allocating objects, reading/writing data with the cpu, freeing objects. - * Even there, platform-dependent optimizations for reading/writing data with - * the CPU mean we'll likely hook those out to driver-specific calls. However, - * the DRI2 implementation wants to have at least allocate/mmap be generic. - * - * The goal was to have swap-backed object allocation managed through - * struct file. However, file descriptors as handles to a struct file have - * two major failings: - * - Process limits prevent more than 1024 or so being used at a time by - * default. - * - Inability to allocate high fds will aggravate the X Server's select() - * handling, and likely that of many GL client applications as well. - * - * This led to a plan of using our own integer IDs (called handles, following - * DRM terminology) to mimic fds, and implement the fd syscalls we need as - * ioctls. The objects themselves will still include the struct file so - * that we can transition to fds if the required kernel infrastructure shows - * up at a later date, and as our interface with shmfs for memory allocation. - */ - -/* - * We make up offsets for buffer objects so we can recognize them at - * mmap time. - */ - -/* pgoff in mmap is an unsigned long, so we need to make sure that - * the faked up offset will fit - */ - -#if BITS_PER_LONG == 64 -#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1) -#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16) -#else -#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1) -#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16) -#endif - -/** - * drm_gem_init - Initialize the GEM device fields - * @dev: drm_devic structure to initialize - */ -int -drm_gem_init(struct drm_device *dev) -{ - struct drm_vma_offset_manager *vma_offset_manager; - - mutex_init(&dev->object_name_lock); - idr_init(&dev->object_name_idr); - - vma_offset_manager = kzalloc(sizeof(*vma_offset_manager), GFP_KERNEL); - if (!vma_offset_manager) { - DRM_ERROR("out of memory\n"); - return -ENOMEM; - } - - dev->vma_offset_manager = vma_offset_manager; - drm_vma_offset_manager_init(vma_offset_manager, - DRM_FILE_PAGE_OFFSET_START, - DRM_FILE_PAGE_OFFSET_SIZE); - - return 0; -} - -void -drm_gem_destroy(struct drm_device *dev) -{ - - drm_vma_offset_manager_destroy(dev->vma_offset_manager); - kfree(dev->vma_offset_manager); - dev->vma_offset_manager = NULL; -} - -/** - * drm_gem_object_init - initialize an allocated shmem-backed GEM object - * @dev: drm_device the object should be initialized for - * @obj: drm_gem_object to initialize - * @size: object size - * - * Initialize an already allocated GEM object of the specified size with - * shmfs backing store. - */ -int drm_gem_object_init(struct drm_device *dev, - struct drm_gem_object *obj, size_t size) -{ - struct file *filp; - - drm_gem_private_object_init(dev, obj, size); - - filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); - if (IS_ERR(filp)) - return PTR_ERR(filp); - - obj->filp = filp; - - return 0; -} -EXPORT_SYMBOL(drm_gem_object_init); - -/** - * drm_gem_private_object_init - initialize an allocated private GEM object - * @dev: drm_device the object should be initialized for - * @obj: drm_gem_object to initialize - * @size: object size - * - * Initialize an already allocated GEM object of the specified size with - * no GEM provided backing store. Instead the caller is responsible for - * backing the object and handling it. - */ -void drm_gem_private_object_init(struct drm_device *dev, - struct drm_gem_object *obj, size_t size) -{ - BUG_ON((size & (PAGE_SIZE - 1)) != 0); - - obj->dev = dev; - obj->filp = NULL; - - kref_init(&obj->refcount); - obj->handle_count = 0; - obj->size = size; - drm_vma_node_reset(&obj->vma_node); -} -EXPORT_SYMBOL(drm_gem_private_object_init); - -static void -drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) -{ - /* - * Note: obj->dma_buf can't disappear as long as we still hold a - * handle reference in obj->handle_count. - */ - mutex_lock(&filp->prime.lock); - if (obj->dma_buf) { - drm_prime_remove_buf_handle_locked(&filp->prime, - obj->dma_buf); - } - mutex_unlock(&filp->prime.lock); -} - -/** - * drm_gem_object_handle_free - release resources bound to userspace handles - * @obj: GEM object to clean up. - * - * Called after the last handle to the object has been closed - * - * Removes any name for the object. Note that this must be - * called before drm_gem_object_free or we'll be touching - * freed memory - */ -static void drm_gem_object_handle_free(struct drm_gem_object *obj) -{ - struct drm_device *dev = obj->dev; - - /* Remove any name for this object */ - if (obj->name) { - idr_remove(&dev->object_name_idr, obj->name); - obj->name = 0; - } -} - -static void drm_gem_object_exported_dma_buf_free(struct drm_gem_object *obj) -{ - /* Unbreak the reference cycle if we have an exported dma_buf. */ - if (obj->dma_buf) { - dma_buf_put(obj->dma_buf); - obj->dma_buf = NULL; - } -} - -static void -drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) -{ - if (WARN_ON(obj->handle_count == 0)) - return; - - /* - * Must bump handle count first as this may be the last - * ref, in which case the object would disappear before we - * checked for a name - */ - - mutex_lock(&obj->dev->object_name_lock); - if (--obj->handle_count == 0) { - drm_gem_object_handle_free(obj); - drm_gem_object_exported_dma_buf_free(obj); - } - mutex_unlock(&obj->dev->object_name_lock); - - drm_gem_object_unreference_unlocked(obj); -} - -/** - * drm_gem_handle_delete - deletes the given file-private handle - * @filp: drm file-private structure to use for the handle look up - * @handle: userspace handle to delete - * - * Removes the GEM handle from the @filp lookup table and if this is the last - * handle also cleans up linked resources like GEM names. - */ -int -drm_gem_handle_delete(struct drm_file *filp, u32 handle) -{ - struct drm_device *dev; - struct drm_gem_object *obj; - - /* This is gross. The idr system doesn't let us try a delete and - * return an error code. It just spews if you fail at deleting. - * So, we have to grab a lock around finding the object and then - * doing the delete on it and dropping the refcount, or the user - * could race us to double-decrement the refcount and cause a - * use-after-free later. Given the frequency of our handle lookups, - * we may want to use ida for number allocation and a hash table - * for the pointers, anyway. - */ - spin_lock(&filp->table_lock); - - /* Check if we currently have a reference on the object */ - obj = idr_find(&filp->object_idr, handle); - if (obj == NULL) { - spin_unlock(&filp->table_lock); - return -EINVAL; - } - dev = obj->dev; - - /* Release reference and decrement refcount. */ - idr_remove(&filp->object_idr, handle); - spin_unlock(&filp->table_lock); - - if (drm_core_check_feature(dev, DRIVER_PRIME)) - drm_gem_remove_prime_handles(obj, filp); - drm_vma_node_revoke(&obj->vma_node, filp->filp); - - if (dev->driver->gem_close_object) - dev->driver->gem_close_object(obj, filp); - drm_gem_object_handle_unreference_unlocked(obj); - - return 0; -} -EXPORT_SYMBOL(drm_gem_handle_delete); - -/** - * drm_gem_dumb_destroy - dumb fb callback helper for gem based drivers - * @file: drm file-private structure to remove the dumb handle from - * @dev: corresponding drm_device - * @handle: the dumb handle to remove - * - * This implements the ->dumb_destroy kms driver callback for drivers which use - * gem to manage their backing storage. - */ -int drm_gem_dumb_destroy(struct drm_file *file, - struct drm_device *dev, - uint32_t handle) -{ - return drm_gem_handle_delete(file, handle); -} -EXPORT_SYMBOL(drm_gem_dumb_destroy); - -/** - * drm_gem_handle_create_tail - internal functions to create a handle - * @file_priv: drm file-private structure to register the handle for - * @obj: object to register - * @handlep: pointer to return the created handle to the caller - * - * This expects the dev->object_name_lock to be held already and will drop it - * before returning. Used to avoid races in establishing new handles when - * importing an object from either an flink name or a dma-buf. - */ -int -drm_gem_handle_create_tail(struct drm_file *file_priv, - struct drm_gem_object *obj, - u32 *handlep) -{ - struct drm_device *dev = obj->dev; - int ret; - - WARN_ON(!mutex_is_locked(&dev->object_name_lock)); - - /* - * Get the user-visible handle using idr. Preload and perform - * allocation under our spinlock. - */ - idr_preload(GFP_KERNEL); - spin_lock(&file_priv->table_lock); - - ret = idr_alloc(&file_priv->object_idr, obj, 1, 0, GFP_NOWAIT); - drm_gem_object_reference(obj); - obj->handle_count++; - spin_unlock(&file_priv->table_lock); - idr_preload_end(); - mutex_unlock(&dev->object_name_lock); - if (ret < 0) - goto err_unref; - - *handlep = ret; - - ret = drm_vma_node_allow(&obj->vma_node, file_priv->filp); - if (ret) - goto err_remove; - - if (dev->driver->gem_open_object) { - ret = dev->driver->gem_open_object(obj, file_priv); - if (ret) - goto err_revoke; - } - - return 0; - -err_revoke: - drm_vma_node_revoke(&obj->vma_node, file_priv->filp); -err_remove: - spin_lock(&file_priv->table_lock); - idr_remove(&file_priv->object_idr, *handlep); - spin_unlock(&file_priv->table_lock); -err_unref: - drm_gem_object_handle_unreference_unlocked(obj); - return ret; -} - -/** - * drm_gem_handle_create - create a gem handle for an object - * @file_priv: drm file-private structure to register the handle for - * @obj: object to register - * @handlep: pionter to return the created handle to the caller - * - * Create a handle for this object. This adds a handle reference - * to the object, which includes a regular reference count. Callers - * will likely want to dereference the object afterwards. - */ -int drm_gem_handle_create(struct drm_file *file_priv, - struct drm_gem_object *obj, - u32 *handlep) -{ - mutex_lock(&obj->dev->object_name_lock); - - return drm_gem_handle_create_tail(file_priv, obj, handlep); -} -EXPORT_SYMBOL(drm_gem_handle_create); - - -/** - * drm_gem_free_mmap_offset - release a fake mmap offset for an object - * @obj: obj in question - * - * This routine frees fake offsets allocated by drm_gem_create_mmap_offset(). - */ -void -drm_gem_free_mmap_offset(struct drm_gem_object *obj) -{ - struct drm_device *dev = obj->dev; - - drm_vma_offset_remove(dev->vma_offset_manager, &obj->vma_node); -} -EXPORT_SYMBOL(drm_gem_free_mmap_offset); - -/** - * drm_gem_create_mmap_offset_size - create a fake mmap offset for an object - * @obj: obj in question - * @size: the virtual size - * - * GEM memory mapping works by handing back to userspace a fake mmap offset - * it can use in a subsequent mmap(2) call. The DRM core code then looks - * up the object based on the offset and sets up the various memory mapping - * structures. - * - * This routine allocates and attaches a fake offset for @obj, in cases where - * the virtual size differs from the physical size (ie. obj->size). Otherwise - * just use drm_gem_create_mmap_offset(). - */ -int -drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size) -{ - struct drm_device *dev = obj->dev; - - return drm_vma_offset_add(dev->vma_offset_manager, &obj->vma_node, - size / PAGE_SIZE); -} -EXPORT_SYMBOL(drm_gem_create_mmap_offset_size); - -/** - * drm_gem_create_mmap_offset - create a fake mmap offset for an object - * @obj: obj in question - * - * GEM memory mapping works by handing back to userspace a fake mmap offset - * it can use in a subsequent mmap(2) call. The DRM core code then looks - * up the object based on the offset and sets up the various memory mapping - * structures. - * - * This routine allocates and attaches a fake offset for @obj. - */ -int drm_gem_create_mmap_offset(struct drm_gem_object *obj) -{ - return drm_gem_create_mmap_offset_size(obj, obj->size); -} -EXPORT_SYMBOL(drm_gem_create_mmap_offset); - -/** - * drm_gem_get_pages - helper to allocate backing pages for a GEM object - * from shmem - * @obj: obj in question - * - * This reads the page-array of the shmem-backing storage of the given gem - * object. An array of pages is returned. If a page is not allocated or - * swapped-out, this will allocate/swap-in the required pages. Note that the - * whole object is covered by the page-array and pinned in memory. - * - * Use drm_gem_put_pages() to release the array and unpin all pages. - * - * This uses the GFP-mask set on the shmem-mapping (see mapping_set_gfp_mask()). - * If you require other GFP-masks, you have to do those allocations yourself. - * - * Note that you are not allowed to change gfp-zones during runtime. That is, - * shmem_read_mapping_page_gfp() must be called with the same gfp_zone(gfp) as - * set during initialization. If you have special zone constraints, set them - * after drm_gem_init_object() via mapping_set_gfp_mask(). shmem-core takes care - * to keep pages in the required zone during swap-in. - */ -struct page **drm_gem_get_pages(struct drm_gem_object *obj) -{ - struct address_space *mapping; - struct page *p, **pages; - int i, npages; - - /* This is the shared memory object that backs the GEM resource */ - mapping = file_inode(obj->filp)->i_mapping; - - /* We already BUG_ON() for non-page-aligned sizes in - * drm_gem_object_init(), so we should never hit this unless - * driver author is doing something really wrong: - */ - WARN_ON((obj->size & (PAGE_SIZE - 1)) != 0); - - npages = obj->size >> PAGE_SHIFT; - - pages = drm_malloc_ab(npages, sizeof(struct page *)); - if (pages == NULL) - return ERR_PTR(-ENOMEM); - - for (i = 0; i < npages; i++) { - p = shmem_read_mapping_page(mapping, i); - if (IS_ERR(p)) - goto fail; - pages[i] = p; - - /* Make sure shmem keeps __GFP_DMA32 allocated pages in the - * correct region during swapin. Note that this requires - * __GFP_DMA32 to be set in mapping_gfp_mask(inode->i_mapping) - * so shmem can relocate pages during swapin if required. - */ - BUG_ON(mapping_gfp_constraint(mapping, __GFP_DMA32) && - (page_to_pfn(p) >= 0x00100000UL)); - } - - return pages; - -fail: - while (i--) - page_cache_release(pages[i]); - - drm_free_large(pages); - return ERR_CAST(p); -} -EXPORT_SYMBOL(drm_gem_get_pages); - -/** - * drm_gem_put_pages - helper to free backing pages for a GEM object - * @obj: obj in question - * @pages: pages to free - * @dirty: if true, pages will be marked as dirty - * @accessed: if true, the pages will be marked as accessed - */ -void drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, - bool dirty, bool accessed) -{ - int i, npages; - - /* We already BUG_ON() for non-page-aligned sizes in - * drm_gem_object_init(), so we should never hit this unless - * driver author is doing something really wrong: - */ - WARN_ON((obj->size & (PAGE_SIZE - 1)) != 0); - - npages = obj->size >> PAGE_SHIFT; - - for (i = 0; i < npages; i++) { - if (dirty) - set_page_dirty(pages[i]); - - if (accessed) - mark_page_accessed(pages[i]); - - /* Undo the reference we took when populating the table */ - page_cache_release(pages[i]); - } - - drm_free_large(pages); -} -EXPORT_SYMBOL(drm_gem_put_pages); - -/** Returns a reference to the object named by the handle. */ -struct drm_gem_object * -drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp, - u32 handle) -{ - struct drm_gem_object *obj; - - spin_lock(&filp->table_lock); - - /* Check if we currently have a reference on the object */ - obj = idr_find(&filp->object_idr, handle); - if (obj == NULL) { - spin_unlock(&filp->table_lock); - return NULL; - } - - drm_gem_object_reference(obj); - - spin_unlock(&filp->table_lock); - - return obj; -} -EXPORT_SYMBOL(drm_gem_object_lookup); - -/** - * drm_gem_close_ioctl - implementation of the GEM_CLOSE ioctl - * @dev: drm_device - * @data: ioctl data - * @file_priv: drm file-private structure - * - * Releases the handle to an mm object. - */ -int -drm_gem_close_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_gem_close *args = data; - int ret; - - if (!drm_core_check_feature(dev, DRIVER_GEM)) - return -ENODEV; - - ret = drm_gem_handle_delete(file_priv, args->handle); - - return ret; -} - -/** - * drm_gem_flink_ioctl - implementation of the GEM_FLINK ioctl - * @dev: drm_device - * @data: ioctl data - * @file_priv: drm file-private structure - * - * Create a global name for an object, returning the name. - * - * Note that the name does not hold a reference; when the object - * is freed, the name goes away. - */ -int -drm_gem_flink_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_gem_flink *args = data; - struct drm_gem_object *obj; - int ret; - - if (!drm_core_check_feature(dev, DRIVER_GEM)) - return -ENODEV; - - obj = drm_gem_object_lookup(dev, file_priv, args->handle); - if (obj == NULL) - return -ENOENT; - - mutex_lock(&dev->object_name_lock); - idr_preload(GFP_KERNEL); - /* prevent races with concurrent gem_close. */ - if (obj->handle_count == 0) { - ret = -ENOENT; - goto err; - } - - if (!obj->name) { - ret = idr_alloc(&dev->object_name_idr, obj, 1, 0, GFP_NOWAIT); - if (ret < 0) - goto err; - - obj->name = ret; - } - - args->name = (uint64_t) obj->name; - ret = 0; - -err: - idr_preload_end(); - mutex_unlock(&dev->object_name_lock); - drm_gem_object_unreference_unlocked(obj); - return ret; -} - -/** - * drm_gem_open - implementation of the GEM_OPEN ioctl - * @dev: drm_device - * @data: ioctl data - * @file_priv: drm file-private structure - * - * Open an object using the global name, returning a handle and the size. - * - * This handle (of course) holds a reference to the object, so the object - * will not go away until the handle is deleted. - */ -int -drm_gem_open_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_gem_open *args = data; - struct drm_gem_object *obj; - int ret; - u32 handle; - - if (!drm_core_check_feature(dev, DRIVER_GEM)) - return -ENODEV; - - mutex_lock(&dev->object_name_lock); - obj = idr_find(&dev->object_name_idr, (int) args->name); - if (obj) { - drm_gem_object_reference(obj); - } else { - mutex_unlock(&dev->object_name_lock); - return -ENOENT; - } - - /* drm_gem_handle_create_tail unlocks dev->object_name_lock. */ - ret = drm_gem_handle_create_tail(file_priv, obj, &handle); - drm_gem_object_unreference_unlocked(obj); - if (ret) - return ret; - - args->handle = handle; - args->size = obj->size; - - return 0; -} - -/** - * gem_gem_open - initalizes GEM file-private structures at devnode open time - * @dev: drm_device which is being opened by userspace - * @file_private: drm file-private structure to set up - * - * Called at device open time, sets up the structure for handling refcounting - * of mm objects. - */ -void -drm_gem_open(struct drm_device *dev, struct drm_file *file_private) -{ - idr_init(&file_private->object_idr); - spin_lock_init(&file_private->table_lock); -} - -/* - * Called at device close to release the file's - * handle references on objects. - */ -static int -drm_gem_object_release_handle(int id, void *ptr, void *data) -{ - struct drm_file *file_priv = data; - struct drm_gem_object *obj = ptr; - struct drm_device *dev = obj->dev; - - if (dev->driver->gem_close_object) - dev->driver->gem_close_object(obj, file_priv); - - if (drm_core_check_feature(dev, DRIVER_PRIME)) - drm_gem_remove_prime_handles(obj, file_priv); - drm_vma_node_revoke(&obj->vma_node, file_priv->filp); - - drm_gem_object_handle_unreference_unlocked(obj); - - return 0; -} - -/** - * drm_gem_release - release file-private GEM resources - * @dev: drm_device which is being closed by userspace - * @file_private: drm file-private structure to clean up - * - * Called at close time when the filp is going away. - * - * Releases any remaining references on objects by this filp. - */ -void -drm_gem_release(struct drm_device *dev, struct drm_file *file_private) -{ - idr_for_each(&file_private->object_idr, - &drm_gem_object_release_handle, file_private); - idr_destroy(&file_private->object_idr); -} - -void -drm_gem_object_release(struct drm_gem_object *obj) -{ - WARN_ON(obj->dma_buf); - - if (obj->filp) - fput(obj->filp); - - drm_gem_free_mmap_offset(obj); -} -EXPORT_SYMBOL(drm_gem_object_release); - -/** - * drm_gem_object_free - free a GEM object - * @kref: kref of the object to free - * - * Called after the last reference to the object has been lost. - * Must be called holding struct_ mutex - * - * Frees the object - */ -void -drm_gem_object_free(struct kref *kref) -{ - struct drm_gem_object *obj = - container_of(kref, struct drm_gem_object, refcount); - struct drm_device *dev = obj->dev; - - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - - if (dev->driver->gem_free_object != NULL) - dev->driver->gem_free_object(obj); -} -EXPORT_SYMBOL(drm_gem_object_free); - -void drm_gem_vm_open(struct vm_area_struct *vma) -{ - struct drm_gem_object *obj = vma->vm_private_data; - - drm_gem_object_reference(obj); -} -EXPORT_SYMBOL(drm_gem_vm_open); - -void drm_gem_vm_close(struct vm_area_struct *vma) -{ - struct drm_gem_object *obj = vma->vm_private_data; - - drm_gem_object_unreference_unlocked(obj); -} -EXPORT_SYMBOL(drm_gem_vm_close); - -/** - * drm_gem_mmap_obj - memory map a GEM object - * @obj: the GEM object to map - * @obj_size: the object size to be mapped, in bytes - * @vma: VMA for the area to be mapped - * - * Set up the VMA to prepare mapping of the GEM object using the gem_vm_ops - * provided by the driver. Depending on their requirements, drivers can either - * provide a fault handler in their gem_vm_ops (in which case any accesses to - * the object will be trapped, to perform migration, GTT binding, surface - * register allocation, or performance monitoring), or mmap the buffer memory - * synchronously after calling drm_gem_mmap_obj. - * - * This function is mainly intended to implement the DMABUF mmap operation, when - * the GEM object is not looked up based on its fake offset. To implement the - * DRM mmap operation, drivers should use the drm_gem_mmap() function. - * - * drm_gem_mmap_obj() assumes the user is granted access to the buffer while - * drm_gem_mmap() prevents unprivileged users from mapping random objects. So - * callers must verify access restrictions before calling this helper. - * - * Return 0 or success or -EINVAL if the object size is smaller than the VMA - * size, or if no gem_vm_ops are provided. - */ -int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, - struct vm_area_struct *vma) -{ - struct drm_device *dev = obj->dev; - - /* Check for valid size. */ - if (obj_size < vma->vm_end - vma->vm_start) - return -EINVAL; - - if (!dev->driver->gem_vm_ops) - return -EINVAL; - - vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP; - vma->vm_ops = dev->driver->gem_vm_ops; - vma->vm_private_data = obj; - vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); - - /* Take a ref for this mapping of the object, so that the fault - * handler can dereference the mmap offset's pointer to the object. - * This reference is cleaned up by the corresponding vm_close - * (which should happen whether the vma was created by this call, or - * by a vm_open due to mremap or partial unmap or whatever). - */ - drm_gem_object_reference(obj); - - return 0; -} -EXPORT_SYMBOL(drm_gem_mmap_obj); - -/** - * drm_gem_mmap - memory map routine for GEM objects - * @filp: DRM file pointer - * @vma: VMA for the area to be mapped - * - * If a driver supports GEM object mapping, mmap calls on the DRM file - * descriptor will end up here. - * - * Look up the GEM object based on the offset passed in (vma->vm_pgoff will - * contain the fake offset we created when the GTT map ioctl was called on - * the object) and map it with a call to drm_gem_mmap_obj(). - * - * If the caller is not granted access to the buffer object, the mmap will fail - * with EACCES. Please see the vma manager for more information. - */ -int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct drm_file *priv = filp->private_data; - struct drm_device *dev = priv->minor->dev; - struct drm_gem_object *obj = NULL; - struct drm_vma_offset_node *node; - int ret; - - if (drm_device_is_unplugged(dev)) - return -ENODEV; - - drm_vma_offset_lock_lookup(dev->vma_offset_manager); - node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager, - vma->vm_pgoff, - vma_pages(vma)); - if (likely(node)) { - obj = container_of(node, struct drm_gem_object, vma_node); - /* - * When the object is being freed, after it hits 0-refcnt it - * proceeds to tear down the object. In the process it will - * attempt to remove the VMA offset and so acquire this - * mgr->vm_lock. Therefore if we find an object with a 0-refcnt - * that matches our range, we know it is in the process of being - * destroyed and will be freed as soon as we release the lock - - * so we have to check for the 0-refcnted object and treat it as - * invalid. - */ - if (!kref_get_unless_zero(&obj->refcount)) - obj = NULL; - } - drm_vma_offset_unlock_lookup(dev->vma_offset_manager); - - if (!obj) - return -EINVAL; - - if (!drm_vma_node_is_allowed(node, filp)) { - drm_gem_object_unreference_unlocked(obj); - return -EACCES; - } - - ret = drm_gem_mmap_obj(obj, drm_vma_node_size(node) << PAGE_SHIFT, - vma); - - drm_gem_object_unreference_unlocked(obj); - - return ret; -} -EXPORT_SYMBOL(drm_gem_mmap); diff --git a/src/4.x/drivers/gpu/drm/drm_gem_cma_helper.c b/src/4.x/drivers/gpu/drm/drm_gem_cma_helper.c deleted file mode 100644 index e109b49cd..000000000 --- a/src/4.x/drivers/gpu/drm/drm_gem_cma_helper.c +++ /dev/null @@ -1,532 +0,0 @@ -/* - * drm gem CMA (contiguous memory allocator) helper functions - * - * Copyright (C) 2012 Sascha Hauer, Pengutronix - * - * Based on Samsung Exynos code - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/** - * DOC: cma helpers - * - * The Contiguous Memory Allocator reserves a pool of memory at early boot - * that is used to service requests for large blocks of contiguous memory. - * - * The DRM GEM/CMA helpers use this allocator as a means to provide buffer - * objects that are physically contiguous in memory. This is useful for - * display drivers that are unable to map scattered buffers via an IOMMU. - */ - -/** - * __drm_gem_cma_create - Create a GEM CMA object without allocating memory - * @drm: DRM device - * @size: size of the object to allocate - * - * This function creates and initializes a GEM CMA object of the given size, - * but doesn't allocate any memory to back the object. - * - * Returns: - * A struct drm_gem_cma_object * on success or an ERR_PTR()-encoded negative - * error code on failure. - */ -static struct drm_gem_cma_object * -__drm_gem_cma_create(struct drm_device *drm, size_t size) -{ - struct drm_gem_cma_object *cma_obj; - struct drm_gem_object *gem_obj; - int ret; - - cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); - if (!cma_obj) - return ERR_PTR(-ENOMEM); - - gem_obj = &cma_obj->base; - - ret = drm_gem_object_init(drm, gem_obj, size); - if (ret) - goto error; - - ret = drm_gem_create_mmap_offset(gem_obj); - if (ret) { - drm_gem_object_release(gem_obj); - goto error; - } - - return cma_obj; - -error: - kfree(cma_obj); - return ERR_PTR(ret); -} - -/** - * drm_gem_cma_create - allocate an object with the given size - * @drm: DRM device - * @size: size of the object to allocate - * - * This function creates a CMA GEM object and allocates a contiguous chunk of - * memory as backing store. The backing memory has the writecombine attribute - * set. - * - * Returns: - * A struct drm_gem_cma_object * on success or an ERR_PTR()-encoded negative - * error code on failure. - */ -struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, - size_t size) -{ - struct drm_gem_cma_object *cma_obj; - int ret; - - size = round_up(size, PAGE_SIZE); - - cma_obj = __drm_gem_cma_create(drm, size); - if (IS_ERR(cma_obj)) - return cma_obj; - - cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size, - &cma_obj->paddr, GFP_KERNEL | __GFP_NOWARN); - if (!cma_obj->vaddr) { - dev_err(drm->dev, "failed to allocate buffer with size %zu\n", - size); - ret = -ENOMEM; - goto error; - } - - return cma_obj; - -error: - drm_gem_cma_free_object(&cma_obj->base); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(drm_gem_cma_create); - -/** - * drm_gem_cma_create_with_handle - allocate an object with the given size and - * return a GEM handle to it - * @file_priv: DRM file-private structure to register the handle for - * @drm: DRM device - * @size: size of the object to allocate - * @handle: return location for the GEM handle - * - * This function creates a CMA GEM object, allocating a physically contiguous - * chunk of memory as backing store. The GEM object is then added to the list - * of object associated with the given file and a handle to it is returned. - * - * Returns: - * A struct drm_gem_cma_object * on success or an ERR_PTR()-encoded negative - * error code on failure. - */ -static struct drm_gem_cma_object * -drm_gem_cma_create_with_handle(struct drm_file *file_priv, - struct drm_device *drm, size_t size, - uint32_t *handle) -{ - struct drm_gem_cma_object *cma_obj; - struct drm_gem_object *gem_obj; - int ret; - - cma_obj = drm_gem_cma_create(drm, size); - if (IS_ERR(cma_obj)) - return cma_obj; - - gem_obj = &cma_obj->base; - - /* - * allocate a id of idr table where the obj is registered - * and handle has the id what user can see. - */ - ret = drm_gem_handle_create(file_priv, gem_obj, handle); - if (ret) - goto err_handle_create; - - /* drop reference from allocate - handle holds it now. */ - drm_gem_object_unreference_unlocked(gem_obj); - - return cma_obj; - -err_handle_create: - drm_gem_cma_free_object(gem_obj); - - return ERR_PTR(ret); -} - -/** - * drm_gem_cma_free_object - free resources associated with a CMA GEM object - * @gem_obj: GEM object to free - * - * This function frees the backing memory of the CMA GEM object, cleans up the - * GEM object state and frees the memory used to store the object itself. - * Drivers using the CMA helpers should set this as their DRM driver's - * ->gem_free_object() callback. - */ -void drm_gem_cma_free_object(struct drm_gem_object *gem_obj) -{ - struct drm_gem_cma_object *cma_obj; - - cma_obj = to_drm_gem_cma_obj(gem_obj); - - if (cma_obj->vaddr) { - dma_free_writecombine(gem_obj->dev->dev, cma_obj->base.size, - cma_obj->vaddr, cma_obj->paddr); - } else if (gem_obj->import_attach) { - drm_prime_gem_destroy(gem_obj, cma_obj->sgt); - } - - drm_gem_object_release(gem_obj); - - kfree(cma_obj); -} -EXPORT_SYMBOL_GPL(drm_gem_cma_free_object); - -/** - * drm_gem_cma_dumb_create_internal - create a dumb buffer object - * @file_priv: DRM file-private structure to create the dumb buffer for - * @drm: DRM device - * @args: IOCTL data - * - * This aligns the pitch and size arguments to the minimum required. This is - * an internal helper that can be wrapped by a driver to account for hardware - * with more specific alignment requirements. It should not be used directly - * as the ->dumb_create() callback in a DRM driver. - * - * Returns: - * 0 on success or a negative error code on failure. - */ -int drm_gem_cma_dumb_create_internal(struct drm_file *file_priv, - struct drm_device *drm, - struct drm_mode_create_dumb *args) -{ - unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); - struct drm_gem_cma_object *cma_obj; - - if (args->pitch < min_pitch) - args->pitch = min_pitch; - - if (args->size < args->pitch * args->height) - args->size = args->pitch * args->height; - - cma_obj = drm_gem_cma_create_with_handle(file_priv, drm, args->size, - &args->handle); - return PTR_ERR_OR_ZERO(cma_obj); -} -EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create_internal); - -/** - * drm_gem_cma_dumb_create - create a dumb buffer object - * @file_priv: DRM file-private structure to create the dumb buffer for - * @drm: DRM device - * @args: IOCTL data - * - * This function computes the pitch of the dumb buffer and rounds it up to an - * integer number of bytes per pixel. Drivers for hardware that doesn't have - * any additional restrictions on the pitch can directly use this function as - * their ->dumb_create() callback. - * - * For hardware with additional restrictions, drivers can adjust the fields - * set up by userspace and pass the IOCTL data along to the - * drm_gem_cma_dumb_create_internal() function. - * - * Returns: - * 0 on success or a negative error code on failure. - */ -int drm_gem_cma_dumb_create(struct drm_file *file_priv, - struct drm_device *drm, - struct drm_mode_create_dumb *args) -{ - struct drm_gem_cma_object *cma_obj; - - args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8); - args->size = args->pitch * args->height; - - cma_obj = drm_gem_cma_create_with_handle(file_priv, drm, args->size, - &args->handle); - return PTR_ERR_OR_ZERO(cma_obj); -} -EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create); - -/** - * drm_gem_cma_dumb_map_offset - return the fake mmap offset for a CMA GEM - * object - * @file_priv: DRM file-private structure containing the GEM object - * @drm: DRM device - * @handle: GEM object handle - * @offset: return location for the fake mmap offset - * - * This function look up an object by its handle and returns the fake mmap - * offset associated with it. Drivers using the CMA helpers should set this - * as their DRM driver's ->dumb_map_offset() callback. - * - * Returns: - * 0 on success or a negative error code on failure. - */ -int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv, - struct drm_device *drm, u32 handle, - u64 *offset) -{ - struct drm_gem_object *gem_obj; - - gem_obj = drm_gem_object_lookup(drm, file_priv, handle); - if (!gem_obj) { - dev_err(drm->dev, "failed to lookup GEM object\n"); - return -EINVAL; - } - - *offset = drm_vma_node_offset_addr(&gem_obj->vma_node); - - drm_gem_object_unreference_unlocked(gem_obj); - - return 0; -} -EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_map_offset); - -const struct vm_operations_struct drm_gem_cma_vm_ops = { - .open = drm_gem_vm_open, - .close = drm_gem_vm_close, -}; -EXPORT_SYMBOL_GPL(drm_gem_cma_vm_ops); - -static int drm_gem_cma_mmap_obj(struct drm_gem_cma_object *cma_obj, - struct vm_area_struct *vma) -{ - int ret; - - /* - * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the - * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map - * the whole buffer. - */ - vma->vm_flags &= ~VM_PFNMAP; - vma->vm_pgoff = 0; - - ret = dma_mmap_writecombine(cma_obj->base.dev->dev, vma, - cma_obj->vaddr, cma_obj->paddr, - vma->vm_end - vma->vm_start); - if (ret) - drm_gem_vm_close(vma); - - return ret; -} - -/** - * drm_gem_cma_mmap - memory-map a CMA GEM object - * @filp: file object - * @vma: VMA for the area to be mapped - * - * This function implements an augmented version of the GEM DRM file mmap - * operation for CMA objects: In addition to the usual GEM VMA setup it - * immediately faults in the entire object instead of using on-demaind - * faulting. Drivers which employ the CMA helpers should use this function - * as their ->mmap() handler in the DRM device file's file_operations - * structure. - * - * Returns: - * 0 on success or a negative error code on failure. - */ -int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct drm_gem_cma_object *cma_obj; - struct drm_gem_object *gem_obj; - int ret; - - ret = drm_gem_mmap(filp, vma); - if (ret) - return ret; - - gem_obj = vma->vm_private_data; - cma_obj = to_drm_gem_cma_obj(gem_obj); - - return drm_gem_cma_mmap_obj(cma_obj, vma); -} -EXPORT_SYMBOL_GPL(drm_gem_cma_mmap); - -#ifdef CONFIG_DEBUG_FS -/** - * drm_gem_cma_describe - describe a CMA GEM object for debugfs - * @cma_obj: CMA GEM object - * @m: debugfs file handle - * - * This function can be used to dump a human-readable representation of the - * CMA GEM object into a synthetic file. - */ -void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, - struct seq_file *m) -{ - struct drm_gem_object *obj = &cma_obj->base; - uint64_t off; - - off = drm_vma_node_start(&obj->vma_node); - - seq_printf(m, "%2d (%2d) %08llx %pad %p %zu", - obj->name, obj->refcount.refcount.counter, - off, &cma_obj->paddr, cma_obj->vaddr, obj->size); - - seq_printf(m, "\n"); -} -EXPORT_SYMBOL_GPL(drm_gem_cma_describe); -#endif - -/** - * drm_gem_cma_prime_get_sg_table - provide a scatter/gather table of pinned - * pages for a CMA GEM object - * @obj: GEM object - * - * This function exports a scatter/gather table suitable for PRIME usage by - * calling the standard DMA mapping API. Drivers using the CMA helpers should - * set this as their DRM driver's ->gem_prime_get_sg_table() callback. - * - * Returns: - * A pointer to the scatter/gather table of pinned pages or NULL on failure. - */ -struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj) -{ - struct drm_gem_cma_object *cma_obj = to_drm_gem_cma_obj(obj); - struct sg_table *sgt; - int ret; - - sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); - if (!sgt) - return NULL; - - ret = dma_get_sgtable(obj->dev->dev, sgt, cma_obj->vaddr, - cma_obj->paddr, obj->size); - if (ret < 0) - goto out; - - return sgt; - -out: - kfree(sgt); - return NULL; -} -EXPORT_SYMBOL_GPL(drm_gem_cma_prime_get_sg_table); - -/** - * drm_gem_cma_prime_import_sg_table - produce a CMA GEM object from another - * driver's scatter/gather table of pinned pages - * @dev: device to import into - * @attach: DMA-BUF attachment - * @sgt: scatter/gather table of pinned pages - * - * This function imports a scatter/gather table exported via DMA-BUF by - * another driver. Imported buffers must be physically contiguous in memory - * (i.e. the scatter/gather table must contain a single entry). Drivers that - * use the CMA helpers should set this as their DRM driver's - * ->gem_prime_import_sg_table() callback. - * - * Returns: - * A pointer to a newly created GEM object or an ERR_PTR-encoded negative - * error code on failure. - */ -struct drm_gem_object * -drm_gem_cma_prime_import_sg_table(struct drm_device *dev, - struct dma_buf_attachment *attach, - struct sg_table *sgt) -{ - struct drm_gem_cma_object *cma_obj; - - if (sgt->nents != 1) - return ERR_PTR(-EINVAL); - - /* Create a CMA GEM buffer. */ - cma_obj = __drm_gem_cma_create(dev, attach->dmabuf->size); - if (IS_ERR(cma_obj)) - return ERR_CAST(cma_obj); - - cma_obj->paddr = sg_dma_address(sgt->sgl); - cma_obj->sgt = sgt; - - DRM_DEBUG_PRIME("dma_addr = %pad, size = %zu\n", &cma_obj->paddr, attach->dmabuf->size); - - return &cma_obj->base; -} -EXPORT_SYMBOL_GPL(drm_gem_cma_prime_import_sg_table); - -/** - * drm_gem_cma_prime_mmap - memory-map an exported CMA GEM object - * @obj: GEM object - * @vma: VMA for the area to be mapped - * - * This function maps a buffer imported via DRM PRIME into a userspace - * process's address space. Drivers that use the CMA helpers should set this - * as their DRM driver's ->gem_prime_mmap() callback. - * - * Returns: - * 0 on success or a negative error code on failure. - */ -int drm_gem_cma_prime_mmap(struct drm_gem_object *obj, - struct vm_area_struct *vma) -{ - struct drm_gem_cma_object *cma_obj; - int ret; - - ret = drm_gem_mmap_obj(obj, obj->size, vma); - if (ret < 0) - return ret; - - cma_obj = to_drm_gem_cma_obj(obj); - return drm_gem_cma_mmap_obj(cma_obj, vma); -} -EXPORT_SYMBOL_GPL(drm_gem_cma_prime_mmap); - -/** - * drm_gem_cma_prime_vmap - map a CMA GEM object into the kernel's virtual - * address space - * @obj: GEM object - * - * This function maps a buffer exported via DRM PRIME into the kernel's - * virtual address space. Since the CMA buffers are already mapped into the - * kernel virtual address space this simply returns the cached virtual - * address. Drivers using the CMA helpers should set this as their DRM - * driver's ->gem_prime_vmap() callback. - * - * Returns: - * The kernel virtual address of the CMA GEM object's backing store. - */ -void *drm_gem_cma_prime_vmap(struct drm_gem_object *obj) -{ - struct drm_gem_cma_object *cma_obj = to_drm_gem_cma_obj(obj); - - return cma_obj->vaddr; -} -EXPORT_SYMBOL_GPL(drm_gem_cma_prime_vmap); - -/** - * drm_gem_cma_prime_vunmap - unmap a CMA GEM object from the kernel's virtual - * address space - * @obj: GEM object - * @vaddr: kernel virtual address where the CMA GEM object was mapped - * - * This function removes a buffer exported via DRM PRIME from the kernel's - * virtual address space. This is a no-op because CMA buffers cannot be - * unmapped from kernel space. Drivers using the CMA helpers should set this - * as their DRM driver's ->gem_prime_vunmap() callback. - */ -void drm_gem_cma_prime_vunmap(struct drm_gem_object *obj, void *vaddr) -{ - /* Nothing to do */ -} -EXPORT_SYMBOL_GPL(drm_gem_cma_prime_vunmap); diff --git a/src/4.x/drivers/gpu/drm/drm_global.c b/src/4.x/drivers/gpu/drm/drm_global.c deleted file mode 100644 index 3d2e91c4d..000000000 --- a/src/4.x/drivers/gpu/drm/drm_global.c +++ /dev/null @@ -1,110 +0,0 @@ -/************************************************************************** - * - * Copyright 2008-2009 VMware, Inc., Palo Alto, CA., USA - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ -/* - * Authors: Thomas Hellstrom - */ - -#include -#include -#include -#include - -struct drm_global_item { - struct mutex mutex; - void *object; - int refcount; -}; - -static struct drm_global_item glob[DRM_GLOBAL_NUM]; - -void drm_global_init(void) -{ - int i; - - for (i = 0; i < DRM_GLOBAL_NUM; ++i) { - struct drm_global_item *item = &glob[i]; - mutex_init(&item->mutex); - item->object = NULL; - item->refcount = 0; - } -} - -void drm_global_release(void) -{ - int i; - for (i = 0; i < DRM_GLOBAL_NUM; ++i) { - struct drm_global_item *item = &glob[i]; - BUG_ON(item->object != NULL); - BUG_ON(item->refcount != 0); - } -} - -int drm_global_item_ref(struct drm_global_reference *ref) -{ - int ret; - struct drm_global_item *item = &glob[ref->global_type]; - - mutex_lock(&item->mutex); - if (item->refcount == 0) { - item->object = kzalloc(ref->size, GFP_KERNEL); - if (unlikely(item->object == NULL)) { - ret = -ENOMEM; - goto out_err; - } - - ref->object = item->object; - ret = ref->init(ref); - if (unlikely(ret != 0)) - goto out_err; - - } - ++item->refcount; - ref->object = item->object; - mutex_unlock(&item->mutex); - return 0; -out_err: - mutex_unlock(&item->mutex); - item->object = NULL; - return ret; -} -EXPORT_SYMBOL(drm_global_item_ref); - -void drm_global_item_unref(struct drm_global_reference *ref) -{ - struct drm_global_item *item = &glob[ref->global_type]; - - mutex_lock(&item->mutex); - BUG_ON(item->refcount == 0); - BUG_ON(ref->object != item->object); - if (--item->refcount == 0) { - ref->release(ref); - item->object = NULL; - } - mutex_unlock(&item->mutex); -} -EXPORT_SYMBOL(drm_global_item_unref); - diff --git a/src/4.x/drivers/gpu/drm/drm_hashtab.c b/src/4.x/drivers/gpu/drm/drm_hashtab.c deleted file mode 100644 index c3b80fd65..000000000 --- a/src/4.x/drivers/gpu/drm/drm_hashtab.c +++ /dev/null @@ -1,208 +0,0 @@ -/************************************************************************** - * - * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * - **************************************************************************/ -/* - * Simple open hash tab implementation. - * - * Authors: - * Thomas Hellström - */ - -#include -#include -#include -#include -#include - -int drm_ht_create(struct drm_open_hash *ht, unsigned int order) -{ - unsigned int size = 1 << order; - - ht->order = order; - ht->table = NULL; - if (size <= PAGE_SIZE / sizeof(*ht->table)) - ht->table = kcalloc(size, sizeof(*ht->table), GFP_KERNEL); - else - ht->table = vzalloc(size*sizeof(*ht->table)); - if (!ht->table) { - DRM_ERROR("Out of memory for hash table\n"); - return -ENOMEM; - } - return 0; -} -EXPORT_SYMBOL(drm_ht_create); - -void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key) -{ - struct drm_hash_item *entry; - struct hlist_head *h_list; - unsigned int hashed_key; - int count = 0; - - hashed_key = hash_long(key, ht->order); - DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key); - h_list = &ht->table[hashed_key]; - hlist_for_each_entry(entry, h_list, head) - DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key); -} - -static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht, - unsigned long key) -{ - struct drm_hash_item *entry; - struct hlist_head *h_list; - unsigned int hashed_key; - - hashed_key = hash_long(key, ht->order); - h_list = &ht->table[hashed_key]; - hlist_for_each_entry(entry, h_list, head) { - if (entry->key == key) - return &entry->head; - if (entry->key > key) - break; - } - return NULL; -} - -static struct hlist_node *drm_ht_find_key_rcu(struct drm_open_hash *ht, - unsigned long key) -{ - struct drm_hash_item *entry; - struct hlist_head *h_list; - unsigned int hashed_key; - - hashed_key = hash_long(key, ht->order); - h_list = &ht->table[hashed_key]; - hlist_for_each_entry_rcu(entry, h_list, head) { - if (entry->key == key) - return &entry->head; - if (entry->key > key) - break; - } - return NULL; -} - -int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item) -{ - struct drm_hash_item *entry; - struct hlist_head *h_list; - struct hlist_node *parent; - unsigned int hashed_key; - unsigned long key = item->key; - - hashed_key = hash_long(key, ht->order); - h_list = &ht->table[hashed_key]; - parent = NULL; - hlist_for_each_entry(entry, h_list, head) { - if (entry->key == key) - return -EINVAL; - if (entry->key > key) - break; - parent = &entry->head; - } - if (parent) { - hlist_add_behind_rcu(&item->head, parent); - } else { - hlist_add_head_rcu(&item->head, h_list); - } - return 0; -} -EXPORT_SYMBOL(drm_ht_insert_item); - -/* - * Just insert an item and return any "bits" bit key that hasn't been - * used before. - */ -int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *item, - unsigned long seed, int bits, int shift, - unsigned long add) -{ - int ret; - unsigned long mask = (1 << bits) - 1; - unsigned long first, unshifted_key; - - unshifted_key = hash_long(seed, bits); - first = unshifted_key; - do { - item->key = (unshifted_key << shift) + add; - ret = drm_ht_insert_item(ht, item); - if (ret) - unshifted_key = (unshifted_key + 1) & mask; - } while(ret && (unshifted_key != first)); - - if (ret) { - DRM_ERROR("Available key bit space exhausted\n"); - return -EINVAL; - } - return 0; -} -EXPORT_SYMBOL(drm_ht_just_insert_please); - -int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key, - struct drm_hash_item **item) -{ - struct hlist_node *list; - - list = drm_ht_find_key_rcu(ht, key); - if (!list) - return -EINVAL; - - *item = hlist_entry(list, struct drm_hash_item, head); - return 0; -} -EXPORT_SYMBOL(drm_ht_find_item); - -int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key) -{ - struct hlist_node *list; - - list = drm_ht_find_key(ht, key); - if (list) { - hlist_del_init_rcu(list); - return 0; - } - return -EINVAL; -} - -int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item) -{ - hlist_del_init_rcu(&item->head); - return 0; -} -EXPORT_SYMBOL(drm_ht_remove_item); - -void drm_ht_remove(struct drm_open_hash *ht) -{ - if (ht->table) { - if ((PAGE_SIZE / sizeof(*ht->table)) >> ht->order) - kfree(ht->table); - else - vfree(ht->table); - ht->table = NULL; - } -} -EXPORT_SYMBOL(drm_ht_remove); diff --git a/src/4.x/drivers/gpu/drm/drm_info.c b/src/4.x/drivers/gpu/drm/drm_info.c deleted file mode 100644 index cbb4fc0fc..000000000 --- a/src/4.x/drivers/gpu/drm/drm_info.c +++ /dev/null @@ -1,222 +0,0 @@ -/** - * \file drm_info.c - * DRM info file implementations - * - * \author Ben Gamari - */ - -/* - * Created: Sun Dec 21 13:09:50 2008 by bgamari@gmail.com - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * Copyright 2008 Ben Gamari - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include - -#include "drm_internal.h" -#include "drm_legacy.h" - -/** - * Called when "/proc/dri/.../name" is read. - * - * Prints the device name together with the bus id if available. - */ -int drm_name_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_minor *minor = node->minor; - struct drm_device *dev = minor->dev; - struct drm_master *master = minor->master; - if (!master) - return 0; - - if (master->unique) { - seq_printf(m, "%s %s %s\n", - dev->driver->name, - dev_name(dev->dev), master->unique); - } else { - seq_printf(m, "%s %s\n", - dev->driver->name, dev_name(dev->dev)); - } - return 0; -} - -/** - * Called when "/proc/dri/.../vm" is read. - * - * Prints information about all mappings in drm_device::maplist. - */ -int drm_vm_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_local_map *map; - struct drm_map_list *r_list; - - /* Hardcoded from _DRM_FRAME_BUFFER, - _DRM_REGISTERS, _DRM_SHM, _DRM_AGP, and - _DRM_SCATTER_GATHER and _DRM_CONSISTENT */ - const char *types[] = { "FB", "REG", "SHM", "AGP", "SG", "PCI" }; - const char *type; - int i; - - mutex_lock(&dev->struct_mutex); - seq_printf(m, "slot offset size type flags address mtrr\n\n"); - i = 0; - list_for_each_entry(r_list, &dev->maplist, head) { - map = r_list->map; - if (!map) - continue; - if (map->type < 0 || map->type > 5) - type = "??"; - else - type = types[map->type]; - - seq_printf(m, "%4d 0x%016llx 0x%08lx %4.4s 0x%02x 0x%08lx ", - i, - (unsigned long long)map->offset, - map->size, type, map->flags, - (unsigned long) r_list->user_token); - if (map->mtrr < 0) - seq_printf(m, "none\n"); - else - seq_printf(m, "%4d\n", map->mtrr); - i++; - } - mutex_unlock(&dev->struct_mutex); - return 0; -} - -/** - * Called when "/proc/dri/.../bufs" is read. - */ -int drm_bufs_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_device_dma *dma; - int i, seg_pages; - - mutex_lock(&dev->struct_mutex); - dma = dev->dma; - if (!dma) { - mutex_unlock(&dev->struct_mutex); - return 0; - } - - seq_printf(m, " o size count free segs pages kB\n\n"); - for (i = 0; i <= DRM_MAX_ORDER; i++) { - if (dma->bufs[i].buf_count) { - seg_pages = dma->bufs[i].seg_count * (1 << dma->bufs[i].page_order); - seq_printf(m, "%2d %8d %5d %5d %5d %5d %5ld\n", - i, - dma->bufs[i].buf_size, - dma->bufs[i].buf_count, - 0, - dma->bufs[i].seg_count, - seg_pages, - seg_pages * PAGE_SIZE / 1024); - } - } - seq_printf(m, "\n"); - for (i = 0; i < dma->buf_count; i++) { - if (i && !(i % 32)) - seq_printf(m, "\n"); - seq_printf(m, " %d", dma->buflist[i]->list); - } - seq_printf(m, "\n"); - mutex_unlock(&dev->struct_mutex); - return 0; -} - -/** - * Called when "/proc/dri/.../clients" is read. - * - */ -int drm_clients_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_file *priv; - - seq_printf(m, - "%20s %5s %3s master a %5s %10s\n", - "command", - "pid", - "dev", - "uid", - "magic"); - - /* dev->filelist is sorted youngest first, but we want to present - * oldest first (i.e. kernel, servers, clients), so walk backwardss. - */ - mutex_lock(&dev->struct_mutex); - list_for_each_entry_reverse(priv, &dev->filelist, lhead) { - struct task_struct *task; - - rcu_read_lock(); /* locks pid_task()->comm */ - task = pid_task(priv->pid, PIDTYPE_PID); - seq_printf(m, "%20s %5d %3d %c %c %5d %10u\n", - task ? task->comm : "", - pid_vnr(priv->pid), - priv->minor->index, - priv->is_master ? 'y' : 'n', - priv->authenticated ? 'y' : 'n', - from_kuid_munged(seq_user_ns(m), priv->uid), - priv->magic); - rcu_read_unlock(); - } - mutex_unlock(&dev->struct_mutex); - return 0; -} - - -static int drm_gem_one_name_info(int id, void *ptr, void *data) -{ - struct drm_gem_object *obj = ptr; - struct seq_file *m = data; - - seq_printf(m, "%6d %8zd %7d %8d\n", - obj->name, obj->size, - obj->handle_count, - atomic_read(&obj->refcount.refcount)); - return 0; -} - -int drm_gem_name_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - - seq_printf(m, " name size handles refcount\n"); - - mutex_lock(&dev->object_name_lock); - idr_for_each(&dev->object_name_idr, drm_gem_one_name_info, m); - mutex_unlock(&dev->object_name_lock); - - return 0; -} diff --git a/src/4.x/drivers/gpu/drm/drm_internal.h b/src/4.x/drivers/gpu/drm/drm_internal.h deleted file mode 100644 index 43cbda330..000000000 --- a/src/4.x/drivers/gpu/drm/drm_internal.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright © 2014 Intel Corporation - * Daniel Vetter - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* drm_irq.c */ -extern unsigned int drm_timestamp_monotonic; - -/* drm_fops.c */ -extern struct mutex drm_global_mutex; -int drm_lastclose(struct drm_device *dev); - -/* drm_pci.c */ -int drm_pci_set_unique(struct drm_device *dev, - struct drm_master *master, - struct drm_unique *u); -int drm_irq_by_busid(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -/* drm_vm.c */ -int drm_vma_info(struct seq_file *m, void *data); -void drm_vm_open_locked(struct drm_device *dev, struct vm_area_struct *vma); -void drm_vm_close_locked(struct drm_device *dev, struct vm_area_struct *vma); - -/* drm_prime.c */ -int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); -void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); -void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpriv, - struct dma_buf *dma_buf); - -/* drm_info.c */ -int drm_name_info(struct seq_file *m, void *data); -int drm_vm_info(struct seq_file *m, void *data); -int drm_bufs_info(struct seq_file *m, void *data); -int drm_clients_info(struct seq_file *m, void* data); -int drm_gem_name_info(struct seq_file *m, void *data); - -/* drm_irq.c */ -int drm_control(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_modeset_ctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -/* drm_auth.c */ -int drm_getmagic(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_authmagic(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -/* drm_sysfs.c */ -extern struct class *drm_class; - -int drm_sysfs_init(void); -void drm_sysfs_destroy(void); -struct device *drm_sysfs_minor_alloc(struct drm_minor *minor); -int drm_sysfs_connector_add(struct drm_connector *connector); -void drm_sysfs_connector_remove(struct drm_connector *connector); - -/* drm_gem.c */ -int drm_gem_init(struct drm_device *dev); -void drm_gem_destroy(struct drm_device *dev); -int drm_gem_handle_create_tail(struct drm_file *file_priv, - struct drm_gem_object *obj, - u32 *handlep); -int drm_gem_close_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_gem_flink_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_gem_open_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -void drm_gem_open(struct drm_device *dev, struct drm_file *file_private); -void drm_gem_release(struct drm_device *dev, struct drm_file *file_private); - -/* drm_drv.c */ -int drm_setmaster_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_dropmaster_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -struct drm_master *drm_master_create(struct drm_minor *minor); - -/* drm_debugfs.c */ -#if defined(CONFIG_DEBUG_FS) -int drm_debugfs_init(struct drm_minor *minor, int minor_id, - struct dentry *root); -int drm_debugfs_cleanup(struct drm_minor *minor); -int drm_debugfs_connector_add(struct drm_connector *connector); -void drm_debugfs_connector_remove(struct drm_connector *connector); -#else -static inline int drm_debugfs_init(struct drm_minor *minor, int minor_id, - struct dentry *root) -{ - return 0; -} - -static inline int drm_debugfs_cleanup(struct drm_minor *minor) -{ - return 0; -} - -static inline int drm_debugfs_connector_add(struct drm_connector *connector) -{ - return 0; -} -static inline void drm_debugfs_connector_remove(struct drm_connector *connector) -{ -} -#endif diff --git a/src/4.x/drivers/gpu/drm/drm_ioc32.c b/src/4.x/drivers/gpu/drm/drm_ioc32.c deleted file mode 100644 index a6289752b..000000000 --- a/src/4.x/drivers/gpu/drm/drm_ioc32.c +++ /dev/null @@ -1,1146 +0,0 @@ -/** - * \file drm_ioc32.c - * - * 32-bit ioctl compatibility routines for the DRM. - * - * \author Paul Mackerras - * - * Copyright (C) Paul Mackerras 2005. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#include -#include -#include - -#include -#include - -#define DRM_IOCTL_VERSION32 DRM_IOWR(0x00, drm_version32_t) -#define DRM_IOCTL_GET_UNIQUE32 DRM_IOWR(0x01, drm_unique32_t) -#define DRM_IOCTL_GET_MAP32 DRM_IOWR(0x04, drm_map32_t) -#define DRM_IOCTL_GET_CLIENT32 DRM_IOWR(0x05, drm_client32_t) -#define DRM_IOCTL_GET_STATS32 DRM_IOR( 0x06, drm_stats32_t) - -#define DRM_IOCTL_SET_UNIQUE32 DRM_IOW( 0x10, drm_unique32_t) -#define DRM_IOCTL_ADD_MAP32 DRM_IOWR(0x15, drm_map32_t) -#define DRM_IOCTL_ADD_BUFS32 DRM_IOWR(0x16, drm_buf_desc32_t) -#define DRM_IOCTL_MARK_BUFS32 DRM_IOW( 0x17, drm_buf_desc32_t) -#define DRM_IOCTL_INFO_BUFS32 DRM_IOWR(0x18, drm_buf_info32_t) -#define DRM_IOCTL_MAP_BUFS32 DRM_IOWR(0x19, drm_buf_map32_t) -#define DRM_IOCTL_FREE_BUFS32 DRM_IOW( 0x1a, drm_buf_free32_t) - -#define DRM_IOCTL_RM_MAP32 DRM_IOW( 0x1b, drm_map32_t) - -#define DRM_IOCTL_SET_SAREA_CTX32 DRM_IOW( 0x1c, drm_ctx_priv_map32_t) -#define DRM_IOCTL_GET_SAREA_CTX32 DRM_IOWR(0x1d, drm_ctx_priv_map32_t) - -#define DRM_IOCTL_RES_CTX32 DRM_IOWR(0x26, drm_ctx_res32_t) -#define DRM_IOCTL_DMA32 DRM_IOWR(0x29, drm_dma32_t) - -#define DRM_IOCTL_AGP_ENABLE32 DRM_IOW( 0x32, drm_agp_mode32_t) -#define DRM_IOCTL_AGP_INFO32 DRM_IOR( 0x33, drm_agp_info32_t) -#define DRM_IOCTL_AGP_ALLOC32 DRM_IOWR(0x34, drm_agp_buffer32_t) -#define DRM_IOCTL_AGP_FREE32 DRM_IOW( 0x35, drm_agp_buffer32_t) -#define DRM_IOCTL_AGP_BIND32 DRM_IOW( 0x36, drm_agp_binding32_t) -#define DRM_IOCTL_AGP_UNBIND32 DRM_IOW( 0x37, drm_agp_binding32_t) - -#define DRM_IOCTL_SG_ALLOC32 DRM_IOW( 0x38, drm_scatter_gather32_t) -#define DRM_IOCTL_SG_FREE32 DRM_IOW( 0x39, drm_scatter_gather32_t) - -#define DRM_IOCTL_UPDATE_DRAW32 DRM_IOW( 0x3f, drm_update_draw32_t) - -#define DRM_IOCTL_WAIT_VBLANK32 DRM_IOWR(0x3a, drm_wait_vblank32_t) - -#define DRM_IOCTL_MODE_ADDFB232 DRM_IOWR(0xb8, drm_mode_fb_cmd232_t) - -typedef struct drm_version_32 { - int version_major; /**< Major version */ - int version_minor; /**< Minor version */ - int version_patchlevel; /**< Patch level */ - u32 name_len; /**< Length of name buffer */ - u32 name; /**< Name of driver */ - u32 date_len; /**< Length of date buffer */ - u32 date; /**< User-space buffer to hold date */ - u32 desc_len; /**< Length of desc buffer */ - u32 desc; /**< User-space buffer to hold desc */ -} drm_version32_t; - -static int compat_drm_version(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_version32_t v32; - struct drm_version __user *version; - int err; - - if (copy_from_user(&v32, (void __user *)arg, sizeof(v32))) - return -EFAULT; - - version = compat_alloc_user_space(sizeof(*version)); - if (!version) - return -EFAULT; - if (__put_user(v32.name_len, &version->name_len) - || __put_user((void __user *)(unsigned long)v32.name, - &version->name) - || __put_user(v32.date_len, &version->date_len) - || __put_user((void __user *)(unsigned long)v32.date, - &version->date) - || __put_user(v32.desc_len, &version->desc_len) - || __put_user((void __user *)(unsigned long)v32.desc, - &version->desc)) - return -EFAULT; - - err = drm_ioctl(file, - DRM_IOCTL_VERSION, (unsigned long)version); - if (err) - return err; - - if (__get_user(v32.version_major, &version->version_major) - || __get_user(v32.version_minor, &version->version_minor) - || __get_user(v32.version_patchlevel, &version->version_patchlevel) - || __get_user(v32.name_len, &version->name_len) - || __get_user(v32.date_len, &version->date_len) - || __get_user(v32.desc_len, &version->desc_len)) - return -EFAULT; - - if (copy_to_user((void __user *)arg, &v32, sizeof(v32))) - return -EFAULT; - return 0; -} - -typedef struct drm_unique32 { - u32 unique_len; /**< Length of unique */ - u32 unique; /**< Unique name for driver instantiation */ -} drm_unique32_t; - -static int compat_drm_getunique(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_unique32_t uq32; - struct drm_unique __user *u; - int err; - - if (copy_from_user(&uq32, (void __user *)arg, sizeof(uq32))) - return -EFAULT; - - u = compat_alloc_user_space(sizeof(*u)); - if (!u) - return -EFAULT; - if (__put_user(uq32.unique_len, &u->unique_len) - || __put_user((void __user *)(unsigned long)uq32.unique, - &u->unique)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_GET_UNIQUE, (unsigned long)u); - if (err) - return err; - - if (__get_user(uq32.unique_len, &u->unique_len)) - return -EFAULT; - if (copy_to_user((void __user *)arg, &uq32, sizeof(uq32))) - return -EFAULT; - return 0; -} - -static int compat_drm_setunique(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_unique32_t uq32; - struct drm_unique __user *u; - - if (copy_from_user(&uq32, (void __user *)arg, sizeof(uq32))) - return -EFAULT; - - u = compat_alloc_user_space(sizeof(*u)); - if (!u) - return -EFAULT; - if (__put_user(uq32.unique_len, &u->unique_len) - || __put_user((void __user *)(unsigned long)uq32.unique, - &u->unique)) - return -EFAULT; - - return drm_ioctl(file, DRM_IOCTL_SET_UNIQUE, (unsigned long)u); -} - -typedef struct drm_map32 { - u32 offset; /**< Requested physical address (0 for SAREA)*/ - u32 size; /**< Requested physical size (bytes) */ - enum drm_map_type type; /**< Type of memory to map */ - enum drm_map_flags flags; /**< Flags */ - u32 handle; /**< User-space: "Handle" to pass to mmap() */ - int mtrr; /**< MTRR slot used */ -} drm_map32_t; - -static int compat_drm_getmap(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_map32_t __user *argp = (void __user *)arg; - drm_map32_t m32; - struct drm_map __user *map; - int idx, err; - void *handle; - - if (get_user(idx, &argp->offset)) - return -EFAULT; - - map = compat_alloc_user_space(sizeof(*map)); - if (!map) - return -EFAULT; - if (__put_user(idx, &map->offset)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_GET_MAP, (unsigned long)map); - if (err) - return err; - - if (__get_user(m32.offset, &map->offset) - || __get_user(m32.size, &map->size) - || __get_user(m32.type, &map->type) - || __get_user(m32.flags, &map->flags) - || __get_user(handle, &map->handle) - || __get_user(m32.mtrr, &map->mtrr)) - return -EFAULT; - - m32.handle = (unsigned long)handle; - if (copy_to_user(argp, &m32, sizeof(m32))) - return -EFAULT; - return 0; - -} - -static int compat_drm_addmap(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_map32_t __user *argp = (void __user *)arg; - drm_map32_t m32; - struct drm_map __user *map; - int err; - void *handle; - - if (copy_from_user(&m32, argp, sizeof(m32))) - return -EFAULT; - - map = compat_alloc_user_space(sizeof(*map)); - if (!map) - return -EFAULT; - if (__put_user(m32.offset, &map->offset) - || __put_user(m32.size, &map->size) - || __put_user(m32.type, &map->type) - || __put_user(m32.flags, &map->flags)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_ADD_MAP, (unsigned long)map); - if (err) - return err; - - if (__get_user(m32.offset, &map->offset) - || __get_user(m32.mtrr, &map->mtrr) - || __get_user(handle, &map->handle)) - return -EFAULT; - - m32.handle = (unsigned long)handle; - if (m32.handle != (unsigned long)handle) - printk_ratelimited(KERN_ERR "compat_drm_addmap truncated handle" - " %p for type %d offset %x\n", - handle, m32.type, m32.offset); - - if (copy_to_user(argp, &m32, sizeof(m32))) - return -EFAULT; - - return 0; -} - -static int compat_drm_rmmap(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_map32_t __user *argp = (void __user *)arg; - struct drm_map __user *map; - u32 handle; - - if (get_user(handle, &argp->handle)) - return -EFAULT; - - map = compat_alloc_user_space(sizeof(*map)); - if (!map) - return -EFAULT; - if (__put_user((void *)(unsigned long)handle, &map->handle)) - return -EFAULT; - - return drm_ioctl(file, DRM_IOCTL_RM_MAP, (unsigned long)map); -} - -typedef struct drm_client32 { - int idx; /**< Which client desired? */ - int auth; /**< Is client authenticated? */ - u32 pid; /**< Process ID */ - u32 uid; /**< User ID */ - u32 magic; /**< Magic */ - u32 iocs; /**< Ioctl count */ -} drm_client32_t; - -static int compat_drm_getclient(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_client32_t c32; - drm_client32_t __user *argp = (void __user *)arg; - struct drm_client __user *client; - int idx, err; - - if (get_user(idx, &argp->idx)) - return -EFAULT; - - client = compat_alloc_user_space(sizeof(*client)); - if (!client) - return -EFAULT; - if (__put_user(idx, &client->idx)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_GET_CLIENT, (unsigned long)client); - if (err) - return err; - - if (__get_user(c32.idx, &client->idx) - || __get_user(c32.auth, &client->auth) - || __get_user(c32.pid, &client->pid) - || __get_user(c32.uid, &client->uid) - || __get_user(c32.magic, &client->magic) - || __get_user(c32.iocs, &client->iocs)) - return -EFAULT; - - if (copy_to_user(argp, &c32, sizeof(c32))) - return -EFAULT; - return 0; -} - -typedef struct drm_stats32 { - u32 count; - struct { - u32 value; - enum drm_stat_type type; - } data[15]; -} drm_stats32_t; - -static int compat_drm_getstats(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_stats32_t s32; - drm_stats32_t __user *argp = (void __user *)arg; - struct drm_stats __user *stats; - int i, err; - - stats = compat_alloc_user_space(sizeof(*stats)); - if (!stats) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_GET_STATS, (unsigned long)stats); - if (err) - return err; - - if (__get_user(s32.count, &stats->count)) - return -EFAULT; - for (i = 0; i < 15; ++i) - if (__get_user(s32.data[i].value, &stats->data[i].value) - || __get_user(s32.data[i].type, &stats->data[i].type)) - return -EFAULT; - - if (copy_to_user(argp, &s32, sizeof(s32))) - return -EFAULT; - return 0; -} - -typedef struct drm_buf_desc32 { - int count; /**< Number of buffers of this size */ - int size; /**< Size in bytes */ - int low_mark; /**< Low water mark */ - int high_mark; /**< High water mark */ - int flags; - u32 agp_start; /**< Start address in the AGP aperture */ -} drm_buf_desc32_t; - -static int compat_drm_addbufs(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_buf_desc32_t __user *argp = (void __user *)arg; - struct drm_buf_desc __user *buf; - int err; - unsigned long agp_start; - - buf = compat_alloc_user_space(sizeof(*buf)); - if (!buf || !access_ok(VERIFY_WRITE, argp, sizeof(*argp))) - return -EFAULT; - - if (__copy_in_user(buf, argp, offsetof(drm_buf_desc32_t, agp_start)) - || __get_user(agp_start, &argp->agp_start) - || __put_user(agp_start, &buf->agp_start)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_ADD_BUFS, (unsigned long)buf); - if (err) - return err; - - if (__copy_in_user(argp, buf, offsetof(drm_buf_desc32_t, agp_start)) - || __get_user(agp_start, &buf->agp_start) - || __put_user(agp_start, &argp->agp_start)) - return -EFAULT; - - return 0; -} - -static int compat_drm_markbufs(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_buf_desc32_t b32; - drm_buf_desc32_t __user *argp = (void __user *)arg; - struct drm_buf_desc __user *buf; - - if (copy_from_user(&b32, argp, sizeof(b32))) - return -EFAULT; - - buf = compat_alloc_user_space(sizeof(*buf)); - if (!buf) - return -EFAULT; - - if (__put_user(b32.size, &buf->size) - || __put_user(b32.low_mark, &buf->low_mark) - || __put_user(b32.high_mark, &buf->high_mark)) - return -EFAULT; - - return drm_ioctl(file, DRM_IOCTL_MARK_BUFS, (unsigned long)buf); -} - -typedef struct drm_buf_info32 { - int count; /**< Entries in list */ - u32 list; -} drm_buf_info32_t; - -static int compat_drm_infobufs(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_buf_info32_t req32; - drm_buf_info32_t __user *argp = (void __user *)arg; - drm_buf_desc32_t __user *to; - struct drm_buf_info __user *request; - struct drm_buf_desc __user *list; - size_t nbytes; - int i, err; - int count, actual; - - if (copy_from_user(&req32, argp, sizeof(req32))) - return -EFAULT; - - count = req32.count; - to = (drm_buf_desc32_t __user *) (unsigned long)req32.list; - if (count < 0) - count = 0; - if (count > 0 - && !access_ok(VERIFY_WRITE, to, count * sizeof(drm_buf_desc32_t))) - return -EFAULT; - - nbytes = sizeof(*request) + count * sizeof(struct drm_buf_desc); - request = compat_alloc_user_space(nbytes); - if (!request) - return -EFAULT; - list = (struct drm_buf_desc *) (request + 1); - - if (__put_user(count, &request->count) - || __put_user(list, &request->list)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_INFO_BUFS, (unsigned long)request); - if (err) - return err; - - if (__get_user(actual, &request->count)) - return -EFAULT; - if (count >= actual) - for (i = 0; i < actual; ++i) - if (__copy_in_user(&to[i], &list[i], - offsetof(struct drm_buf_desc, flags))) - return -EFAULT; - - if (__put_user(actual, &argp->count)) - return -EFAULT; - - return 0; -} - -typedef struct drm_buf_pub32 { - int idx; /**< Index into the master buffer list */ - int total; /**< Buffer size */ - int used; /**< Amount of buffer in use (for DMA) */ - u32 address; /**< Address of buffer */ -} drm_buf_pub32_t; - -typedef struct drm_buf_map32 { - int count; /**< Length of the buffer list */ - u32 virtual; /**< Mmap'd area in user-virtual */ - u32 list; /**< Buffer information */ -} drm_buf_map32_t; - -static int compat_drm_mapbufs(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_buf_map32_t __user *argp = (void __user *)arg; - drm_buf_map32_t req32; - drm_buf_pub32_t __user *list32; - struct drm_buf_map __user *request; - struct drm_buf_pub __user *list; - int i, err; - int count, actual; - size_t nbytes; - void __user *addr; - - if (copy_from_user(&req32, argp, sizeof(req32))) - return -EFAULT; - count = req32.count; - list32 = (void __user *)(unsigned long)req32.list; - - if (count < 0) - return -EINVAL; - nbytes = sizeof(*request) + count * sizeof(struct drm_buf_pub); - request = compat_alloc_user_space(nbytes); - if (!request) - return -EFAULT; - list = (struct drm_buf_pub *) (request + 1); - - if (__put_user(count, &request->count) - || __put_user(list, &request->list)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_MAP_BUFS, (unsigned long)request); - if (err) - return err; - - if (__get_user(actual, &request->count)) - return -EFAULT; - if (count >= actual) - for (i = 0; i < actual; ++i) - if (__copy_in_user(&list32[i], &list[i], - offsetof(struct drm_buf_pub, address)) - || __get_user(addr, &list[i].address) - || __put_user((unsigned long)addr, - &list32[i].address)) - return -EFAULT; - - if (__put_user(actual, &argp->count) - || __get_user(addr, &request->virtual) - || __put_user((unsigned long)addr, &argp->virtual)) - return -EFAULT; - - return 0; -} - -typedef struct drm_buf_free32 { - int count; - u32 list; -} drm_buf_free32_t; - -static int compat_drm_freebufs(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_buf_free32_t req32; - struct drm_buf_free __user *request; - drm_buf_free32_t __user *argp = (void __user *)arg; - - if (copy_from_user(&req32, argp, sizeof(req32))) - return -EFAULT; - - request = compat_alloc_user_space(sizeof(*request)); - if (!request) - return -EFAULT; - if (__put_user(req32.count, &request->count) - || __put_user((int __user *)(unsigned long)req32.list, - &request->list)) - return -EFAULT; - - return drm_ioctl(file, DRM_IOCTL_FREE_BUFS, (unsigned long)request); -} - -typedef struct drm_ctx_priv_map32 { - unsigned int ctx_id; /**< Context requesting private mapping */ - u32 handle; /**< Handle of map */ -} drm_ctx_priv_map32_t; - -static int compat_drm_setsareactx(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_ctx_priv_map32_t req32; - struct drm_ctx_priv_map __user *request; - drm_ctx_priv_map32_t __user *argp = (void __user *)arg; - - if (copy_from_user(&req32, argp, sizeof(req32))) - return -EFAULT; - - request = compat_alloc_user_space(sizeof(*request)); - if (!request) - return -EFAULT; - if (__put_user(req32.ctx_id, &request->ctx_id) - || __put_user((void *)(unsigned long)req32.handle, - &request->handle)) - return -EFAULT; - - return drm_ioctl(file, DRM_IOCTL_SET_SAREA_CTX, (unsigned long)request); -} - -static int compat_drm_getsareactx(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct drm_ctx_priv_map __user *request; - drm_ctx_priv_map32_t __user *argp = (void __user *)arg; - int err; - unsigned int ctx_id; - void *handle; - - if (!access_ok(VERIFY_WRITE, argp, sizeof(*argp)) - || __get_user(ctx_id, &argp->ctx_id)) - return -EFAULT; - - request = compat_alloc_user_space(sizeof(*request)); - if (!request) - return -EFAULT; - if (__put_user(ctx_id, &request->ctx_id)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_GET_SAREA_CTX, (unsigned long)request); - if (err) - return err; - - if (__get_user(handle, &request->handle) - || __put_user((unsigned long)handle, &argp->handle)) - return -EFAULT; - - return 0; -} - -typedef struct drm_ctx_res32 { - int count; - u32 contexts; -} drm_ctx_res32_t; - -static int compat_drm_resctx(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_ctx_res32_t __user *argp = (void __user *)arg; - drm_ctx_res32_t res32; - struct drm_ctx_res __user *res; - int err; - - if (copy_from_user(&res32, argp, sizeof(res32))) - return -EFAULT; - - res = compat_alloc_user_space(sizeof(*res)); - if (!res) - return -EFAULT; - if (__put_user(res32.count, &res->count) - || __put_user((struct drm_ctx __user *) (unsigned long)res32.contexts, - &res->contexts)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_RES_CTX, (unsigned long)res); - if (err) - return err; - - if (__get_user(res32.count, &res->count) - || __put_user(res32.count, &argp->count)) - return -EFAULT; - - return 0; -} - -typedef struct drm_dma32 { - int context; /**< Context handle */ - int send_count; /**< Number of buffers to send */ - u32 send_indices; /**< List of handles to buffers */ - u32 send_sizes; /**< Lengths of data to send */ - enum drm_dma_flags flags; /**< Flags */ - int request_count; /**< Number of buffers requested */ - int request_size; /**< Desired size for buffers */ - u32 request_indices; /**< Buffer information */ - u32 request_sizes; - int granted_count; /**< Number of buffers granted */ -} drm_dma32_t; - -static int compat_drm_dma(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_dma32_t d32; - drm_dma32_t __user *argp = (void __user *)arg; - struct drm_dma __user *d; - int err; - - if (copy_from_user(&d32, argp, sizeof(d32))) - return -EFAULT; - - d = compat_alloc_user_space(sizeof(*d)); - if (!d) - return -EFAULT; - - if (__put_user(d32.context, &d->context) - || __put_user(d32.send_count, &d->send_count) - || __put_user((int __user *)(unsigned long)d32.send_indices, - &d->send_indices) - || __put_user((int __user *)(unsigned long)d32.send_sizes, - &d->send_sizes) - || __put_user(d32.flags, &d->flags) - || __put_user(d32.request_count, &d->request_count) - || __put_user((int __user *)(unsigned long)d32.request_indices, - &d->request_indices) - || __put_user((int __user *)(unsigned long)d32.request_sizes, - &d->request_sizes)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_DMA, (unsigned long)d); - if (err) - return err; - - if (__get_user(d32.request_size, &d->request_size) - || __get_user(d32.granted_count, &d->granted_count) - || __put_user(d32.request_size, &argp->request_size) - || __put_user(d32.granted_count, &argp->granted_count)) - return -EFAULT; - - return 0; -} - -#if IS_ENABLED(CONFIG_AGP) -typedef struct drm_agp_mode32 { - u32 mode; /**< AGP mode */ -} drm_agp_mode32_t; - -static int compat_drm_agp_enable(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_agp_mode32_t __user *argp = (void __user *)arg; - drm_agp_mode32_t m32; - struct drm_agp_mode __user *mode; - - if (get_user(m32.mode, &argp->mode)) - return -EFAULT; - - mode = compat_alloc_user_space(sizeof(*mode)); - if (put_user(m32.mode, &mode->mode)) - return -EFAULT; - - return drm_ioctl(file, DRM_IOCTL_AGP_ENABLE, (unsigned long)mode); -} - -typedef struct drm_agp_info32 { - int agp_version_major; - int agp_version_minor; - u32 mode; - u32 aperture_base; /* physical address */ - u32 aperture_size; /* bytes */ - u32 memory_allowed; /* bytes */ - u32 memory_used; - - /* PCI information */ - unsigned short id_vendor; - unsigned short id_device; -} drm_agp_info32_t; - -static int compat_drm_agp_info(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_agp_info32_t __user *argp = (void __user *)arg; - drm_agp_info32_t i32; - struct drm_agp_info __user *info; - int err; - - info = compat_alloc_user_space(sizeof(*info)); - if (!info) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_AGP_INFO, (unsigned long)info); - if (err) - return err; - - if (__get_user(i32.agp_version_major, &info->agp_version_major) - || __get_user(i32.agp_version_minor, &info->agp_version_minor) - || __get_user(i32.mode, &info->mode) - || __get_user(i32.aperture_base, &info->aperture_base) - || __get_user(i32.aperture_size, &info->aperture_size) - || __get_user(i32.memory_allowed, &info->memory_allowed) - || __get_user(i32.memory_used, &info->memory_used) - || __get_user(i32.id_vendor, &info->id_vendor) - || __get_user(i32.id_device, &info->id_device)) - return -EFAULT; - - if (copy_to_user(argp, &i32, sizeof(i32))) - return -EFAULT; - - return 0; -} - -typedef struct drm_agp_buffer32 { - u32 size; /**< In bytes -- will round to page boundary */ - u32 handle; /**< Used for binding / unbinding */ - u32 type; /**< Type of memory to allocate */ - u32 physical; /**< Physical used by i810 */ -} drm_agp_buffer32_t; - -static int compat_drm_agp_alloc(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_agp_buffer32_t __user *argp = (void __user *)arg; - drm_agp_buffer32_t req32; - struct drm_agp_buffer __user *request; - int err; - - if (copy_from_user(&req32, argp, sizeof(req32))) - return -EFAULT; - - request = compat_alloc_user_space(sizeof(*request)); - if (!request - || __put_user(req32.size, &request->size) - || __put_user(req32.type, &request->type)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_AGP_ALLOC, (unsigned long)request); - if (err) - return err; - - if (__get_user(req32.handle, &request->handle) - || __get_user(req32.physical, &request->physical) - || copy_to_user(argp, &req32, sizeof(req32))) { - drm_ioctl(file, DRM_IOCTL_AGP_FREE, (unsigned long)request); - return -EFAULT; - } - - return 0; -} - -static int compat_drm_agp_free(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_agp_buffer32_t __user *argp = (void __user *)arg; - struct drm_agp_buffer __user *request; - u32 handle; - - request = compat_alloc_user_space(sizeof(*request)); - if (!request - || get_user(handle, &argp->handle) - || __put_user(handle, &request->handle)) - return -EFAULT; - - return drm_ioctl(file, DRM_IOCTL_AGP_FREE, (unsigned long)request); -} - -typedef struct drm_agp_binding32 { - u32 handle; /**< From drm_agp_buffer */ - u32 offset; /**< In bytes -- will round to page boundary */ -} drm_agp_binding32_t; - -static int compat_drm_agp_bind(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_agp_binding32_t __user *argp = (void __user *)arg; - drm_agp_binding32_t req32; - struct drm_agp_binding __user *request; - - if (copy_from_user(&req32, argp, sizeof(req32))) - return -EFAULT; - - request = compat_alloc_user_space(sizeof(*request)); - if (!request - || __put_user(req32.handle, &request->handle) - || __put_user(req32.offset, &request->offset)) - return -EFAULT; - - return drm_ioctl(file, DRM_IOCTL_AGP_BIND, (unsigned long)request); -} - -static int compat_drm_agp_unbind(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_agp_binding32_t __user *argp = (void __user *)arg; - struct drm_agp_binding __user *request; - u32 handle; - - request = compat_alloc_user_space(sizeof(*request)); - if (!request - || get_user(handle, &argp->handle) - || __put_user(handle, &request->handle)) - return -EFAULT; - - return drm_ioctl(file, DRM_IOCTL_AGP_UNBIND, (unsigned long)request); -} -#endif /* CONFIG_AGP */ - -typedef struct drm_scatter_gather32 { - u32 size; /**< In bytes -- will round to page boundary */ - u32 handle; /**< Used for mapping / unmapping */ -} drm_scatter_gather32_t; - -static int compat_drm_sg_alloc(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_scatter_gather32_t __user *argp = (void __user *)arg; - struct drm_scatter_gather __user *request; - int err; - unsigned long x; - - request = compat_alloc_user_space(sizeof(*request)); - if (!request || !access_ok(VERIFY_WRITE, argp, sizeof(*argp)) - || __get_user(x, &argp->size) - || __put_user(x, &request->size)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_SG_ALLOC, (unsigned long)request); - if (err) - return err; - - /* XXX not sure about the handle conversion here... */ - if (__get_user(x, &request->handle) - || __put_user(x >> PAGE_SHIFT, &argp->handle)) - return -EFAULT; - - return 0; -} - -static int compat_drm_sg_free(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_scatter_gather32_t __user *argp = (void __user *)arg; - struct drm_scatter_gather __user *request; - unsigned long x; - - request = compat_alloc_user_space(sizeof(*request)); - if (!request || !access_ok(VERIFY_WRITE, argp, sizeof(*argp)) - || __get_user(x, &argp->handle) - || __put_user(x << PAGE_SHIFT, &request->handle)) - return -EFAULT; - - return drm_ioctl(file, DRM_IOCTL_SG_FREE, (unsigned long)request); -} - -#if defined(CONFIG_X86) || defined(CONFIG_IA64) -typedef struct drm_update_draw32 { - drm_drawable_t handle; - unsigned int type; - unsigned int num; - /* 64-bit version has a 32-bit pad here */ - u64 data; /**< Pointer */ -} __attribute__((packed)) drm_update_draw32_t; - -static int compat_drm_update_draw(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_update_draw32_t update32; - struct drm_update_draw __user *request; - int err; - - if (copy_from_user(&update32, (void __user *)arg, sizeof(update32))) - return -EFAULT; - - request = compat_alloc_user_space(sizeof(*request)); - if (!request || - __put_user(update32.handle, &request->handle) || - __put_user(update32.type, &request->type) || - __put_user(update32.num, &request->num) || - __put_user(update32.data, &request->data)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_UPDATE_DRAW, (unsigned long)request); - return err; -} -#endif - -struct drm_wait_vblank_request32 { - enum drm_vblank_seq_type type; - unsigned int sequence; - u32 signal; -}; - -struct drm_wait_vblank_reply32 { - enum drm_vblank_seq_type type; - unsigned int sequence; - s32 tval_sec; - s32 tval_usec; -}; - -typedef union drm_wait_vblank32 { - struct drm_wait_vblank_request32 request; - struct drm_wait_vblank_reply32 reply; -} drm_wait_vblank32_t; - -static int compat_drm_wait_vblank(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_wait_vblank32_t __user *argp = (void __user *)arg; - drm_wait_vblank32_t req32; - union drm_wait_vblank __user *request; - int err; - - if (copy_from_user(&req32, argp, sizeof(req32))) - return -EFAULT; - - request = compat_alloc_user_space(sizeof(*request)); - if (!request - || __put_user(req32.request.type, &request->request.type) - || __put_user(req32.request.sequence, &request->request.sequence) - || __put_user(req32.request.signal, &request->request.signal)) - return -EFAULT; - - err = drm_ioctl(file, DRM_IOCTL_WAIT_VBLANK, (unsigned long)request); - if (err) - return err; - - if (__get_user(req32.reply.type, &request->reply.type) - || __get_user(req32.reply.sequence, &request->reply.sequence) - || __get_user(req32.reply.tval_sec, &request->reply.tval_sec) - || __get_user(req32.reply.tval_usec, &request->reply.tval_usec)) - return -EFAULT; - - if (copy_to_user(argp, &req32, sizeof(req32))) - return -EFAULT; - - return 0; -} - -#if defined(CONFIG_X86) || defined(CONFIG_IA64) -typedef struct drm_mode_fb_cmd232 { - u32 fb_id; - u32 width; - u32 height; - u32 pixel_format; - u32 flags; - u32 handles[4]; - u32 pitches[4]; - u32 offsets[4]; - u64 modifier[4]; -} __attribute__((packed)) drm_mode_fb_cmd232_t; - -static int compat_drm_mode_addfb2(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct drm_mode_fb_cmd232 __user *argp = (void __user *)arg; - struct drm_mode_fb_cmd232 req32; - struct drm_mode_fb_cmd2 __user *req64; - int i; - int err; - - if (copy_from_user(&req32, argp, sizeof(req32))) - return -EFAULT; - - req64 = compat_alloc_user_space(sizeof(*req64)); - - if (!access_ok(VERIFY_WRITE, req64, sizeof(*req64)) - || __put_user(req32.width, &req64->width) - || __put_user(req32.height, &req64->height) - || __put_user(req32.pixel_format, &req64->pixel_format) - || __put_user(req32.flags, &req64->flags)) - return -EFAULT; - - for (i = 0; i < 4; i++) { - if (__put_user(req32.handles[i], &req64->handles[i])) - return -EFAULT; - if (__put_user(req32.pitches[i], &req64->pitches[i])) - return -EFAULT; - if (__put_user(req32.offsets[i], &req64->offsets[i])) - return -EFAULT; - if (__put_user(req32.modifier[i], &req64->modifier[i])) - return -EFAULT; - } - - err = drm_ioctl(file, DRM_IOCTL_MODE_ADDFB2, (unsigned long)req64); - if (err) - return err; - - if (__get_user(req32.fb_id, &req64->fb_id)) - return -EFAULT; - - if (copy_to_user(argp, &req32, sizeof(req32))) - return -EFAULT; - - return 0; -} -#endif - -static drm_ioctl_compat_t *drm_compat_ioctls[] = { - [DRM_IOCTL_NR(DRM_IOCTL_VERSION32)] = compat_drm_version, - [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE32)] = compat_drm_getunique, - [DRM_IOCTL_NR(DRM_IOCTL_GET_MAP32)] = compat_drm_getmap, - [DRM_IOCTL_NR(DRM_IOCTL_GET_CLIENT32)] = compat_drm_getclient, - [DRM_IOCTL_NR(DRM_IOCTL_GET_STATS32)] = compat_drm_getstats, - [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE32)] = compat_drm_setunique, - [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP32)] = compat_drm_addmap, - [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS32)] = compat_drm_addbufs, - [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS32)] = compat_drm_markbufs, - [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS32)] = compat_drm_infobufs, - [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS32)] = compat_drm_mapbufs, - [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS32)] = compat_drm_freebufs, - [DRM_IOCTL_NR(DRM_IOCTL_RM_MAP32)] = compat_drm_rmmap, - [DRM_IOCTL_NR(DRM_IOCTL_SET_SAREA_CTX32)] = compat_drm_setsareactx, - [DRM_IOCTL_NR(DRM_IOCTL_GET_SAREA_CTX32)] = compat_drm_getsareactx, - [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX32)] = compat_drm_resctx, - [DRM_IOCTL_NR(DRM_IOCTL_DMA32)] = compat_drm_dma, -#if IS_ENABLED(CONFIG_AGP) - [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE32)] = compat_drm_agp_enable, - [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO32)] = compat_drm_agp_info, - [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC32)] = compat_drm_agp_alloc, - [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE32)] = compat_drm_agp_free, - [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND32)] = compat_drm_agp_bind, - [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND32)] = compat_drm_agp_unbind, -#endif - [DRM_IOCTL_NR(DRM_IOCTL_SG_ALLOC32)] = compat_drm_sg_alloc, - [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE32)] = compat_drm_sg_free, -#if defined(CONFIG_X86) || defined(CONFIG_IA64) - [DRM_IOCTL_NR(DRM_IOCTL_UPDATE_DRAW32)] = compat_drm_update_draw, -#endif - [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK32)] = compat_drm_wait_vblank, -#if defined(CONFIG_X86) || defined(CONFIG_IA64) - [DRM_IOCTL_NR(DRM_IOCTL_MODE_ADDFB232)] = compat_drm_mode_addfb2, -#endif -}; - -/** - * Called whenever a 32-bit process running under a 64-bit kernel - * performs an ioctl on /dev/drm. - * - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument. - * \return zero on success or negative number on failure. - */ -long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - unsigned int nr = DRM_IOCTL_NR(cmd); - drm_ioctl_compat_t *fn; - int ret; - - /* Assume that ioctls without an explicit compat routine will just - * work. This may not always be a good assumption, but it's better - * than always failing. - */ - if (nr >= ARRAY_SIZE(drm_compat_ioctls)) - return drm_ioctl(filp, cmd, arg); - - fn = drm_compat_ioctls[nr]; - - if (fn != NULL) - ret = (*fn) (filp, cmd, arg); - else - ret = drm_ioctl(filp, cmd, arg); - - return ret; -} - -EXPORT_SYMBOL(drm_compat_ioctl); diff --git a/src/4.x/drivers/gpu/drm/drm_ioctl.c b/src/4.x/drivers/gpu/drm/drm_ioctl.c deleted file mode 100644 index a7030ada8..000000000 --- a/src/4.x/drivers/gpu/drm/drm_ioctl.c +++ /dev/null @@ -1,823 +0,0 @@ -/* - * Created: Fri Jan 8 09:01:26 1999 by faith@valinux.com - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Author Rickard E. (Rik) Faith - * Author Gareth Hughes - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include "drm_legacy.h" -#include "drm_internal.h" -#include "drm_crtc_internal.h" - -#include -#include -#include - -static int drm_version(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -/* - * Get the bus id. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_unique structure. - * \return zero on success or a negative number on failure. - * - * Copies the bus id from drm_device::unique into user space. - */ -static int drm_getunique(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_unique *u = data; - struct drm_master *master = file_priv->master; - - if (u->unique_len >= master->unique_len) { - if (copy_to_user(u->unique, master->unique, master->unique_len)) - return -EFAULT; - } - u->unique_len = master->unique_len; - - return 0; -} - -static void -drm_unset_busid(struct drm_device *dev, - struct drm_master *master) -{ - kfree(master->unique); - master->unique = NULL; - master->unique_len = 0; -} - -/* - * Set the bus id. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_unique structure. - * \return zero on success or a negative number on failure. - * - * Copies the bus id from userspace into drm_device::unique, and verifies that - * it matches the device this DRM is attached to (EINVAL otherwise). Deprecated - * in interface version 1.1 and will return EBUSY when setversion has requested - * version 1.1 or greater. Also note that KMS is all version 1.1 and later and - * UMS was only ever supported on pci devices. - */ -static int drm_setunique(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_unique *u = data; - struct drm_master *master = file_priv->master; - int ret; - - if (master->unique_len || master->unique) - return -EBUSY; - - if (!u->unique_len || u->unique_len > 1024) - return -EINVAL; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return 0; - - if (WARN_ON(!dev->pdev)) - return -EINVAL; - - ret = drm_pci_set_unique(dev, master, u); - if (ret) - goto err; - - return 0; - -err: - drm_unset_busid(dev, master); - return ret; -} - -static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) -{ - struct drm_master *master = file_priv->master; - int ret; - - if (master->unique != NULL) - drm_unset_busid(dev, master); - - if (dev->driver->set_busid) { - ret = dev->driver->set_busid(dev, master); - if (ret) { - drm_unset_busid(dev, master); - return ret; - } - } else { - if (WARN(dev->unique == NULL, - "No drm_driver.set_busid() implementation provided by " - "%ps. Use drm_dev_set_unique() to set the unique " - "name explicitly.", dev->driver)) - return -EINVAL; - - master->unique = kstrdup(dev->unique, GFP_KERNEL); - if (master->unique) - master->unique_len = strlen(dev->unique); - } - - return 0; -} - -/* - * Get a mapping information. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_map structure. - * - * \return zero on success or a negative number on failure. - * - * Searches for the mapping with the specified offset and copies its information - * into userspace - */ -static int drm_getmap(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_map *map = data; - struct drm_map_list *r_list = NULL; - struct list_head *list; - int idx; - int i; - - idx = map->offset; - if (idx < 0) - return -EINVAL; - - i = 0; - mutex_lock(&dev->struct_mutex); - list_for_each(list, &dev->maplist) { - if (i == idx) { - r_list = list_entry(list, struct drm_map_list, head); - break; - } - i++; - } - if (!r_list || !r_list->map) { - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - - map->offset = r_list->map->offset; - map->size = r_list->map->size; - map->type = r_list->map->type; - map->flags = r_list->map->flags; - map->handle = (void *)(unsigned long) r_list->user_token; - map->mtrr = arch_phys_wc_index(r_list->map->mtrr); - - mutex_unlock(&dev->struct_mutex); - - return 0; -} - -/* - * Get client information. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_client structure. - * - * \return zero on success or a negative number on failure. - * - * Searches for the client with the specified index and copies its information - * into userspace - */ -static int drm_getclient(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_client *client = data; - - /* - * Hollowed-out getclient ioctl to keep some dead old drm tests/tools - * not breaking completely. Userspace tools stop enumerating one they - * get -EINVAL, hence this is the return value we need to hand back for - * no clients tracked. - * - * Unfortunately some clients (*cough* libva *cough*) use this in a fun - * attempt to figure out whether they're authenticated or not. Since - * that's the only thing they care about, give it to the directly - * instead of walking one giant list. - */ - if (client->idx == 0) { - client->auth = file_priv->authenticated; - client->pid = pid_vnr(file_priv->pid); - client->uid = from_kuid_munged(current_user_ns(), - file_priv->uid); - client->magic = 0; - client->iocs = 0; - - return 0; - } else { - return -EINVAL; - } -} - -/* - * Get statistics information. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_stats structure. - * - * \return zero on success or a negative number on failure. - */ -static int drm_getstats(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_stats *stats = data; - - /* Clear stats to prevent userspace from eating its stack garbage. */ - memset(stats, 0, sizeof(*stats)); - - return 0; -} - -/* - * Get device/driver capabilities - */ -static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_get_cap *req = data; - - req->value = 0; - switch (req->capability) { - case DRM_CAP_DUMB_BUFFER: - if (dev->driver->dumb_create) - req->value = 1; - break; - case DRM_CAP_VBLANK_HIGH_CRTC: - req->value = 1; - break; - case DRM_CAP_DUMB_PREFERRED_DEPTH: - req->value = dev->mode_config.preferred_depth; - break; - case DRM_CAP_DUMB_PREFER_SHADOW: - req->value = dev->mode_config.prefer_shadow; - break; - case DRM_CAP_PRIME: - req->value |= dev->driver->prime_fd_to_handle ? DRM_PRIME_CAP_IMPORT : 0; - req->value |= dev->driver->prime_handle_to_fd ? DRM_PRIME_CAP_EXPORT : 0; - break; - case DRM_CAP_TIMESTAMP_MONOTONIC: - req->value = drm_timestamp_monotonic; - break; - case DRM_CAP_ASYNC_PAGE_FLIP: - req->value = dev->mode_config.async_page_flip; - break; - case DRM_CAP_CURSOR_WIDTH: - if (dev->mode_config.cursor_width) - req->value = dev->mode_config.cursor_width; - else - req->value = 64; - break; - case DRM_CAP_CURSOR_HEIGHT: - if (dev->mode_config.cursor_height) - req->value = dev->mode_config.cursor_height; - else - req->value = 64; - break; - case DRM_CAP_ADDFB2_MODIFIERS: - req->value = dev->mode_config.allow_fb_modifiers; - break; - default: - return -EINVAL; - } - return 0; -} - -/* - * Set device/driver capabilities - */ -static int -drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_set_client_cap *req = data; - - switch (req->capability) { - case DRM_CLIENT_CAP_STEREO_3D: - if (req->value > 1) - return -EINVAL; - file_priv->stereo_allowed = req->value; - break; - case DRM_CLIENT_CAP_UNIVERSAL_PLANES: - if (req->value > 1) - return -EINVAL; - file_priv->universal_planes = req->value; - break; - case DRM_CLIENT_CAP_ATOMIC: - if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) - return -EINVAL; - if (req->value > 1) - return -EINVAL; - file_priv->atomic = req->value; - file_priv->universal_planes = req->value; - break; - default: - return -EINVAL; - } - - return 0; -} - -/* - * Setversion ioctl. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_lock structure. - * \return zero on success or negative number on failure. - * - * Sets the requested interface version - */ -static int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_set_version *sv = data; - int if_version, retcode = 0; - - if (sv->drm_di_major != -1) { - if (sv->drm_di_major != DRM_IF_MAJOR || - sv->drm_di_minor < 0 || sv->drm_di_minor > DRM_IF_MINOR) { - retcode = -EINVAL; - goto done; - } - if_version = DRM_IF_VERSION(sv->drm_di_major, - sv->drm_di_minor); - dev->if_version = max(if_version, dev->if_version); - if (sv->drm_di_minor >= 1) { - /* - * Version 1.1 includes tying of DRM to specific device - * Version 1.4 has proper PCI domain support - */ - retcode = drm_set_busid(dev, file_priv); - if (retcode) - goto done; - } - } - - if (sv->drm_dd_major != -1) { - if (sv->drm_dd_major != dev->driver->major || - sv->drm_dd_minor < 0 || sv->drm_dd_minor > - dev->driver->minor) { - retcode = -EINVAL; - goto done; - } - } - -done: - sv->drm_di_major = DRM_IF_MAJOR; - sv->drm_di_minor = DRM_IF_MINOR; - sv->drm_dd_major = dev->driver->major; - sv->drm_dd_minor = dev->driver->minor; - - return retcode; -} - -/** - * drm_noop - DRM no-op ioctl implemntation - * @dev: DRM device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: DRM file for the ioctl call - * - * This no-op implementation for drm ioctls is useful for deprecated - * functionality where we can't return a failure code because existing userspace - * checks the result of the ioctl, but doesn't care about the action. - * - * Always returns successfully with 0. - */ -int drm_noop(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - DRM_DEBUG("\n"); - return 0; -} -EXPORT_SYMBOL(drm_noop); - -/** - * drm_invalid_op - DRM invalid ioctl implemntation - * @dev: DRM device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: DRM file for the ioctl call - * - * This no-op implementation for drm ioctls is useful for deprecated - * functionality where we really don't want to allow userspace to call the ioctl - * any more. This is the case for old ums interfaces for drivers that - * transitioned to kms gradually and so kept the old legacy tables around. This - * only applies to radeon and i915 kms drivers, other drivers shouldn't need to - * use this function. - * - * Always fails with a return value of -EINVAL. - */ -int drm_invalid_op(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - return -EINVAL; -} -EXPORT_SYMBOL(drm_invalid_op); - -/* - * Copy and IOCTL return string to user space - */ -static int drm_copy_field(char __user *buf, size_t *buf_len, const char *value) -{ - int len; - - /* don't overflow userbuf */ - len = strlen(value); - if (len > *buf_len) - len = *buf_len; - - /* let userspace know exact length of driver value (which could be - * larger than the userspace-supplied buffer) */ - *buf_len = strlen(value); - - /* finally, try filling in the userbuf */ - if (len && buf) - if (copy_to_user(buf, value, len)) - return -EFAULT; - return 0; -} - -/* - * Get version information - * - * \param inode device inode. - * \param filp file pointer. - * \param cmd command. - * \param arg user argument, pointing to a drm_version structure. - * \return zero on success or negative number on failure. - * - * Fills in the version information in \p arg. - */ -static int drm_version(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_version *version = data; - int err; - - version->version_major = dev->driver->major; - version->version_minor = dev->driver->minor; - version->version_patchlevel = dev->driver->patchlevel; - err = drm_copy_field(version->name, &version->name_len, - dev->driver->name); - if (!err) - err = drm_copy_field(version->date, &version->date_len, - dev->driver->date); - if (!err) - err = drm_copy_field(version->desc, &version->desc_len, - dev->driver->desc); - - return err; -} - -/* - * drm_ioctl_permit - Check ioctl permissions against caller - * - * @flags: ioctl permission flags. - * @file_priv: Pointer to struct drm_file identifying the caller. - * - * Checks whether the caller is allowed to run an ioctl with the - * indicated permissions. If so, returns zero. Otherwise returns an - * error code suitable for ioctl return. - */ -int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) -{ - /* ROOT_ONLY is only for CAP_SYS_ADMIN */ - if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN))) - return -EACCES; - - /* AUTH is only for authenticated or render client */ - if (unlikely((flags & DRM_AUTH) && !drm_is_render_client(file_priv) && - !file_priv->authenticated)) - return -EACCES; - - /* MASTER is only for master or control clients */ - if (unlikely((flags & DRM_MASTER) && !file_priv->is_master && - !drm_is_control_client(file_priv))) - return -EACCES; - - /* Control clients must be explicitly allowed */ - if (unlikely(!(flags & DRM_CONTROL_ALLOW) && - drm_is_control_client(file_priv))) - return -EACCES; - - /* Render clients must be explicitly allowed */ - if (unlikely(!(flags & DRM_RENDER_ALLOW) && - drm_is_render_client(file_priv))) - return -EACCES; - - return 0; -} -EXPORT_SYMBOL(drm_ioctl_permit); - -#define DRM_IOCTL_DEF(ioctl, _func, _flags) \ - [DRM_IOCTL_NR(ioctl)] = { \ - .cmd = ioctl, \ - .func = _func, \ - .flags = _flags, \ - .name = #ioctl \ - } - -/* Ioctl table */ -static const struct drm_ioctl_desc drm_ioctls[] = { - DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, - DRM_UNLOCKED|DRM_RENDER_ALLOW|DRM_CONTROL_ALLOW), - DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0), - DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0), - DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED|DRM_RENDER_ALLOW), - DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, 0), - DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER), - - DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER), - - DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_legacy_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_legacy_rmmap_ioctl, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_legacy_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_legacy_getsareactx, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY), - - DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_legacy_addctx, DRM_AUTH|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_legacy_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_GET_CTX, drm_legacy_getctx, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_SWITCH_CTX, drm_legacy_switchctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_NEW_CTX, drm_legacy_newctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_RES_CTX, drm_legacy_resctx, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_ADD_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_RM_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - - DRM_IOCTL_DEF(DRM_IOCTL_LOCK, drm_legacy_lock, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_UNLOCK, drm_legacy_unlock, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_FINISH, drm_noop, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_ADD_BUFS, drm_legacy_addbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_MARK_BUFS, drm_legacy_markbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_INFO_BUFS, drm_legacy_infobufs, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_MAP_BUFS, drm_legacy_mapbufs, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_FREE_BUFS, drm_legacy_freebufs, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_DMA, drm_legacy_dma_ioctl, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - -#if IS_ENABLED(CONFIG_AGP) - DRM_IOCTL_DEF(DRM_IOCTL_AGP_ACQUIRE, drm_agp_acquire_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_RELEASE, drm_agp_release_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_ENABLE, drm_agp_enable_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_INFO, drm_agp_info_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_ALLOC, drm_agp_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_FREE, drm_agp_free_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_BIND, drm_agp_bind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_UNBIND, drm_agp_unbind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), -#endif - - DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_legacy_sg_alloc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_legacy_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - - DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_UNLOCKED), - - DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0), - - DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - - DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), - DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED), - - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - - DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), - DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), - - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATEPROPBLOB, drm_mode_createblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROYPROPBLOB, drm_mode_destroyblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), -}; - -#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) - -/** - * drm_ioctl - ioctl callback implementation for DRM drivers - * @filp: file this ioctl is called on - * @cmd: ioctl cmd number - * @arg: user argument - * - * Looks up the ioctl function in the ::ioctls table, checking for root - * previleges if so required, and dispatches to the respective function. - * - * Returns: - * Zero on success, negative error code on failure. - */ -long drm_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct drm_file *file_priv = filp->private_data; - struct drm_device *dev; - const struct drm_ioctl_desc *ioctl = NULL; - drm_ioctl_t *func; - unsigned int nr = DRM_IOCTL_NR(cmd); - int retcode = -EINVAL; - char stack_kdata[128]; - char *kdata = NULL; - unsigned int usize, asize, drv_size; - bool is_driver_ioctl; - - dev = file_priv->minor->dev; - - if (drm_device_is_unplugged(dev)) - return -ENODEV; - - is_driver_ioctl = nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END; - - if (is_driver_ioctl) { - /* driver ioctl */ - unsigned int index = nr - DRM_COMMAND_BASE; - - if (index >= dev->driver->num_ioctls) - goto err_i1; - index = array_index_nospec(index, dev->driver->num_ioctls); - ioctl = &dev->driver->ioctls[index]; - } else { - /* core ioctl */ - if (nr >= DRM_CORE_IOCTL_COUNT) - goto err_i1; - nr = array_index_nospec(nr, DRM_CORE_IOCTL_COUNT); - ioctl = &drm_ioctls[nr]; - } - - drv_size = _IOC_SIZE(ioctl->cmd); - usize = _IOC_SIZE(cmd); - asize = max(usize, drv_size); - cmd = ioctl->cmd; - - DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n", - task_pid_nr(current), - (long)old_encode_dev(file_priv->minor->kdev->devt), - file_priv->authenticated, ioctl->name); - - /* Do not trust userspace, use our own definition */ - func = ioctl->func; - - if (unlikely(!func)) { - DRM_DEBUG("no function\n"); - retcode = -EINVAL; - goto err_i1; - } - - retcode = drm_ioctl_permit(ioctl->flags, file_priv); - if (unlikely(retcode)) - goto err_i1; - - if (cmd & (IOC_IN | IOC_OUT)) { - if (asize <= sizeof(stack_kdata)) { - kdata = stack_kdata; - } else { - kdata = kmalloc(asize, GFP_KERNEL); - if (!kdata) { - retcode = -ENOMEM; - goto err_i1; - } - } - if (asize > usize) - memset(kdata + usize, 0, asize - usize); - } - - if (cmd & IOC_IN) { - if (copy_from_user(kdata, (void __user *)arg, - usize) != 0) { - retcode = -EFAULT; - goto err_i1; - } - } else if (cmd & IOC_OUT) { - memset(kdata, 0, usize); - } - - /* Enforce sane locking for kms driver ioctls. Core ioctls are - * too messy still. */ - if ((drm_core_check_feature(dev, DRIVER_MODESET) && is_driver_ioctl) || - (ioctl->flags & DRM_UNLOCKED)) - retcode = func(dev, kdata, file_priv); - else { - mutex_lock(&drm_global_mutex); - retcode = func(dev, kdata, file_priv); - mutex_unlock(&drm_global_mutex); - } - - if (cmd & IOC_OUT) { - if (copy_to_user((void __user *)arg, kdata, - usize) != 0) - retcode = -EFAULT; - } - - err_i1: - if (!ioctl) - DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n", - task_pid_nr(current), - (long)old_encode_dev(file_priv->minor->kdev->devt), - file_priv->authenticated, cmd, nr); - - if (kdata != stack_kdata) - kfree(kdata); - if (retcode) - DRM_DEBUG("ret = %d\n", retcode); - return retcode; -} -EXPORT_SYMBOL(drm_ioctl); - -/** - * drm_ioctl_flags - Check for core ioctl and return ioctl permission flags - * @nr: ioctl number - * @flags: where to return the ioctl permission flags - * - * This ioctl is only used by the vmwgfx driver to augment the access checks - * done by the drm core and insofar a pretty decent layering violation. This - * shouldn't be used by any drivers. - * - * Returns: - * True if the @nr corresponds to a DRM core ioctl numer, false otherwise. - */ -bool drm_ioctl_flags(unsigned int nr, unsigned int *flags) -{ - if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) - return false; - - if (nr >= DRM_CORE_IOCTL_COUNT) - return false; - nr = array_index_nospec(nr, DRM_CORE_IOCTL_COUNT); - - *flags = drm_ioctls[nr].flags; - return true; -} -EXPORT_SYMBOL(drm_ioctl_flags); diff --git a/src/4.x/drivers/gpu/drm/drm_irq.c b/src/4.x/drivers/gpu/drm/drm_irq.c deleted file mode 100644 index 4ddbc4912..000000000 --- a/src/4.x/drivers/gpu/drm/drm_irq.c +++ /dev/null @@ -1,1954 +0,0 @@ -/* - * drm_irq.c IRQ and vblank support - * - * \author Rickard E. (Rik) Faith - * \author Gareth Hughes - */ - -/* - * Created: Fri Mar 19 14:30:16 1999 by faith@valinux.com - * - * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include "drm_trace.h" -#include "drm_internal.h" - -#include /* For task queue support */ -#include - -#include -#include - -/* Access macro for slots in vblank timestamp ringbuffer. */ -#define vblanktimestamp(dev, pipe, count) \ - ((dev)->vblank[pipe].time[(count) % DRM_VBLANKTIME_RBSIZE]) - -/* Retry timestamp calculation up to 3 times to satisfy - * drm_timestamp_precision before giving up. - */ -#define DRM_TIMESTAMP_MAXRETRIES 3 - -/* Threshold in nanoseconds for detection of redundant - * vblank irq in drm_handle_vblank(). 1 msec should be ok. - */ -#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000 - -static bool -drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, - struct timeval *tvblank, unsigned flags); - -static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ - -/* - * Default to use monotonic timestamps for wait-for-vblank and page-flip - * complete events. - */ -unsigned int drm_timestamp_monotonic = 1; - -static int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */ - -module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); -module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); -module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); - -static void store_vblank(struct drm_device *dev, unsigned int pipe, - u32 vblank_count_inc, - struct timeval *t_vblank, u32 last) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - u32 tslot; - - assert_spin_locked(&dev->vblank_time_lock); - - vblank->last = last; - - /* All writers hold the spinlock, but readers are serialized by - * the latching of vblank->count below. - */ - tslot = vblank->count + vblank_count_inc; - vblanktimestamp(dev, pipe, tslot) = *t_vblank; - - /* - * vblank timestamp updates are protected on the write side with - * vblank_time_lock, but on the read side done locklessly using a - * sequence-lock on the vblank counter. Ensure correct ordering using - * memory barrriers. We need the barrier both before and also after the - * counter update to synchronize with the next timestamp write. - * The read-side barriers for this are in drm_vblank_count_and_time. - */ - smp_wmb(); - vblank->count += vblank_count_inc; - smp_wmb(); -} - -/** - * drm_reset_vblank_timestamp - reset the last timestamp to the last vblank - * @dev: DRM device - * @pipe: index of CRTC for which to reset the timestamp - * - * Reset the stored timestamp for the current vblank count to correspond - * to the last vblank occurred. - * - * Only to be called from drm_vblank_on(). - * - * Note: caller must hold dev->vbl_lock since this reads & writes - * device vblank fields. - */ -static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe) -{ - u32 cur_vblank; - bool rc; - struct timeval t_vblank; - int count = DRM_TIMESTAMP_MAXRETRIES; - - spin_lock(&dev->vblank_time_lock); - - /* - * sample the current counter to avoid random jumps - * when drm_vblank_enable() applies the diff - */ - do { - cur_vblank = dev->driver->get_vblank_counter(dev, pipe); - rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, 0); - } while (cur_vblank != dev->driver->get_vblank_counter(dev, pipe) && --count > 0); - - /* - * Only reinitialize corresponding vblank timestamp if high-precision query - * available and didn't fail. Otherwise reinitialize delayed at next vblank - * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid. - */ - if (!rc) - t_vblank = (struct timeval) {0, 0}; - - /* - * +1 to make sure user will never see the same - * vblank counter value before and after a modeset - */ - store_vblank(dev, pipe, 1, &t_vblank, cur_vblank); - - spin_unlock(&dev->vblank_time_lock); -} - -/** - * drm_update_vblank_count - update the master vblank counter - * @dev: DRM device - * @pipe: counter to update - * - * Call back into the driver to update the appropriate vblank counter - * (specified by @pipe). Deal with wraparound, if it occurred, and - * update the last read value so we can deal with wraparound on the next - * call if necessary. - * - * Only necessary when going from off->on, to account for frames we - * didn't get an interrupt for. - * - * Note: caller must hold dev->vbl_lock since this reads & writes - * device vblank fields. - */ -static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, - unsigned long flags) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - u32 cur_vblank, diff; - bool rc; - struct timeval t_vblank; - int count = DRM_TIMESTAMP_MAXRETRIES; - int framedur_ns = vblank->framedur_ns; - - /* - * Interrupts were disabled prior to this call, so deal with counter - * wrap if needed. - * NOTE! It's possible we lost a full dev->max_vblank_count + 1 events - * here if the register is small or we had vblank interrupts off for - * a long time. - * - * We repeat the hardware vblank counter & timestamp query until - * we get consistent results. This to prevent races between gpu - * updating its hardware counter while we are retrieving the - * corresponding vblank timestamp. - */ - do { - cur_vblank = dev->driver->get_vblank_counter(dev, pipe); - rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, flags); - } while (cur_vblank != dev->driver->get_vblank_counter(dev, pipe) && --count > 0); - - if (dev->max_vblank_count != 0) { - /* trust the hw counter when it's around */ - diff = (cur_vblank - vblank->last) & dev->max_vblank_count; - } else if (rc && framedur_ns) { - const struct timeval *t_old; - u64 diff_ns; - - t_old = &vblanktimestamp(dev, pipe, vblank->count); - diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old); - - /* - * Figure out how many vblanks we've missed based - * on the difference in the timestamps and the - * frame/field duration. - */ - diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns); - - if (diff == 0 && flags & DRM_CALLED_FROM_VBLIRQ) - DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored." - " diff_ns = %lld, framedur_ns = %d)\n", - pipe, (long long) diff_ns, framedur_ns); - } else { - /* some kind of default for drivers w/o accurate vbl timestamping */ - diff = (flags & DRM_CALLED_FROM_VBLIRQ) != 0; - } - - /* - * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset - * interval? If so then vblank irqs keep running and it will likely - * happen that the hardware vblank counter is not trustworthy as it - * might reset at some point in that interval and vblank timestamps - * are not trustworthy either in that interval. Iow. this can result - * in a bogus diff >> 1 which must be avoided as it would cause - * random large forward jumps of the software vblank counter. - */ - if (diff > 1 && (vblank->inmodeset & 0x2)) { - DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u" - " due to pre-modeset.\n", pipe, diff); - diff = 1; - } - - /* - * FIMXE: Need to replace this hack with proper seqlocks. - * - * Restrict the bump of the software vblank counter to a safe maximum - * value of +1 whenever there is the possibility that concurrent readers - * of vblank timestamps could be active at the moment, as the current - * implementation of the timestamp caching and updating is not safe - * against concurrent readers for calls to store_vblank() with a bump - * of anything but +1. A bump != 1 would very likely return corrupted - * timestamps to userspace, because the same slot in the cache could - * be concurrently written by store_vblank() and read by one of those - * readers without the read-retry logic detecting the collision. - * - * Concurrent readers can exist when we are called from the - * drm_vblank_off() or drm_vblank_on() functions and other non-vblank- - * irq callers. However, all those calls to us are happening with the - * vbl_lock locked to prevent drm_vblank_get(), so the vblank refcount - * can't increase while we are executing. Therefore a zero refcount at - * this point is safe for arbitrary counter bumps if we are called - * outside vblank irq, a non-zero count is not 100% safe. Unfortunately - * we must also accept a refcount of 1, as whenever we are called from - * drm_vblank_get() -> drm_vblank_enable() the refcount will be 1 and - * we must let that one pass through in order to not lose vblank counts - * during vblank irq off - which would completely defeat the whole - * point of this routine. - * - * Whenever we are called from vblank irq, we have to assume concurrent - * readers exist or can show up any time during our execution, even if - * the refcount is currently zero, as vblank irqs are usually only - * enabled due to the presence of readers, and because when we are called - * from vblank irq we can't hold the vbl_lock to protect us from sudden - * bumps in vblank refcount. Therefore also restrict bumps to +1 when - * called from vblank irq. - */ - if ((diff > 1) && (atomic_read(&vblank->refcount) > 1 || - (flags & DRM_CALLED_FROM_VBLIRQ))) { - DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u " - "refcount %u, vblirq %u\n", pipe, diff, - atomic_read(&vblank->refcount), - (flags & DRM_CALLED_FROM_VBLIRQ) != 0); - diff = 1; - } - - DRM_DEBUG_VBL("updating vblank count on crtc %u:" - " current=%u, diff=%u, hw=%u hw_last=%u\n", - pipe, vblank->count, diff, cur_vblank, vblank->last); - - if (diff == 0) { - WARN_ON_ONCE(cur_vblank != vblank->last); - return; - } - - /* - * Only reinitialize corresponding vblank timestamp if high-precision query - * available and didn't fail, or we were called from the vblank interrupt. - * Otherwise reinitialize delayed at next vblank interrupt and assign 0 - * for now, to mark the vblanktimestamp as invalid. - */ - if (!rc && (flags & DRM_CALLED_FROM_VBLIRQ) == 0) - t_vblank = (struct timeval) {0, 0}; - - store_vblank(dev, pipe, diff, &t_vblank, cur_vblank); -} - -/* - * Disable vblank irq's on crtc, make sure that last vblank count - * of hardware and corresponding consistent software vblank counter - * are preserved, even if there are any spurious vblank irq's after - * disable. - */ -static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; - - /* Prevent vblank irq processing while disabling vblank irqs, - * so no updates of timestamps or count can happen after we've - * disabled. Needed to prevent races in case of delayed irq's. - */ - spin_lock_irqsave(&dev->vblank_time_lock, irqflags); - - /* - * Only disable vblank interrupts if they're enabled. This avoids - * calling the ->disable_vblank() operation in atomic context with the - * hardware potentially runtime suspended. - */ - if (vblank->enabled) { - dev->driver->disable_vblank(dev, pipe); - vblank->enabled = false; - } - - /* - * Always update the count and timestamp to maintain the - * appearance that the counter has been ticking all along until - * this time. This makes the count account for the entire time - * between drm_vblank_on() and drm_vblank_off(). - */ - drm_update_vblank_count(dev, pipe, 0); - - spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); -} - -static void vblank_disable_fn(unsigned long arg) -{ - struct drm_vblank_crtc *vblank = (void *)arg; - struct drm_device *dev = vblank->dev; - unsigned int pipe = vblank->pipe; - unsigned long irqflags; - - if (!dev->vblank_disable_allowed) - return; - - spin_lock_irqsave(&dev->vbl_lock, irqflags); - if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) { - DRM_DEBUG("disabling vblank on crtc %u\n", pipe); - vblank_disable_and_save(dev, pipe); - } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); -} - -/** - * drm_vblank_cleanup - cleanup vblank support - * @dev: DRM device - * - * This function cleans up any resources allocated in drm_vblank_init. - */ -void drm_vblank_cleanup(struct drm_device *dev) -{ - unsigned int pipe; - - /* Bail if the driver didn't call drm_vblank_init() */ - if (dev->num_crtcs == 0) - return; - - for (pipe = 0; pipe < dev->num_crtcs; pipe++) { - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - - WARN_ON(vblank->enabled && - drm_core_check_feature(dev, DRIVER_MODESET)); - - del_timer_sync(&vblank->disable_timer); - } - - kfree(dev->vblank); - - dev->num_crtcs = 0; -} -EXPORT_SYMBOL(drm_vblank_cleanup); - -/** - * drm_vblank_init - initialize vblank support - * @dev: DRM device - * @num_crtcs: number of CRTCs supported by @dev - * - * This function initializes vblank support for @num_crtcs display pipelines. - * - * Returns: - * Zero on success or a negative error code on failure. - */ -int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) -{ - int ret = -ENOMEM; - unsigned int i; - - spin_lock_init(&dev->vbl_lock); - spin_lock_init(&dev->vblank_time_lock); - - dev->num_crtcs = num_crtcs; - - dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL); - if (!dev->vblank) - goto err; - - for (i = 0; i < num_crtcs; i++) { - struct drm_vblank_crtc *vblank = &dev->vblank[i]; - - vblank->dev = dev; - vblank->pipe = i; - init_waitqueue_head(&vblank->queue); - setup_timer(&vblank->disable_timer, vblank_disable_fn, - (unsigned long)vblank); - } - - DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n"); - - /* Driver specific high-precision vblank timestamping supported? */ - if (dev->driver->get_vblank_timestamp) - DRM_INFO("Driver supports precise vblank timestamp query.\n"); - else - DRM_INFO("No driver support for vblank timestamp query.\n"); - - /* Must have precise timestamping for reliable vblank instant disable */ - if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) { - dev->vblank_disable_immediate = false; - DRM_INFO("Setting vblank_disable_immediate to false because " - "get_vblank_timestamp == NULL\n"); - } - - dev->vblank_disable_allowed = false; - - return 0; - -err: - dev->num_crtcs = 0; - return ret; -} -EXPORT_SYMBOL(drm_vblank_init); - -static void drm_irq_vgaarb_nokms(void *cookie, bool state) -{ - struct drm_device *dev = cookie; - - if (dev->driver->vgaarb_irq) { - dev->driver->vgaarb_irq(dev, state); - return; - } - - if (!dev->irq_enabled) - return; - - if (state) { - if (dev->driver->irq_uninstall) - dev->driver->irq_uninstall(dev); - } else { - if (dev->driver->irq_preinstall) - dev->driver->irq_preinstall(dev); - if (dev->driver->irq_postinstall) - dev->driver->irq_postinstall(dev); - } -} - -/** - * drm_irq_install - install IRQ handler - * @dev: DRM device - * @irq: IRQ number to install the handler for - * - * Initializes the IRQ related data. Installs the handler, calling the driver - * irq_preinstall() and irq_postinstall() functions before and after the - * installation. - * - * This is the simplified helper interface provided for drivers with no special - * needs. Drivers which need to install interrupt handlers for multiple - * interrupts must instead set drm_device->irq_enabled to signal the DRM core - * that vblank interrupts are available. - * - * Returns: - * Zero on success or a negative error code on failure. - */ -int drm_irq_install(struct drm_device *dev, int irq) -{ - int ret; - unsigned long sh_flags = 0; - - if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) - return -EINVAL; - - if (irq == 0) - return -EINVAL; - - /* Driver must have been initialized */ - if (!dev->dev_private) - return -EINVAL; - - if (dev->irq_enabled) - return -EBUSY; - dev->irq_enabled = true; - - DRM_DEBUG("irq=%d\n", irq); - - /* Before installing handler */ - if (dev->driver->irq_preinstall) - dev->driver->irq_preinstall(dev); - - /* Install handler */ - if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED)) - sh_flags = IRQF_SHARED; - - ret = request_irq(irq, dev->driver->irq_handler, - sh_flags, dev->driver->name, dev); - - if (ret < 0) { - dev->irq_enabled = false; - return ret; - } - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - vga_client_register(dev->pdev, (void *)dev, drm_irq_vgaarb_nokms, NULL); - - /* After installing handler */ - if (dev->driver->irq_postinstall) - ret = dev->driver->irq_postinstall(dev); - - if (ret < 0) { - dev->irq_enabled = false; - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - vga_client_register(dev->pdev, NULL, NULL, NULL); - free_irq(irq, dev); - } else { - dev->irq = irq; - } - - return ret; -} -EXPORT_SYMBOL(drm_irq_install); - -/** - * drm_irq_uninstall - uninstall the IRQ handler - * @dev: DRM device - * - * Calls the driver's irq_uninstall() function and unregisters the IRQ handler. - * This should only be called by drivers which used drm_irq_install() to set up - * their interrupt handler. Other drivers must only reset - * drm_device->irq_enabled to false. - * - * Note that for kernel modesetting drivers it is a bug if this function fails. - * The sanity checks are only to catch buggy user modesetting drivers which call - * the same function through an ioctl. - * - * Returns: - * Zero on success or a negative error code on failure. - */ -int drm_irq_uninstall(struct drm_device *dev) -{ - unsigned long irqflags; - bool irq_enabled; - int i; - - if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) - return -EINVAL; - - irq_enabled = dev->irq_enabled; - dev->irq_enabled = false; - - /* - * Wake up any waiters so they don't hang. This is just to paper over - * isssues for UMS drivers which aren't in full control of their - * vblank/irq handling. KMS drivers must ensure that vblanks are all - * disabled when uninstalling the irq handler. - */ - if (dev->num_crtcs) { - spin_lock_irqsave(&dev->vbl_lock, irqflags); - for (i = 0; i < dev->num_crtcs; i++) { - struct drm_vblank_crtc *vblank = &dev->vblank[i]; - - if (!vblank->enabled) - continue; - - WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET)); - - vblank_disable_and_save(dev, i); - wake_up(&vblank->queue); - } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - } - - if (!irq_enabled) - return -EINVAL; - - DRM_DEBUG("irq=%d\n", dev->irq); - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - vga_client_register(dev->pdev, NULL, NULL, NULL); - - if (dev->driver->irq_uninstall) - dev->driver->irq_uninstall(dev); - - free_irq(dev->irq, dev); - - return 0; -} -EXPORT_SYMBOL(drm_irq_uninstall); - -/* - * IRQ control ioctl. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_control structure. - * \return zero on success or a negative number on failure. - * - * Calls irq_install() or irq_uninstall() according to \p arg. - */ -int drm_control(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_control *ctl = data; - int ret = 0, irq; - - /* if we haven't irq we fallback for compatibility reasons - - * this used to be a separate function in drm_dma.h - */ - - if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) - return 0; - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return 0; - /* UMS was only ever support on pci devices. */ - if (WARN_ON(!dev->pdev)) - return -EINVAL; - - switch (ctl->func) { - case DRM_INST_HANDLER: - irq = dev->pdev->irq; - - if (dev->if_version < DRM_IF_VERSION(1, 2) && - ctl->irq != irq) - return -EINVAL; - mutex_lock(&dev->struct_mutex); - ret = drm_irq_install(dev, irq); - mutex_unlock(&dev->struct_mutex); - - return ret; - case DRM_UNINST_HANDLER: - mutex_lock(&dev->struct_mutex); - ret = drm_irq_uninstall(dev); - mutex_unlock(&dev->struct_mutex); - - return ret; - default: - return -EINVAL; - } -} - -/** - * drm_calc_timestamping_constants - calculate vblank timestamp constants - * @crtc: drm_crtc whose timestamp constants should be updated. - * @mode: display mode containing the scanout timings - * - * Calculate and store various constants which are later - * needed by vblank and swap-completion timestamping, e.g, - * by drm_calc_vbltimestamp_from_scanoutpos(). They are - * derived from CRTC's true scanout timing, so they take - * things like panel scaling or other adjustments into account. - */ -void drm_calc_timestamping_constants(struct drm_crtc *crtc, - const struct drm_display_mode *mode) -{ - struct drm_device *dev = crtc->dev; - unsigned int pipe = drm_crtc_index(crtc); - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - int linedur_ns = 0, framedur_ns = 0; - int dotclock = mode->crtc_clock; - - if (!dev->num_crtcs) - return; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - /* Valid dotclock? */ - if (dotclock > 0) { - int frame_size = mode->crtc_htotal * mode->crtc_vtotal; - - /* - * Convert scanline length in pixels and video - * dot clock to line duration and frame duration - * in nanoseconds: - */ - linedur_ns = div_u64((u64) mode->crtc_htotal * 1000000, dotclock); - framedur_ns = div_u64((u64) frame_size * 1000000, dotclock); - - /* - * Fields of interlaced scanout modes are only half a frame duration. - */ - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - framedur_ns /= 2; - } else - DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n", - crtc->base.id); - - vblank->linedur_ns = linedur_ns; - vblank->framedur_ns = framedur_ns; - - DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n", - crtc->base.id, mode->crtc_htotal, - mode->crtc_vtotal, mode->crtc_vdisplay); - DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n", - crtc->base.id, dotclock, framedur_ns, linedur_ns); -} -EXPORT_SYMBOL(drm_calc_timestamping_constants); - -/** - * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper - * @dev: DRM device - * @pipe: index of CRTC whose vblank timestamp to retrieve - * @max_error: Desired maximum allowable error in timestamps (nanosecs) - * On return contains true maximum error of timestamp - * @vblank_time: Pointer to struct timeval which should receive the timestamp - * @flags: Flags to pass to driver: - * 0 = Default, - * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler - * @mode: mode which defines the scanout timings - * - * Implements calculation of exact vblank timestamps from given drm_display_mode - * timings and current video scanout position of a CRTC. This can be called from - * within get_vblank_timestamp() implementation of a kms driver to implement the - * actual timestamping. - * - * Should return timestamps conforming to the OML_sync_control OpenML - * extension specification. The timestamp corresponds to the end of - * the vblank interval, aka start of scanout of topmost-leftmost display - * pixel in the following video frame. - * - * Requires support for optional dev->driver->get_scanout_position() - * in kms driver, plus a bit of setup code to provide a drm_display_mode - * that corresponds to the true scanout timing. - * - * The current implementation only handles standard video modes. It - * returns as no operation if a doublescan or interlaced video mode is - * active. Higher level code is expected to handle this. - * - * Returns: - * Negative value on error, failure or if not supported in current - * video mode: - * - * -EINVAL - Invalid CRTC. - * -EAGAIN - Temporary unavailable, e.g., called before initial modeset. - * -ENOTSUPP - Function not supported in current display mode. - * -EIO - Failed, e.g., due to failed scanout position query. - * - * Returns or'ed positive status flags on success: - * - * DRM_VBLANKTIME_SCANOUTPOS_METHOD - Signal this method used for timestamping. - * DRM_VBLANKTIME_INVBL - Timestamp taken while scanout was in vblank interval. - * - */ -int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, - unsigned int pipe, - int *max_error, - struct timeval *vblank_time, - unsigned flags, - const struct drm_display_mode *mode) -{ - struct timeval tv_etime; - ktime_t stime, etime; - unsigned int vbl_status; - int ret = DRM_VBLANKTIME_SCANOUTPOS_METHOD; - int vpos, hpos, i; - int delta_ns, duration_ns; - - if (pipe >= dev->num_crtcs) { - DRM_ERROR("Invalid crtc %u\n", pipe); - return -EINVAL; - } - - /* Scanout position query not supported? Should not happen. */ - if (!dev->driver->get_scanout_position) { - DRM_ERROR("Called from driver w/o get_scanout_position()!?\n"); - return -EIO; - } - - /* If mode timing undefined, just return as no-op: - * Happens during initial modesetting of a crtc. - */ - if (mode->crtc_clock == 0) { - DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe); - return -EAGAIN; - } - - /* Get current scanout position with system timestamp. - * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times - * if single query takes longer than max_error nanoseconds. - * - * This guarantees a tight bound on maximum error if - * code gets preempted or delayed for some reason. - */ - for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) { - /* - * Get vertical and horizontal scanout position vpos, hpos, - * and bounding timestamps stime, etime, pre/post query. - */ - vbl_status = dev->driver->get_scanout_position(dev, pipe, flags, - &vpos, &hpos, - &stime, &etime, - mode); - - /* Return as no-op if scanout query unsupported or failed. */ - if (!(vbl_status & DRM_SCANOUTPOS_VALID)) { - DRM_DEBUG("crtc %u : scanoutpos query failed [0x%x].\n", - pipe, vbl_status); - return -EIO; - } - - /* Compute uncertainty in timestamp of scanout position query. */ - duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime); - - /* Accept result with < max_error nsecs timing uncertainty. */ - if (duration_ns <= *max_error) - break; - } - - /* Noisy system timing? */ - if (i == DRM_TIMESTAMP_MAXRETRIES) { - DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n", - pipe, duration_ns/1000, *max_error/1000, i); - } - - /* Return upper bound of timestamp precision error. */ - *max_error = duration_ns; - - /* Check if in vblank area: - * vpos is >=0 in video scanout area, but negative - * within vblank area, counting down the number of lines until - * start of scanout. - */ - if (vbl_status & DRM_SCANOUTPOS_IN_VBLANK) - ret |= DRM_VBLANKTIME_IN_VBLANK; - - /* Convert scanout position into elapsed time at raw_time query - * since start of scanout at first display scanline. delta_ns - * can be negative if start of scanout hasn't happened yet. - */ - delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos), - mode->crtc_clock); - - if (!drm_timestamp_monotonic) - etime = ktime_mono_to_real(etime); - - /* save this only for debugging purposes */ - tv_etime = ktime_to_timeval(etime); - /* Subtract time delta from raw timestamp to get final - * vblank_time timestamp for end of vblank. - */ - if (delta_ns < 0) - etime = ktime_add_ns(etime, -delta_ns); - else - etime = ktime_sub_ns(etime, delta_ns); - *vblank_time = ktime_to_timeval(etime); - - DRM_DEBUG_VBL("crtc %u : v 0x%x p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n", - pipe, vbl_status, hpos, vpos, - (long)tv_etime.tv_sec, (long)tv_etime.tv_usec, - (long)vblank_time->tv_sec, (long)vblank_time->tv_usec, - duration_ns/1000, i); - - return ret; -} -EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); - -static struct timeval get_drm_timestamp(void) -{ - ktime_t now; - - now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real(); - return ktime_to_timeval(now); -} - -/** - * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent - * vblank interval - * @dev: DRM device - * @pipe: index of CRTC whose vblank timestamp to retrieve - * @tvblank: Pointer to target struct timeval which should receive the timestamp - * @flags: Flags to pass to driver: - * 0 = Default, - * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler - * - * Fetches the system timestamp corresponding to the time of the most recent - * vblank interval on specified CRTC. May call into kms-driver to - * compute the timestamp with a high-precision GPU specific method. - * - * Returns zero if timestamp originates from uncorrected do_gettimeofday() - * call, i.e., it isn't very precisely locked to the true vblank. - * - * Returns: - * True if timestamp is considered to be very precise, false otherwise. - */ -static bool -drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, - struct timeval *tvblank, unsigned flags) -{ - int ret; - - /* Define requested maximum error on timestamps (nanoseconds). */ - int max_error = (int) drm_timestamp_precision * 1000; - - /* Query driver if possible and precision timestamping enabled. */ - if (dev->driver->get_vblank_timestamp && (max_error > 0)) { - ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error, - tvblank, flags); - if (ret > 0) - return true; - } - - /* GPU high precision timestamp query unsupported or failed. - * Return current monotonic/gettimeofday timestamp as best estimate. - */ - *tvblank = get_drm_timestamp(); - - return false; -} - -/** - * drm_vblank_count - retrieve "cooked" vblank counter value - * @dev: DRM device - * @pipe: index of CRTC for which to retrieve the counter - * - * Fetches the "cooked" vblank count value that represents the number of - * vblank events since the system was booted, including lost events due to - * modesetting activity. - * - * This is the legacy version of drm_crtc_vblank_count(). - * - * Returns: - * The software vblank counter. - */ -u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return 0; - - return vblank->count; -} -EXPORT_SYMBOL(drm_vblank_count); - -/** - * drm_crtc_vblank_count - retrieve "cooked" vblank counter value - * @crtc: which counter to retrieve - * - * Fetches the "cooked" vblank count value that represents the number of - * vblank events since the system was booted, including lost events due to - * modesetting activity. - * - * This is the native KMS version of drm_vblank_count(). - * - * Returns: - * The software vblank counter. - */ -u32 drm_crtc_vblank_count(struct drm_crtc *crtc) -{ - return drm_vblank_count(crtc->dev, drm_crtc_index(crtc)); -} -EXPORT_SYMBOL(drm_crtc_vblank_count); - -/** - * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the - * system timestamp corresponding to that vblank counter value. - * @dev: DRM device - * @pipe: index of CRTC whose counter to retrieve - * @vblanktime: Pointer to struct timeval to receive the vblank timestamp. - * - * Fetches the "cooked" vblank count value that represents the number of - * vblank events since the system was booted, including lost events due to - * modesetting activity. Returns corresponding system timestamp of the time - * of the vblank interval that corresponds to the current vblank counter value. - * - * This is the legacy version of drm_crtc_vblank_count_and_time(). - */ -u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, - struct timeval *vblanktime) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - int count = DRM_TIMESTAMP_MAXRETRIES; - u32 cur_vblank; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return 0; - - /* - * Vblank timestamps are read lockless. To ensure consistency the vblank - * counter is rechecked and ordering is ensured using memory barriers. - * This works like a seqlock. The write-side barriers are in store_vblank. - */ - do { - cur_vblank = vblank->count; - smp_rmb(); - *vblanktime = vblanktimestamp(dev, pipe, cur_vblank); - smp_rmb(); - } while (cur_vblank != vblank->count && --count > 0); - - return cur_vblank; -} -EXPORT_SYMBOL(drm_vblank_count_and_time); - -/** - * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value - * and the system timestamp corresponding to that vblank counter value - * @crtc: which counter to retrieve - * @vblanktime: Pointer to struct timeval to receive the vblank timestamp. - * - * Fetches the "cooked" vblank count value that represents the number of - * vblank events since the system was booted, including lost events due to - * modesetting activity. Returns corresponding system timestamp of the time - * of the vblank interval that corresponds to the current vblank counter value. - * - * This is the native KMS version of drm_vblank_count_and_time(). - */ -u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, - struct timeval *vblanktime) -{ - return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc), - vblanktime); -} -EXPORT_SYMBOL(drm_crtc_vblank_count_and_time); - -static void send_vblank_event(struct drm_device *dev, - struct drm_pending_vblank_event *e, - unsigned long seq, struct timeval *now) -{ - assert_spin_locked(&dev->event_lock); - - e->event.sequence = seq; - e->event.tv_sec = now->tv_sec; - e->event.tv_usec = now->tv_usec; - - list_add_tail(&e->base.link, - &e->base.file_priv->event_list); - wake_up_interruptible(&e->base.file_priv->event_wait); - trace_drm_vblank_event_delivered(e->base.pid, e->pipe, - e->event.sequence); -} - -/** - * drm_arm_vblank_event - arm vblank event after pageflip - * @dev: DRM device - * @pipe: CRTC index - * @e: the event to prepare to send - * - * A lot of drivers need to generate vblank events for the very next vblank - * interrupt. For example when the page flip interrupt happens when the page - * flip gets armed, but not when it actually executes within the next vblank - * period. This helper function implements exactly the required vblank arming - * behaviour. - * - * Caller must hold event lock. Caller must also hold a vblank reference for - * the event @e, which will be dropped when the next vblank arrives. - * - * This is the legacy version of drm_crtc_arm_vblank_event(). - */ -void drm_arm_vblank_event(struct drm_device *dev, unsigned int pipe, - struct drm_pending_vblank_event *e) -{ - assert_spin_locked(&dev->event_lock); - - e->pipe = pipe; - e->event.sequence = drm_vblank_count(dev, pipe); - list_add_tail(&e->base.link, &dev->vblank_event_list); -} -EXPORT_SYMBOL(drm_arm_vblank_event); - -/** - * drm_crtc_arm_vblank_event - arm vblank event after pageflip - * @crtc: the source CRTC of the vblank event - * @e: the event to send - * - * A lot of drivers need to generate vblank events for the very next vblank - * interrupt. For example when the page flip interrupt happens when the page - * flip gets armed, but not when it actually executes within the next vblank - * period. This helper function implements exactly the required vblank arming - * behaviour. - * - * Caller must hold event lock. Caller must also hold a vblank reference for - * the event @e, which will be dropped when the next vblank arrives. - * - * This is the native KMS version of drm_arm_vblank_event(). - */ -void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, - struct drm_pending_vblank_event *e) -{ - drm_arm_vblank_event(crtc->dev, drm_crtc_index(crtc), e); -} -EXPORT_SYMBOL(drm_crtc_arm_vblank_event); - -/** - * drm_send_vblank_event - helper to send vblank event after pageflip - * @dev: DRM device - * @pipe: CRTC index - * @e: the event to send - * - * Updates sequence # and timestamp on event, and sends it to userspace. - * Caller must hold event lock. - * - * This is the legacy version of drm_crtc_send_vblank_event(). - */ -void drm_send_vblank_event(struct drm_device *dev, unsigned int pipe, - struct drm_pending_vblank_event *e) -{ - struct timeval now; - unsigned int seq; - - if (dev->num_crtcs > 0) { - seq = drm_vblank_count_and_time(dev, pipe, &now); - } else { - seq = 0; - - now = get_drm_timestamp(); - } - e->pipe = pipe; - send_vblank_event(dev, e, seq, &now); -} -EXPORT_SYMBOL(drm_send_vblank_event); - -/** - * drm_crtc_send_vblank_event - helper to send vblank event after pageflip - * @crtc: the source CRTC of the vblank event - * @e: the event to send - * - * Updates sequence # and timestamp on event, and sends it to userspace. - * Caller must hold event lock. - * - * This is the native KMS version of drm_send_vblank_event(). - */ -void drm_crtc_send_vblank_event(struct drm_crtc *crtc, - struct drm_pending_vblank_event *e) -{ - drm_send_vblank_event(crtc->dev, drm_crtc_index(crtc), e); -} -EXPORT_SYMBOL(drm_crtc_send_vblank_event); - -/** - * drm_vblank_enable - enable the vblank interrupt on a CRTC - * @dev: DRM device - * @pipe: CRTC index - * - * Returns: - * Zero on success or a negative error code on failure. - */ -static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - int ret = 0; - - assert_spin_locked(&dev->vbl_lock); - - spin_lock(&dev->vblank_time_lock); - - if (!vblank->enabled) { - /* - * Enable vblank irqs under vblank_time_lock protection. - * All vblank count & timestamp updates are held off - * until we are done reinitializing master counter and - * timestamps. Filtercode in drm_handle_vblank() will - * prevent double-accounting of same vblank interval. - */ - ret = dev->driver->enable_vblank(dev, pipe); - DRM_DEBUG("enabling vblank on crtc %u, ret: %d\n", pipe, ret); - if (ret) - atomic_dec(&vblank->refcount); - else { - vblank->enabled = true; - drm_update_vblank_count(dev, pipe, 0); - } - } - - spin_unlock(&dev->vblank_time_lock); - - return ret; -} - -/** - * drm_vblank_get - get a reference count on vblank events - * @dev: DRM device - * @pipe: index of CRTC to own - * - * Acquire a reference count on vblank events to avoid having them disabled - * while in use. - * - * This is the legacy version of drm_crtc_vblank_get(). - * - * Returns: - * Zero on success or a negative error code on failure. - */ -int drm_vblank_get(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; - int ret = 0; - - if (!dev->num_crtcs) - return -EINVAL; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return -EINVAL; - - spin_lock_irqsave(&dev->vbl_lock, irqflags); - /* Going from 0->1 means we have to enable interrupts again */ - if (atomic_add_return(1, &vblank->refcount) == 1) { - ret = drm_vblank_enable(dev, pipe); - } else { - if (!vblank->enabled) { - atomic_dec(&vblank->refcount); - ret = -EINVAL; - } - } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - - return ret; -} -EXPORT_SYMBOL(drm_vblank_get); - -/** - * drm_crtc_vblank_get - get a reference count on vblank events - * @crtc: which CRTC to own - * - * Acquire a reference count on vblank events to avoid having them disabled - * while in use. - * - * This is the native kms version of drm_vblank_get(). - * - * Returns: - * Zero on success or a negative error code on failure. - */ -int drm_crtc_vblank_get(struct drm_crtc *crtc) -{ - return drm_vblank_get(crtc->dev, drm_crtc_index(crtc)); -} -EXPORT_SYMBOL(drm_crtc_vblank_get); - -/** - * drm_vblank_put - release ownership of vblank events - * @dev: DRM device - * @pipe: index of CRTC to release - * - * Release ownership of a given vblank counter, turning off interrupts - * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. - * - * This is the legacy version of drm_crtc_vblank_put(). - */ -void drm_vblank_put(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - if (WARN_ON(atomic_read(&vblank->refcount) == 0)) - return; - - /* Last user schedules interrupt disable */ - if (atomic_dec_and_test(&vblank->refcount)) { - if (drm_vblank_offdelay == 0) - return; - else if (drm_vblank_offdelay < 0) - vblank_disable_fn((unsigned long)vblank); - else if (!dev->vblank_disable_immediate) - mod_timer(&vblank->disable_timer, - jiffies + ((drm_vblank_offdelay * HZ)/1000)); - } -} -EXPORT_SYMBOL(drm_vblank_put); - -/** - * drm_crtc_vblank_put - give up ownership of vblank events - * @crtc: which counter to give up - * - * Release ownership of a given vblank counter, turning off interrupts - * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. - * - * This is the native kms version of drm_vblank_put(). - */ -void drm_crtc_vblank_put(struct drm_crtc *crtc) -{ - drm_vblank_put(crtc->dev, drm_crtc_index(crtc)); -} -EXPORT_SYMBOL(drm_crtc_vblank_put); - -/** - * drm_wait_one_vblank - wait for one vblank - * @dev: DRM device - * @pipe: CRTC index - * - * This waits for one vblank to pass on @pipe, using the irq driver interfaces. - * It is a failure to call this when the vblank irq for @pipe is disabled, e.g. - * due to lack of driver support or because the crtc is off. - */ -void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - int ret; - u32 last; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - ret = drm_vblank_get(dev, pipe); - if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", pipe, ret)) - return; - - last = drm_vblank_count(dev, pipe); - - ret = wait_event_timeout(vblank->queue, - last != drm_vblank_count(dev, pipe), - msecs_to_jiffies(100)); - - WARN(ret == 0, "vblank wait timed out on crtc %i\n", pipe); - - drm_vblank_put(dev, pipe); -} -EXPORT_SYMBOL(drm_wait_one_vblank); - -/** - * drm_crtc_wait_one_vblank - wait for one vblank - * @crtc: DRM crtc - * - * This waits for one vblank to pass on @crtc, using the irq driver interfaces. - * It is a failure to call this when the vblank irq for @crtc is disabled, e.g. - * due to lack of driver support or because the crtc is off. - */ -void drm_crtc_wait_one_vblank(struct drm_crtc *crtc) -{ - drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc)); -} -EXPORT_SYMBOL(drm_crtc_wait_one_vblank); - -/** - * drm_vblank_off - disable vblank events on a CRTC - * @dev: DRM device - * @pipe: CRTC index - * - * Drivers can use this function to shut down the vblank interrupt handling when - * disabling a crtc. This function ensures that the latest vblank frame count is - * stored so that drm_vblank_on() can restore it again. - * - * Drivers must use this function when the hardware vblank counter can get - * reset, e.g. when suspending. - * - * This is the legacy version of drm_crtc_vblank_off(). - */ -void drm_vblank_off(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - struct drm_pending_vblank_event *e, *t; - struct timeval now; - unsigned long irqflags; - unsigned int seq; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - spin_lock_irqsave(&dev->event_lock, irqflags); - - spin_lock(&dev->vbl_lock); - DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n", - pipe, vblank->enabled, vblank->inmodeset); - - /* Avoid redundant vblank disables without previous drm_vblank_on(). */ - if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset) - vblank_disable_and_save(dev, pipe); - - wake_up(&vblank->queue); - - /* - * Prevent subsequent drm_vblank_get() from re-enabling - * the vblank interrupt by bumping the refcount. - */ - if (!vblank->inmodeset) { - atomic_inc(&vblank->refcount); - vblank->inmodeset = 1; - } - spin_unlock(&dev->vbl_lock); - - /* Send any queued vblank events, lest the natives grow disquiet */ - seq = drm_vblank_count_and_time(dev, pipe, &now); - - list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { - if (e->pipe != pipe) - continue; - DRM_DEBUG("Sending premature vblank event on disable: " - "wanted %d, current %d\n", - e->event.sequence, seq); - list_del(&e->base.link); - drm_vblank_put(dev, pipe); - send_vblank_event(dev, e, seq, &now); - } - spin_unlock_irqrestore(&dev->event_lock, irqflags); -} -EXPORT_SYMBOL(drm_vblank_off); - -/** - * drm_crtc_vblank_off - disable vblank events on a CRTC - * @crtc: CRTC in question - * - * Drivers can use this function to shut down the vblank interrupt handling when - * disabling a crtc. This function ensures that the latest vblank frame count is - * stored so that drm_vblank_on can restore it again. - * - * Drivers must use this function when the hardware vblank counter can get - * reset, e.g. when suspending. - * - * This is the native kms version of drm_vblank_off(). - */ -void drm_crtc_vblank_off(struct drm_crtc *crtc) -{ - drm_vblank_off(crtc->dev, drm_crtc_index(crtc)); -} -EXPORT_SYMBOL(drm_crtc_vblank_off); - -/** - * drm_crtc_vblank_reset - reset vblank state to off on a CRTC - * @crtc: CRTC in question - * - * Drivers can use this function to reset the vblank state to off at load time. - * Drivers should use this together with the drm_crtc_vblank_off() and - * drm_crtc_vblank_on() functions. The difference compared to - * drm_crtc_vblank_off() is that this function doesn't save the vblank counter - * and hence doesn't need to call any driver hooks. - */ -void drm_crtc_vblank_reset(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - unsigned long irqflags; - unsigned int pipe = drm_crtc_index(crtc); - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - - spin_lock_irqsave(&dev->vbl_lock, irqflags); - /* - * Prevent subsequent drm_vblank_get() from enabling the vblank - * interrupt by bumping the refcount. - */ - if (!vblank->inmodeset) { - atomic_inc(&vblank->refcount); - vblank->inmodeset = 1; - } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - - WARN_ON(!list_empty(&dev->vblank_event_list)); -} -EXPORT_SYMBOL(drm_crtc_vblank_reset); - -/** - * drm_vblank_on - enable vblank events on a CRTC - * @dev: DRM device - * @pipe: CRTC index - * - * This functions restores the vblank interrupt state captured with - * drm_vblank_off() again. Note that calls to drm_vblank_on() and - * drm_vblank_off() can be unbalanced and so can also be unconditionally called - * in driver load code to reflect the current hardware state of the crtc. - * - * This is the legacy version of drm_crtc_vblank_on(). - */ -void drm_vblank_on(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - spin_lock_irqsave(&dev->vbl_lock, irqflags); - DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n", - pipe, vblank->enabled, vblank->inmodeset); - - /* Drop our private "prevent drm_vblank_get" refcount */ - if (vblank->inmodeset) { - atomic_dec(&vblank->refcount); - vblank->inmodeset = 0; - } - - drm_reset_vblank_timestamp(dev, pipe); - - /* - * re-enable interrupts if there are users left, or the - * user wishes vblank interrupts to be enabled all the time. - */ - if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0) - WARN_ON(drm_vblank_enable(dev, pipe)); - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); -} -EXPORT_SYMBOL(drm_vblank_on); - -/** - * drm_crtc_vblank_on - enable vblank events on a CRTC - * @crtc: CRTC in question - * - * This functions restores the vblank interrupt state captured with - * drm_vblank_off() again. Note that calls to drm_vblank_on() and - * drm_vblank_off() can be unbalanced and so can also be unconditionally called - * in driver load code to reflect the current hardware state of the crtc. - * - * This is the native kms version of drm_vblank_on(). - */ -void drm_crtc_vblank_on(struct drm_crtc *crtc) -{ - drm_vblank_on(crtc->dev, drm_crtc_index(crtc)); -} -EXPORT_SYMBOL(drm_crtc_vblank_on); - -/** - * drm_vblank_pre_modeset - account for vblanks across mode sets - * @dev: DRM device - * @pipe: CRTC index - * - * Account for vblank events across mode setting events, which will likely - * reset the hardware frame counter. - * - * This is done by grabbing a temporary vblank reference to ensure that the - * vblank interrupt keeps running across the modeset sequence. With this the - * software-side vblank frame counting will ensure that there are no jumps or - * discontinuities. - * - * Unfortunately this approach is racy and also doesn't work when the vblank - * interrupt stops running, e.g. across system suspend resume. It is therefore - * highly recommended that drivers use the newer drm_vblank_off() and - * drm_vblank_on() instead. drm_vblank_pre_modeset() only works correctly when - * using "cooked" software vblank frame counters and not relying on any hardware - * counters. - * - * Drivers must call drm_vblank_post_modeset() when re-enabling the same crtc - * again. - */ -void drm_vblank_pre_modeset(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - - /* vblank is not initialized (IRQ not installed ?), or has been freed */ - if (!dev->num_crtcs) - return; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - /* - * To avoid all the problems that might happen if interrupts - * were enabled/disabled around or between these calls, we just - * have the kernel take a reference on the CRTC (just once though - * to avoid corrupting the count if multiple, mismatch calls occur), - * so that interrupts remain enabled in the interim. - */ - if (!vblank->inmodeset) { - vblank->inmodeset = 0x1; - if (drm_vblank_get(dev, pipe) == 0) - vblank->inmodeset |= 0x2; - } -} -EXPORT_SYMBOL(drm_vblank_pre_modeset); - -/** - * drm_vblank_post_modeset - undo drm_vblank_pre_modeset changes - * @dev: DRM device - * @pipe: CRTC index - * - * This function again drops the temporary vblank reference acquired in - * drm_vblank_pre_modeset. - */ -void drm_vblank_post_modeset(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; - - /* vblank is not initialized (IRQ not installed ?), or has been freed */ - if (!dev->num_crtcs) - return; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - if (vblank->inmodeset) { - spin_lock_irqsave(&dev->vbl_lock, irqflags); - dev->vblank_disable_allowed = true; - drm_reset_vblank_timestamp(dev, pipe); - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - - if (vblank->inmodeset & 0x2) - drm_vblank_put(dev, pipe); - - vblank->inmodeset = 0; - } -} -EXPORT_SYMBOL(drm_vblank_post_modeset); - -/* - * drm_modeset_ctl - handle vblank event counter changes across mode switch - * @DRM_IOCTL_ARGS: standard ioctl arguments - * - * Applications should call the %_DRM_PRE_MODESET and %_DRM_POST_MODESET - * ioctls around modesetting so that any lost vblank events are accounted for. - * - * Generally the counter will reset across mode sets. If interrupts are - * enabled around this call, we don't have to do anything since the counter - * will have already been incremented. - */ -int drm_modeset_ctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_modeset_ctl *modeset = data; - unsigned int pipe; - - /* If drm_vblank_init() hasn't been called yet, just no-op */ - if (!dev->num_crtcs) - return 0; - - /* KMS drivers handle this internally */ - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return 0; - - pipe = modeset->crtc; - if (pipe >= dev->num_crtcs) - return -EINVAL; - - switch (modeset->cmd) { - case _DRM_PRE_MODESET: - drm_vblank_pre_modeset(dev, pipe); - break; - case _DRM_POST_MODESET: - drm_vblank_post_modeset(dev, pipe); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, - union drm_wait_vblank *vblwait, - struct drm_file *file_priv) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - struct drm_pending_vblank_event *e; - struct timeval now; - unsigned long flags; - unsigned int seq; - int ret; - - e = kzalloc(sizeof(*e), GFP_KERNEL); - if (e == NULL) { - ret = -ENOMEM; - goto err_put; - } - - e->pipe = pipe; - e->base.pid = current->pid; - e->event.base.type = DRM_EVENT_VBLANK; - e->event.base.length = sizeof(e->event); - e->event.user_data = vblwait->request.signal; - e->base.event = &e->event.base; - e->base.file_priv = file_priv; - e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; - - spin_lock_irqsave(&dev->event_lock, flags); - - /* - * drm_vblank_off() might have been called after we called - * drm_vblank_get(). drm_vblank_off() holds event_lock - * around the vblank disable, so no need for further locking. - * The reference from drm_vblank_get() protects against - * vblank disable from another source. - */ - if (!vblank->enabled) { - ret = -EINVAL; - goto err_unlock; - } - - if (file_priv->event_space < sizeof(e->event)) { - ret = -EBUSY; - goto err_unlock; - } - - file_priv->event_space -= sizeof(e->event); - seq = drm_vblank_count_and_time(dev, pipe, &now); - - if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) && - (seq - vblwait->request.sequence) <= (1 << 23)) { - vblwait->request.sequence = seq + 1; - vblwait->reply.sequence = vblwait->request.sequence; - } - - DRM_DEBUG("event on vblank count %d, current %d, crtc %u\n", - vblwait->request.sequence, seq, pipe); - - trace_drm_vblank_event_queued(current->pid, pipe, - vblwait->request.sequence); - - e->event.sequence = vblwait->request.sequence; - if ((seq - vblwait->request.sequence) <= (1 << 23)) { - drm_vblank_put(dev, pipe); - send_vblank_event(dev, e, seq, &now); - vblwait->reply.sequence = seq; - } else { - /* drm_handle_vblank_events will call drm_vblank_put */ - list_add_tail(&e->base.link, &dev->vblank_event_list); - vblwait->reply.sequence = vblwait->request.sequence; - } - - spin_unlock_irqrestore(&dev->event_lock, flags); - - return 0; - -err_unlock: - spin_unlock_irqrestore(&dev->event_lock, flags); - kfree(e); -err_put: - drm_vblank_put(dev, pipe); - return ret; -} - -/* - * Wait for VBLANK. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param data user argument, pointing to a drm_wait_vblank structure. - * \return zero on success or a negative number on failure. - * - * This function enables the vblank interrupt on the pipe requested, then - * sleeps waiting for the requested sequence number to occur, and drops - * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that - * after a timeout with no further vblank waits scheduled). - */ -int drm_wait_vblank(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_vblank_crtc *vblank; - union drm_wait_vblank *vblwait = data; - int ret; - unsigned int flags, seq, pipe, high_pipe; - - if (!dev->irq_enabled) - return -EINVAL; - - if (vblwait->request.type & _DRM_VBLANK_SIGNAL) - return -EINVAL; - - if (vblwait->request.type & - ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK | - _DRM_VBLANK_HIGH_CRTC_MASK)) { - DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n", - vblwait->request.type, - (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK | - _DRM_VBLANK_HIGH_CRTC_MASK)); - return -EINVAL; - } - - flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; - high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK); - if (high_pipe) - pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT; - else - pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; - if (pipe >= dev->num_crtcs) - return -EINVAL; - - vblank = &dev->vblank[pipe]; - - ret = drm_vblank_get(dev, pipe); - if (ret) { - DRM_DEBUG("failed to acquire vblank counter, %d\n", ret); - return ret; - } - seq = drm_vblank_count(dev, pipe); - - switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { - case _DRM_VBLANK_RELATIVE: - vblwait->request.sequence += seq; - vblwait->request.type &= ~_DRM_VBLANK_RELATIVE; - case _DRM_VBLANK_ABSOLUTE: - break; - default: - ret = -EINVAL; - goto done; - } - - if (flags & _DRM_VBLANK_EVENT) { - /* must hold on to the vblank ref until the event fires - * drm_vblank_put will be called asynchronously - */ - return drm_queue_vblank_event(dev, pipe, vblwait, file_priv); - } - - if ((flags & _DRM_VBLANK_NEXTONMISS) && - (seq - vblwait->request.sequence) <= (1<<23)) { - vblwait->request.sequence = seq + 1; - } - - DRM_DEBUG("waiting on vblank count %d, crtc %u\n", - vblwait->request.sequence, pipe); - vblank->last_wait = vblwait->request.sequence; - DRM_WAIT_ON(ret, vblank->queue, 3 * HZ, - (((drm_vblank_count(dev, pipe) - - vblwait->request.sequence) <= (1 << 23)) || - !vblank->enabled || - !dev->irq_enabled)); - - if (ret != -EINTR) { - struct timeval now; - - vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now); - vblwait->reply.tval_sec = now.tv_sec; - vblwait->reply.tval_usec = now.tv_usec; - - DRM_DEBUG("returning %d to client\n", - vblwait->reply.sequence); - } else { - DRM_DEBUG("vblank wait interrupted by signal\n"); - } - -done: - drm_vblank_put(dev, pipe); - return ret; -} - -static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) -{ - struct drm_pending_vblank_event *e, *t; - struct timeval now; - unsigned int seq; - - assert_spin_locked(&dev->event_lock); - - seq = drm_vblank_count_and_time(dev, pipe, &now); - - list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { - if (e->pipe != pipe) - continue; - if ((seq - e->event.sequence) > (1<<23)) - continue; - - DRM_DEBUG("vblank event on %d, current %d\n", - e->event.sequence, seq); - - list_del(&e->base.link); - drm_vblank_put(dev, pipe); - send_vblank_event(dev, e, seq, &now); - } - - trace_drm_vblank_event(pipe, seq); -} - -/** - * drm_handle_vblank - handle a vblank event - * @dev: DRM device - * @pipe: index of CRTC where this event occurred - * - * Drivers should call this routine in their vblank interrupt handlers to - * update the vblank counter and send any signals that may be pending. - * - * This is the legacy version of drm_crtc_handle_vblank(). - */ -bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; - - if (WARN_ON_ONCE(!dev->num_crtcs)) - return false; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return false; - - spin_lock_irqsave(&dev->event_lock, irqflags); - - /* Need timestamp lock to prevent concurrent execution with - * vblank enable/disable, as this would cause inconsistent - * or corrupted timestamps and vblank counts. - */ - spin_lock(&dev->vblank_time_lock); - - /* Vblank irq handling disabled. Nothing to do. */ - if (!vblank->enabled) { - spin_unlock(&dev->vblank_time_lock); - spin_unlock_irqrestore(&dev->event_lock, irqflags); - return false; - } - - drm_update_vblank_count(dev, pipe, DRM_CALLED_FROM_VBLIRQ); - - spin_unlock(&dev->vblank_time_lock); - - wake_up(&vblank->queue); - drm_handle_vblank_events(dev, pipe); - - /* With instant-off, we defer disabling the interrupt until after - * we finish processing the following vblank. The disable has to - * be last (after drm_handle_vblank_events) so that the timestamp - * is always accurate. - */ - if (dev->vblank_disable_immediate && - drm_vblank_offdelay > 0 && - !atomic_read(&vblank->refcount)) - vblank_disable_fn((unsigned long)vblank); - - spin_unlock_irqrestore(&dev->event_lock, irqflags); - - return true; -} -EXPORT_SYMBOL(drm_handle_vblank); - -/** - * drm_crtc_handle_vblank - handle a vblank event - * @crtc: where this event occurred - * - * Drivers should call this routine in their vblank interrupt handlers to - * update the vblank counter and send any signals that may be pending. - * - * This is the native KMS version of drm_handle_vblank(). - * - * Returns: - * True if the event was successfully handled, false on failure. - */ -bool drm_crtc_handle_vblank(struct drm_crtc *crtc) -{ - return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc)); -} -EXPORT_SYMBOL(drm_crtc_handle_vblank); - -/** - * drm_vblank_no_hw_counter - "No hw counter" implementation of .get_vblank_counter() - * @dev: DRM device - * @pipe: CRTC for which to read the counter - * - * Drivers can plug this into the .get_vblank_counter() function if - * there is no useable hardware frame counter available. - * - * Returns: - * 0 - */ -u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) -{ - return 0; -} -EXPORT_SYMBOL(drm_vblank_no_hw_counter); diff --git a/src/4.x/drivers/gpu/drm/drm_legacy.h b/src/4.x/drivers/gpu/drm/drm_legacy.h deleted file mode 100644 index 9b731786e..000000000 --- a/src/4.x/drivers/gpu/drm/drm_legacy.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef __DRM_LEGACY_H__ -#define __DRM_LEGACY_H__ - -/* - * Copyright (c) 2014 David Herrmann - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * This file contains legacy interfaces that modern drm drivers - * should no longer be using. They cannot be removed as legacy - * drivers use them, and removing them are API breaks. - */ -#include -#include - -struct agp_memory; -struct drm_device; -struct drm_file; - -/* - * Generic DRM Contexts - */ - -#define DRM_KERNEL_CONTEXT 0 -#define DRM_RESERVED_CONTEXTS 1 - -void drm_legacy_ctxbitmap_init(struct drm_device *dev); -void drm_legacy_ctxbitmap_cleanup(struct drm_device *dev); -void drm_legacy_ctxbitmap_free(struct drm_device *dev, int ctx_handle); -void drm_legacy_ctxbitmap_flush(struct drm_device *dev, struct drm_file *file); - -int drm_legacy_resctx(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_addctx(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_getctx(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_switchctx(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_newctx(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_rmctx(struct drm_device *d, void *v, struct drm_file *f); - -int drm_legacy_setsareactx(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_getsareactx(struct drm_device *d, void *v, struct drm_file *f); - -/* - * Generic Buffer Management - */ - -#define DRM_MAP_HASH_OFFSET 0x10000000 - -int drm_legacy_addmap_ioctl(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_rmmap_ioctl(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_addbufs(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_infobufs(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_markbufs(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_freebufs(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_mapbufs(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_dma_ioctl(struct drm_device *d, void *v, struct drm_file *f); - -void drm_legacy_vma_flush(struct drm_device *d); - -/* - * AGP Support - */ - -struct drm_agp_mem { - unsigned long handle; - struct agp_memory *memory; - unsigned long bound; - int pages; - struct list_head head; -}; - -/* - * Generic Userspace Locking-API - */ - -int drm_legacy_i_have_hw_lock(struct drm_device *d, struct drm_file *f); -int drm_legacy_lock(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_unlock(struct drm_device *d, void *v, struct drm_file *f); -int drm_legacy_lock_free(struct drm_lock_data *lock, unsigned int ctx); - -/* DMA support */ -int drm_legacy_dma_setup(struct drm_device *dev); -void drm_legacy_dma_takedown(struct drm_device *dev); -void drm_legacy_free_buffer(struct drm_device *dev, - struct drm_buf * buf); -void drm_legacy_reclaim_buffers(struct drm_device *dev, - struct drm_file *filp); - -/* Scatter Gather Support */ -void drm_legacy_sg_cleanup(struct drm_device *dev); -int drm_legacy_sg_alloc(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_legacy_sg_free(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -#endif /* __DRM_LEGACY_H__ */ diff --git a/src/4.x/drivers/gpu/drm/drm_lock.c b/src/4.x/drivers/gpu/drm/drm_lock.c deleted file mode 100644 index daa2ff121..000000000 --- a/src/4.x/drivers/gpu/drm/drm_lock.c +++ /dev/null @@ -1,340 +0,0 @@ -/** - * \file drm_lock.c - * IOCTLs for locking - * - * \author Rickard E. (Rik) Faith - * \author Gareth Hughes - */ - -/* - * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include "drm_legacy.h" -#include "drm_internal.h" - -static int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context); - -/** - * Lock ioctl. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_lock structure. - * \return zero on success or negative number on failure. - * - * Add the current task to the lock wait queue, and attempt to take to lock. - */ -int drm_legacy_lock(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - DECLARE_WAITQUEUE(entry, current); - struct drm_lock *lock = data; - struct drm_master *master = file_priv->master; - int ret = 0; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - ++file_priv->lock_count; - - if (lock->context == DRM_KERNEL_CONTEXT) { - DRM_ERROR("Process %d using kernel context %d\n", - task_pid_nr(current), lock->context); - return -EINVAL; - } - - DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", - lock->context, task_pid_nr(current), - master->lock.hw_lock->lock, lock->flags); - - add_wait_queue(&master->lock.lock_queue, &entry); - spin_lock_bh(&master->lock.spinlock); - master->lock.user_waiters++; - spin_unlock_bh(&master->lock.spinlock); - - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - if (!master->lock.hw_lock) { - /* Device has been unregistered */ - send_sig(SIGTERM, current, 0); - ret = -EINTR; - break; - } - if (drm_lock_take(&master->lock, lock->context)) { - master->lock.file_priv = file_priv; - master->lock.lock_time = jiffies; - break; /* Got lock */ - } - - /* Contention */ - mutex_unlock(&drm_global_mutex); - schedule(); - mutex_lock(&drm_global_mutex); - if (signal_pending(current)) { - ret = -EINTR; - break; - } - } - spin_lock_bh(&master->lock.spinlock); - master->lock.user_waiters--; - spin_unlock_bh(&master->lock.spinlock); - __set_current_state(TASK_RUNNING); - remove_wait_queue(&master->lock.lock_queue, &entry); - - DRM_DEBUG("%d %s\n", lock->context, - ret ? "interrupted" : "has lock"); - if (ret) return ret; - - /* don't set the block all signals on the master process for now - * really probably not the correct answer but lets us debug xkb - * xserver for now */ - if (!file_priv->is_master) { - dev->sigdata.context = lock->context; - dev->sigdata.lock = master->lock.hw_lock; - } - - if (dev->driver->dma_quiescent && (lock->flags & _DRM_LOCK_QUIESCENT)) - { - if (dev->driver->dma_quiescent(dev)) { - DRM_DEBUG("%d waiting for DMA quiescent\n", - lock->context); - return -EBUSY; - } - } - - return 0; -} - -/** - * Unlock ioctl. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_lock structure. - * \return zero on success or negative number on failure. - * - * Transfer and free the lock. - */ -int drm_legacy_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_lock *lock = data; - struct drm_master *master = file_priv->master; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (lock->context == DRM_KERNEL_CONTEXT) { - DRM_ERROR("Process %d using kernel context %d\n", - task_pid_nr(current), lock->context); - return -EINVAL; - } - - if (drm_legacy_lock_free(&master->lock, lock->context)) { - /* FIXME: Should really bail out here. */ - } - - return 0; -} - -/** - * Take the heavyweight lock. - * - * \param lock lock pointer. - * \param context locking context. - * \return one if the lock is held, or zero otherwise. - * - * Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction. - */ -static -int drm_lock_take(struct drm_lock_data *lock_data, - unsigned int context) -{ - unsigned int old, new, prev; - volatile unsigned int *lock = &lock_data->hw_lock->lock; - - spin_lock_bh(&lock_data->spinlock); - do { - old = *lock; - if (old & _DRM_LOCK_HELD) - new = old | _DRM_LOCK_CONT; - else { - new = context | _DRM_LOCK_HELD | - ((lock_data->user_waiters + lock_data->kernel_waiters > 1) ? - _DRM_LOCK_CONT : 0); - } - prev = cmpxchg(lock, old, new); - } while (prev != old); - spin_unlock_bh(&lock_data->spinlock); - - if (_DRM_LOCKING_CONTEXT(old) == context) { - if (old & _DRM_LOCK_HELD) { - if (context != DRM_KERNEL_CONTEXT) { - DRM_ERROR("%d holds heavyweight lock\n", - context); - } - return 0; - } - } - - if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) { - /* Have lock */ - return 1; - } - return 0; -} - -/** - * This takes a lock forcibly and hands it to context. Should ONLY be used - * inside *_unlock to give lock to kernel before calling *_dma_schedule. - * - * \param dev DRM device. - * \param lock lock pointer. - * \param context locking context. - * \return always one. - * - * Resets the lock file pointer. - * Marks the lock as held by the given context, via the \p cmpxchg instruction. - */ -static int drm_lock_transfer(struct drm_lock_data *lock_data, - unsigned int context) -{ - unsigned int old, new, prev; - volatile unsigned int *lock = &lock_data->hw_lock->lock; - - lock_data->file_priv = NULL; - do { - old = *lock; - new = context | _DRM_LOCK_HELD; - prev = cmpxchg(lock, old, new); - } while (prev != old); - return 1; -} - -/** - * Free lock. - * - * \param dev DRM device. - * \param lock lock. - * \param context context. - * - * Resets the lock file pointer. - * Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task - * waiting on the lock queue. - */ -int drm_legacy_lock_free(struct drm_lock_data *lock_data, unsigned int context) -{ - unsigned int old, new, prev; - volatile unsigned int *lock = &lock_data->hw_lock->lock; - - spin_lock_bh(&lock_data->spinlock); - if (lock_data->kernel_waiters != 0) { - drm_lock_transfer(lock_data, 0); - lock_data->idle_has_lock = 1; - spin_unlock_bh(&lock_data->spinlock); - return 1; - } - spin_unlock_bh(&lock_data->spinlock); - - do { - old = *lock; - new = _DRM_LOCKING_CONTEXT(old); - prev = cmpxchg(lock, old, new); - } while (prev != old); - - if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) { - DRM_ERROR("%d freed heavyweight lock held by %d\n", - context, _DRM_LOCKING_CONTEXT(old)); - return 1; - } - wake_up_interruptible(&lock_data->lock_queue); - return 0; -} - -/** - * This function returns immediately and takes the hw lock - * with the kernel context if it is free, otherwise it gets the highest priority when and if - * it is eventually released. - * - * This guarantees that the kernel will _eventually_ have the lock _unless_ it is held - * by a blocked process. (In the latter case an explicit wait for the hardware lock would cause - * a deadlock, which is why the "idlelock" was invented). - * - * This should be sufficient to wait for GPU idle without - * having to worry about starvation. - */ - -void drm_legacy_idlelock_take(struct drm_lock_data *lock_data) -{ - int ret; - - spin_lock_bh(&lock_data->spinlock); - lock_data->kernel_waiters++; - if (!lock_data->idle_has_lock) { - - spin_unlock_bh(&lock_data->spinlock); - ret = drm_lock_take(lock_data, DRM_KERNEL_CONTEXT); - spin_lock_bh(&lock_data->spinlock); - - if (ret == 1) - lock_data->idle_has_lock = 1; - } - spin_unlock_bh(&lock_data->spinlock); -} -EXPORT_SYMBOL(drm_legacy_idlelock_take); - -void drm_legacy_idlelock_release(struct drm_lock_data *lock_data) -{ - unsigned int old, prev; - volatile unsigned int *lock = &lock_data->hw_lock->lock; - - spin_lock_bh(&lock_data->spinlock); - if (--lock_data->kernel_waiters == 0) { - if (lock_data->idle_has_lock) { - do { - old = *lock; - prev = cmpxchg(lock, old, DRM_KERNEL_CONTEXT); - } while (prev != old); - wake_up_interruptible(&lock_data->lock_queue); - lock_data->idle_has_lock = 0; - } - } - spin_unlock_bh(&lock_data->spinlock); -} -EXPORT_SYMBOL(drm_legacy_idlelock_release); - -int drm_legacy_i_have_hw_lock(struct drm_device *dev, - struct drm_file *file_priv) -{ - struct drm_master *master = file_priv->master; - return (file_priv->lock_count && master->lock.hw_lock && - _DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) && - master->lock.file_priv == file_priv); -} diff --git a/src/4.x/drivers/gpu/drm/drm_memory.c b/src/4.x/drivers/gpu/drm/drm_memory.c deleted file mode 100644 index 87a8cb733..000000000 --- a/src/4.x/drivers/gpu/drm/drm_memory.c +++ /dev/null @@ -1,151 +0,0 @@ -/** - * \file drm_memory.c - * Memory management wrappers for DRM - * - * \author Rickard E. (Rik) Faith - * \author Gareth Hughes - */ - -/* - * Created: Thu Feb 4 14:00:34 1999 by faith@valinux.com - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include "drm_legacy.h" - -#if IS_ENABLED(CONFIG_AGP) - -#ifdef HAVE_PAGE_AGP -# include -#else -# ifdef __powerpc__ -# define PAGE_AGP __pgprot(_PAGE_KERNEL | _PAGE_NO_CACHE) -# else -# define PAGE_AGP PAGE_KERNEL -# endif -#endif - -static void *agp_remap(unsigned long offset, unsigned long size, - struct drm_device * dev) -{ - unsigned long i, num_pages = - PAGE_ALIGN(size) / PAGE_SIZE; - struct drm_agp_mem *agpmem; - struct page **page_map; - struct page **phys_page_map; - void *addr; - - size = PAGE_ALIGN(size); - -#ifdef __alpha__ - offset -= dev->hose->mem_space->start; -#endif - - list_for_each_entry(agpmem, &dev->agp->memory, head) - if (agpmem->bound <= offset - && (agpmem->bound + (agpmem->pages << PAGE_SHIFT)) >= - (offset + size)) - break; - if (&agpmem->head == &dev->agp->memory) - return NULL; - - /* - * OK, we're mapping AGP space on a chipset/platform on which memory accesses by - * the CPU do not get remapped by the GART. We fix this by using the kernel's - * page-table instead (that's probably faster anyhow...). - */ - /* note: use vmalloc() because num_pages could be large... */ - page_map = vmalloc(num_pages * sizeof(struct page *)); - if (!page_map) - return NULL; - - phys_page_map = (agpmem->memory->pages + (offset - agpmem->bound) / PAGE_SIZE); - for (i = 0; i < num_pages; ++i) - page_map[i] = phys_page_map[i]; - addr = vmap(page_map, num_pages, VM_IOREMAP, PAGE_AGP); - vfree(page_map); - - return addr; -} - -/** Wrapper around agp_free_memory() */ -void drm_free_agp(struct agp_memory * handle, int pages) -{ - agp_free_memory(handle); -} - -/** Wrapper around agp_bind_memory() */ -int drm_bind_agp(struct agp_memory * handle, unsigned int start) -{ - return agp_bind_memory(handle, start); -} - -/** Wrapper around agp_unbind_memory() */ -int drm_unbind_agp(struct agp_memory * handle) -{ - return agp_unbind_memory(handle); -} - -#else /* CONFIG_AGP */ -static inline void *agp_remap(unsigned long offset, unsigned long size, - struct drm_device * dev) -{ - return NULL; -} - -#endif /* CONFIG_AGP */ - -void drm_legacy_ioremap(struct drm_local_map *map, struct drm_device *dev) -{ - if (dev->agp && dev->agp->cant_use_aperture && map->type == _DRM_AGP) - map->handle = agp_remap(map->offset, map->size, dev); - else - map->handle = ioremap(map->offset, map->size); -} -EXPORT_SYMBOL(drm_legacy_ioremap); - -void drm_legacy_ioremap_wc(struct drm_local_map *map, struct drm_device *dev) -{ - if (dev->agp && dev->agp->cant_use_aperture && map->type == _DRM_AGP) - map->handle = agp_remap(map->offset, map->size, dev); - else - map->handle = ioremap_wc(map->offset, map->size); -} -EXPORT_SYMBOL(drm_legacy_ioremap_wc); - -void drm_legacy_ioremapfree(struct drm_local_map *map, struct drm_device *dev) -{ - if (!map->handle || !map->size) - return; - - if (dev->agp && dev->agp->cant_use_aperture && map->type == _DRM_AGP) - vunmap(map->handle); - else - iounmap(map->handle); -} -EXPORT_SYMBOL(drm_legacy_ioremapfree); diff --git a/src/4.x/drivers/gpu/drm/drm_mipi_dsi.c b/src/4.x/drivers/gpu/drm/drm_mipi_dsi.c deleted file mode 100644 index 2d5ca8eec..000000000 --- a/src/4.x/drivers/gpu/drm/drm_mipi_dsi.c +++ /dev/null @@ -1,933 +0,0 @@ -/* - * MIPI DSI Bus - * - * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd. - * Andrzej Hajda - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include - -#include -#include -#include -#include -#include - -#include